mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-31 01:42:18 +00:00
Merge commit 'a2fcff1b35c8f117c6bb3a068dc28bbe52530a1a'
This commit is contained in:
commit
c3db8f3146
2
.gitignore
vendored
2
.gitignore
vendored
@ -31,3 +31,5 @@ Project.xcodeproj/*
|
||||
Watch/Watch.xcodeproj/*
|
||||
AppBundle.xcworkspace/*
|
||||
tools/buck
|
||||
*.xcodeproj
|
||||
!*_Xcode.xcodeproj
|
||||
|
@ -256,6 +256,7 @@
|
||||
<string>firefox-focus</string>
|
||||
<string>ddgQuickLink</string>
|
||||
<string>moovit</string>
|
||||
<string>alook</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
|
@ -4056,6 +4056,7 @@ Unused sets are archived when you add more.";
|
||||
"LogoutOptions.ContactSupportText" = "Tell us about any issues; logging out doesn't usually help.";
|
||||
"LogoutOptions.LogOut" = "Log Out";
|
||||
"LogoutOptions.LogOutInfo" = "Remember, logging out kills all your Secret Chats.";
|
||||
"LogoutOptions.LogOutWalletInfo" = "Logging out will cancel all your secret chats and unlink your Gram wallet from the app.";
|
||||
|
||||
"GroupPermission.PermissionGloballyDisabled" = "This permission is disabled in this group.";
|
||||
|
||||
@ -4778,8 +4779,8 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Wallet.Info.YourBalance" = "your balance";
|
||||
"Wallet.Info.Receive" = "Receive";
|
||||
"Wallet.Info.Send" = "Send";
|
||||
"Wallet.Info.RefreshErrorTitle" = "An Error Occurred";
|
||||
"Wallet.Info.RefreshErrorText" = "The wallet state can not be retrieved at this time. Please try again later.";
|
||||
"Wallet.Info.RefreshErrorTitle" = "No network";
|
||||
"Wallet.Info.RefreshErrorText" = "Couldn't refresh balance. Please make sure your internet connection is working and try again.";
|
||||
"Wallet.Info.RefreshErrorNetworkText" = "The wallet state can not be retrieved at this time. Please try again later.";
|
||||
"Wallet.Info.UnknownTransaction" = "Empty Transaction";
|
||||
"Wallet.Info.TransactionTo" = "to";
|
||||
@ -4883,16 +4884,20 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Wallet.Words.NotDoneText" = "You didn't have enough time to write those words down.";
|
||||
"Wallet.Words.NotDoneOk" = "OK, Sorry";
|
||||
"Wallet.Words.NotDoneResponse" = "Apologies Accepted";
|
||||
|
||||
"Wallet.Send.NetworkError" = "Network Error";
|
||||
"Wallet.Send.ErrorNotEnoughFunds" = "Not Enough Funds";
|
||||
"Wallet.Send.NetworkErrorTitle" = "No network";
|
||||
"Wallet.Send.NetworkErrorText" = "Couldn't send grams. Please make sure your internet connection is working and try again.";
|
||||
"Wallet.Send.ErrorNotEnoughFundsTitle" = "Insufficient Grams";
|
||||
"Wallet.Send.ErrorNotEnoughFundsText" = "Unfortunately, your transfer couldn't be completed. You don't have enough grams.";
|
||||
"Wallet.Send.ErrorInvalidAddress" = "Invalid wallet address. Please correct and try again.";
|
||||
"Wallet.Send.ErrorDecryptionFailed" = "Please make sure that your device has a passcode set in iOS Settings and try again.";
|
||||
"Wallet.Send.UninitializedTitle" = "Warning";
|
||||
"Wallet.Send.UninitializedText" = "This address belongs to an empty wallet. Are you sure you want to transfer grams to it?";
|
||||
"Wallet.Send.SendAnyway" = "Send Anyway";
|
||||
|
||||
"Wallet.Receive.CreateInvoice" = "Create Invoice";
|
||||
"Wallet.Receive.CreateInvoiceInfo" = "You can specify amount and purpose of the payment to save the sender some time.";
|
||||
"Conversation.WalletRequiredTitle" = "Gram Wallet Required";
|
||||
"Conversation.WalletRequiredText" = "This link can be used to send money on the TON Blockchain. To do this, you need to set up a Gram wallet first.";
|
||||
"Conversation.WalletRequiredNotNow" = "Not Now";
|
||||
"Conversation.WalletRequiredSetup" = "Set Up";
|
||||
|
||||
"Wallet.CreateInvoice.Title" = "Create Invoice";
|
||||
|
@ -209,9 +209,7 @@ public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: C
|
||||
return
|
||||
}
|
||||
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId).lz4v"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64()).lz4v"
|
||||
guard let fileContext = ManagedFile(queue: nil, path: path, mode: .readwrite) else {
|
||||
return
|
||||
}
|
||||
|
@ -188,6 +188,8 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
private let callListDisposable = MetaDisposable()
|
||||
|
||||
private let listNode: ListView
|
||||
private let leftOverlayNode: ASDisplayNode
|
||||
private let rightOverlayNode: ASDisplayNode
|
||||
private let emptyTextNode: ASTextNode
|
||||
|
||||
private let call: (PeerId) -> Void
|
||||
@ -210,6 +212,10 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
|
||||
self.listNode = ListView()
|
||||
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
self.leftOverlayNode = ASDisplayNode()
|
||||
self.leftOverlayNode.backgroundColor = self.presentationData.theme.list.blocksBackgroundColor
|
||||
self.rightOverlayNode = ASDisplayNode()
|
||||
self.rightOverlayNode.backgroundColor = self.presentationData.theme.list.blocksBackgroundColor
|
||||
|
||||
self.emptyTextNode = ASTextNode()
|
||||
self.emptyTextNode.alpha = 0.0
|
||||
@ -385,6 +391,8 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
|
||||
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) {
|
||||
if theme !== self.currentState.theme || strings !== self.currentState.strings || disableAnimations != self.currentState.disableAnimations {
|
||||
self.leftOverlayNode.backgroundColor = theme.list.blocksBackgroundColor
|
||||
self.rightOverlayNode.backgroundColor = theme.list.blocksBackgroundColor
|
||||
switch self.mode {
|
||||
case .tab:
|
||||
self.backgroundColor = theme.chatList.backgroundColor
|
||||
@ -536,7 +544,6 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
let size = layout.size
|
||||
let contentRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
||||
|
||||
|
||||
let textSize = self.emptyTextNode.measure(CGSize(width: size.width - 20.0, height: size.height))
|
||||
transition.updateFrame(node: self.emptyTextNode, frame: CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - textSize.width) / 2.0), y: contentRect.minY + floor((contentRect.height - textSize.height) / 2.0)), size: textSize))
|
||||
}
|
||||
@ -552,7 +559,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
|
||||
self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
|
||||
|
||||
updateLayout(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
self.updateLayout(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
|
||||
var duration: Double = 0.0
|
||||
var curve: UInt = 0
|
||||
|
@ -339,6 +339,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
private var peerPresenceManager: PeerPresenceStatusManager?
|
||||
|
||||
private var cachedChatListText: (String, String)?
|
||||
private var cachedChatListSearchResult: CachedChatListSearchResult?
|
||||
|
||||
var layoutParams: (ChatListItem, first: Bool, last: Bool, firstWithHeader: Bool, nextIsPinned: Bool, ListViewItemLayoutParams, countersSize: CGFloat)?
|
||||
@ -648,6 +649,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let currentItem = self.layoutParams?.0
|
||||
let currentContentImageMedia = self.contentImageMedia
|
||||
let currentChatListText = self.cachedChatListText
|
||||
let currentChatListSearchResult = self.cachedChatListSearchResult
|
||||
|
||||
return { item, params, first, last, firstWithHeader, nextIsPinned in
|
||||
@ -804,17 +806,56 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
var contentImageMedia: Media?
|
||||
var chatListText: (String, String)?
|
||||
var chatListSearchResult: CachedChatListSearchResult?
|
||||
|
||||
switch contentData {
|
||||
case let .chat(itemPeer, _, _, messageText):
|
||||
let messageText = messageText.replacingOccurrences(of: "\n\n", with: " ")
|
||||
case let .chat(itemPeer, _, _, text):
|
||||
var peerText: String?
|
||||
if case .groupReference = item.content {
|
||||
if let messagePeer = itemPeer.chatMainPeer {
|
||||
peerText = messagePeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
}
|
||||
} else if let message = message, let author = message.author as? TelegramUser, let peer = itemPeer.chatMainPeer, !(peer is TelegramUser) {
|
||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
} else if !displayAsMessage {
|
||||
peerText = author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
}
|
||||
}
|
||||
|
||||
func foldLineBreaks(_ text: String, allowTwoLines: Bool) -> String {
|
||||
var lines = text.split { $0.isNewline }
|
||||
var startedBothLines = false
|
||||
var result = ""
|
||||
for line in lines {
|
||||
if result.isEmpty {
|
||||
result += line
|
||||
} else {
|
||||
if allowTwoLines && !startedBothLines {
|
||||
result += "\n" + line
|
||||
startedBothLines = true
|
||||
} else {
|
||||
result += " " + line
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
let messageText: String
|
||||
if let currentChatListText = currentChatListText, currentChatListText.0 == text {
|
||||
messageText = currentChatListText.1
|
||||
chatListText = currentChatListText
|
||||
} else {
|
||||
messageText = foldLineBreaks(text, allowTwoLines: peerText == nil)
|
||||
chatListText = (text, messageText)
|
||||
}
|
||||
|
||||
if inlineAuthorPrefix == nil, let embeddedState = embeddedState as? ChatEmbeddedInterfaceState {
|
||||
hasDraft = true
|
||||
authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor)
|
||||
|
||||
attributedText = NSAttributedString(string: embeddedState.text.string.replacingOccurrences(of: "\n\n", with: " "), font: textFont, textColor: theme.messageTextColor)
|
||||
attributedText = NSAttributedString(string: foldLineBreaks(embeddedState.text.string.replacingOccurrences(of: "\n\n", with: " "), allowTwoLines: false), font: textFont, textColor: theme.messageTextColor)
|
||||
} else if let message = message {
|
||||
let composedString: NSMutableAttributedString
|
||||
if let inlineAuthorPrefix = inlineAuthorPrefix {
|
||||
@ -829,37 +870,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if let cached = currentChatListSearchResult, cached.matches(text: composedString.string, searchQuery: searchQuery) {
|
||||
chatListSearchResult = cached
|
||||
} else {
|
||||
var ranges: [Range<String.Index>] = []
|
||||
let queryWords = searchQuery.split { !$0.isLetter }.filter { !$0.isEmpty }.map { $0.lowercased() }
|
||||
|
||||
let searchRange = composedString.string.startIndex ..< composedString.string.endIndex
|
||||
composedString.string.enumerateSubstrings(in: searchRange, options: .byWords) { (substring, range, _, _) in
|
||||
guard let substring = substring?.lowercased() else {
|
||||
return
|
||||
}
|
||||
|
||||
for word in queryWords {
|
||||
var count = 0
|
||||
inner: for (c1, c2) in zip(word, substring) {
|
||||
if c1 != c2 {
|
||||
break inner
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
if count > 0 {
|
||||
let length = Double(max(word.count, substring.count))
|
||||
if length > 0 {
|
||||
let difference = abs(length - Double(count))
|
||||
let rating = difference / length
|
||||
if rating < 0.33 {
|
||||
ranges.append(range)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chatListSearchResult = CachedChatListSearchResult(text: composedString.string, searchQuery: searchQuery, resultRanges: ranges)
|
||||
let (ranges, text) = findSubstringRanges(in: composedString.string, query: searchQuery)
|
||||
chatListSearchResult = CachedChatListSearchResult(text: text, searchQuery: searchQuery, resultRanges: ranges)
|
||||
}
|
||||
} else {
|
||||
chatListSearchResult = nil
|
||||
@ -867,24 +879,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
if let chatListSearchResult = chatListSearchResult {
|
||||
for range in chatListSearchResult.resultRanges {
|
||||
composedString.addAttribute(.foregroundColor, value: theme.messageHighlightedTextColor, range: NSRange(range, in: composedString.string))
|
||||
composedString.addAttribute(.foregroundColor, value: theme.messageHighlightedTextColor, range: NSRange(range, in: chatListSearchResult.text))
|
||||
}
|
||||
}
|
||||
|
||||
attributedText = composedString
|
||||
|
||||
var peerText: String?
|
||||
if case .groupReference = item.content {
|
||||
if let messagePeer = itemPeer.chatMainPeer {
|
||||
peerText = messagePeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
}
|
||||
} else if let author = message.author as? TelegramUser, let peer = itemPeer.chatMainPeer, !(peer is TelegramUser) {
|
||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
} else if !displayAsMessage {
|
||||
peerText = author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
}
|
||||
}
|
||||
|
||||
if let peerText = peerText {
|
||||
authorAttributedString = NSAttributedString(string: peerText, font: textFont, textColor: theme.authorNameColor)
|
||||
}
|
||||
@ -1215,6 +1215,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.layoutParams = (item, first, last, firstWithHeader, nextIsPinned, params, countersSize)
|
||||
strongSelf.contentImageMedia = contentImageMedia
|
||||
strongSelf.cachedChatListText = chatListText
|
||||
strongSelf.cachedChatListSearchResult = chatListSearchResult
|
||||
|
||||
strongSelf.contextContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
|
@ -185,7 +185,7 @@ class CreatePollOptionActionItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -342,7 +342,7 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode,
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -105,7 +105,7 @@ func contactContextMenuItems(context: AccountContext, peerId: PeerId, contactsCo
|
||||
}
|
||||
|
||||
var canCall = true
|
||||
if let user = peer as? TelegramUser, let cachedUserData = transaction.getPeerCachedData(peerId: peerId) as? CachedUserData, !user.flags.contains(.isSupport) && cachedUserData.callsPrivate {
|
||||
if let user = peer as? TelegramUser, let cachedUserData = transaction.getPeerCachedData(peerId: peerId) as? CachedUserData, user.flags.contains(.isSupport) || cachedUserData.callsPrivate {
|
||||
canCall = false
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ public protocol ContainableController: class {
|
||||
|
||||
func combinedSupportedOrientations(currentOrientationToLock: UIInterfaceOrientationMask) -> ViewControllerSupportedOrientations
|
||||
var deferScreenEdgeGestures: UIRectEdge { get }
|
||||
var prefersOnScreenNavigationHidden: Bool { get }
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition)
|
||||
func updateToInterfaceOrientation(_ orientation: UIInterfaceOrientation)
|
||||
|
@ -108,6 +108,9 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
public var lockOrientation: Bool = false
|
||||
|
||||
public var deferScreenEdgeGestures: UIRectEdge = UIRectEdge()
|
||||
public var prefersOnScreenNavigationHidden: Bool {
|
||||
return (self.topViewController as? ViewController)?.prefersOnScreenNavigationHidden ?? false
|
||||
}
|
||||
|
||||
private let mode: NavigationControllerMode
|
||||
private var theme: NavigationControllerTheme
|
||||
@ -153,6 +156,14 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
return self._viewControllers.last
|
||||
}
|
||||
|
||||
var topOverlayController: ViewController? {
|
||||
if let overlayContainer = self.overlayContainers.last {
|
||||
return overlayContainer.controller
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private var _displayNode: ASDisplayNode?
|
||||
public var displayNode: ASDisplayNode {
|
||||
if let value = self._displayNode {
|
||||
|
43
submodules/Display/Display/SubstringSearch.swift
Normal file
43
submodules/Display/Display/SubstringSearch.swift
Normal file
@ -0,0 +1,43 @@
|
||||
import UIKit
|
||||
|
||||
public func findSubstringRanges(in string: String, query: String) -> ([Range<String.Index>], String) {
|
||||
var ranges: [Range<String.Index>] = []
|
||||
let queryWords = query.split { !$0.isLetter && !$0.isNumber && $0 != "#" && $0 != "@" }.filter { !$0.isEmpty && !["#", "@"].contains($0) }.map { $0.lowercased() }
|
||||
|
||||
let text = string.lowercased()
|
||||
let searchRange = text.startIndex ..< text.endIndex
|
||||
text.enumerateSubstrings(in: searchRange, options: .byWords) { (substring, range, _, _) in
|
||||
guard let substring = substring else {
|
||||
return
|
||||
}
|
||||
for var word in queryWords {
|
||||
var count = 0
|
||||
var hasLeadingSymbol = false
|
||||
if word.hasPrefix("#") || word.hasPrefix("@") {
|
||||
hasLeadingSymbol = true
|
||||
word.removeFirst()
|
||||
}
|
||||
inner: for (c1, c2) in zip(word, substring) {
|
||||
if c1 != c2 {
|
||||
break inner
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
if count > 0 {
|
||||
let length = Double(max(word.count, substring.count))
|
||||
if length > 0 {
|
||||
let difference = abs(length - Double(count))
|
||||
let rating = difference / length
|
||||
if rating < 0.33 {
|
||||
var range = range
|
||||
if hasLeadingSymbol && range.lowerBound > searchRange.lowerBound {
|
||||
range = text.index(before: range.lowerBound)..<range.upperBound
|
||||
}
|
||||
ranges.append(range)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (ranges, text)
|
||||
}
|
@ -453,41 +453,12 @@ public final class TextNodeLayout: NSObject {
|
||||
return []
|
||||
}
|
||||
|
||||
var ranges: [Range<String.Index>] = []
|
||||
let queryWords = text.split { !$0.isLetter }.filter { !$0.isEmpty }.map { $0.lowercased() }
|
||||
|
||||
let text = attributedString.string.lowercased()
|
||||
let searchRange = text.startIndex ..< text.endIndex
|
||||
text.enumerateSubstrings(in: searchRange, options: .byWords) { (substring, range, _, _) in
|
||||
guard let substring = substring else {
|
||||
return
|
||||
}
|
||||
|
||||
for word in queryWords {
|
||||
var count = 0
|
||||
inner: for (c1, c2) in zip(word, substring) {
|
||||
if c1 != c2 {
|
||||
break inner
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
if count > 0 {
|
||||
let length = Double(max(word.count, substring.count))
|
||||
if length > 0 {
|
||||
let difference = abs(length - Double(count))
|
||||
let rating = difference / length
|
||||
if rating < 0.33 {
|
||||
ranges.append(range)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (ranges, searchText) = findSubstringRanges(in: attributedString.string, query: text)
|
||||
|
||||
var result: [[CGRect]] = []
|
||||
for stringRange in ranges {
|
||||
var rects: [CGRect] = []
|
||||
let range = NSRange(stringRange, in: text)
|
||||
let range = NSRange(stringRange, in: searchText)
|
||||
for line in self.lines {
|
||||
let lineRange = NSIntersectionRange(range, line.range)
|
||||
if lineRange.length != 0 {
|
||||
|
@ -1221,7 +1221,11 @@ public class Window1 {
|
||||
}
|
||||
|
||||
private func collectPrefersOnScreenNavigationHidden() -> Bool {
|
||||
var hidden = self.presentationContext.combinedPrefersOnScreenNavigationHidden()
|
||||
var hidden = false
|
||||
if let navigationController = self._rootController as? NavigationController, let overlayController = navigationController.topOverlayController {
|
||||
hidden = hidden || overlayController.prefersOnScreenNavigationHidden
|
||||
}
|
||||
hidden = hidden || self.presentationContext.combinedPrefersOnScreenNavigationHidden()
|
||||
|
||||
for controller in self.topLevelOverlayControllers {
|
||||
if let controller = controller as? ViewController {
|
||||
|
@ -467,7 +467,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
|
||||
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
let contentSize: CGSize
|
||||
var insets: UIEdgeInsets
|
||||
let itemBackgroundColor: UIColor
|
||||
|
@ -215,7 +215,7 @@ class ItemListPeerActionItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -686,7 +686,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -542,7 +542,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -357,7 +357,7 @@ open class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UISc
|
||||
insets.top += navigationBarHeight
|
||||
|
||||
var addedInsets: UIEdgeInsets?
|
||||
if case .tablet = layout.deviceMetrics.type, layout.size.width > 460.0 {
|
||||
if layout.size.width > 480.0 {
|
||||
let inset = max(20.0, floor((layout.size.width - 674.0) / 2.0))
|
||||
insets.left += inset
|
||||
insets.right += inset
|
||||
@ -386,8 +386,8 @@ open class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UISc
|
||||
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
|
||||
self.leftOverlayNode.frame = CGRect(x: 0.0, y: insets.top + 1.0, width: insets.left, height: layout.size.height - insets.top)
|
||||
self.rightOverlayNode.frame = CGRect(x: layout.size.width - insets.right, y: insets.top + 1.0, width: insets.right, height: layout.size.height - insets.top)
|
||||
self.leftOverlayNode.frame = CGRect(x: 0.0, y: 0.0, width: insets.left, height: layout.size.height)
|
||||
self.rightOverlayNode.frame = CGRect(x: layout.size.width - insets.right, y: 0.0, width: insets.right, height: layout.size.height)
|
||||
|
||||
if let emptyStateNode = self.emptyStateNode {
|
||||
var layout = layout
|
||||
|
@ -144,3 +144,7 @@ public func itemListNeighborsGroupedInsets(_ neighbors: ItemListNeighbors) -> UI
|
||||
}
|
||||
return UIEdgeInsets(top: topInset, left: 0.0, bottom: bottomInset, right: 0.0)
|
||||
}
|
||||
|
||||
public func itemListHasRoundedBlockLayout(_ params: ListViewItemLayoutParams) -> Bool {
|
||||
return params.width > 480.0
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -220,7 +220,7 @@ public class ItemListCheckboxItemNode: ListViewItemNode {
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -378,7 +378,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -291,7 +291,7 @@ class InfoItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
if let neighbors = neighbors {
|
||||
|
@ -303,7 +303,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -266,7 +266,7 @@ public class ItemListMultilineTextItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -329,7 +329,7 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -320,7 +320,7 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -450,7 +450,7 @@
|
||||
|
||||
_checked3dTouch = true;
|
||||
|
||||
if (_intent == TGMediaAssetsControllerSetProfilePhotoIntent || _intent == TGMediaAssetsControllerSetSignupProfilePhotoIntent) {
|
||||
if (_intent == TGMediaAssetsControllerSetProfilePhotoIntent || _intent == TGMediaAssetsControllerSetSignupProfilePhotoIntent || _intent == TGMediaAssetsControllerSetCustomWallpaperIntent) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,7 @@ private final class MediaPlayerContext {
|
||||
|
||||
private var lastStatusUpdateTimestamp: Double?
|
||||
private let playerStatus: Promise<MediaPlayerStatus>
|
||||
private let playerStatusValue = Atomic<MediaPlayerStatus?>(value: nil)
|
||||
|
||||
fileprivate var actionAtEnd: MediaPlayerActionAtEnd = .stop
|
||||
|
||||
@ -290,10 +291,12 @@ private final class MediaPlayerContext {
|
||||
loadedDuration = duration
|
||||
let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: min(max(timestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play), soundEnabled: self.enableSound)
|
||||
self.playerStatus.set(.single(status))
|
||||
self.playerStatusValue.swap(status)
|
||||
} else {
|
||||
let duration = seekState?.duration ?? 0.0
|
||||
let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: min(max(timestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play), soundEnabled: self.enableSound)
|
||||
self.playerStatus.set(.single(status))
|
||||
self.playerStatusValue.swap(status)
|
||||
}
|
||||
|
||||
let frameSource = FFMpegMediaFrameSource(queue: self.queue, postbox: self.postbox, resourceReference: self.resourceReference, tempFilePath: self.tempFilePath, streamable: self.streamable.enabled, video: self.video, preferSoftwareDecoding: self.preferSoftwareDecoding, fetchAutomatically: self.fetchAutomatically, stallDuration: self.streamable.parameters.0, lowWaterDuration: self.streamable.parameters.1, highWaterDuration: self.streamable.parameters.2)
|
||||
@ -579,6 +582,12 @@ private final class MediaPlayerContext {
|
||||
self.lastStatusUpdateTimestamp = nil
|
||||
self.tick()
|
||||
self.audioRenderer?.renderer.setBaseRate(baseRate)
|
||||
|
||||
if case .seeking = self.state, let status = self.playerStatusValue.with({ $0 }) {
|
||||
let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: status.duration, dimensions: status.dimensions, timestamp: status.timestamp, baseRate: self.baseRate, seekId: self.seekId, status: status.status, soundEnabled: status.soundEnabled)
|
||||
self.playerStatus.set(.single(status))
|
||||
self.playerStatusValue.swap(status)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func setForceAudioToSpeaker(_ value: Bool) {
|
||||
@ -868,6 +877,7 @@ private final class MediaPlayerContext {
|
||||
}
|
||||
let status = MediaPlayerStatus(generationTimestamp: statusTimestamp, duration: duration, dimensions: CGSize(), timestamp: min(max(reportTimestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: playbackStatus, soundEnabled: self.enableSound)
|
||||
self.playerStatus.set(.single(status))
|
||||
self.playerStatusValue.swap(status)
|
||||
}
|
||||
|
||||
if performActionAtEndNow {
|
||||
|
@ -20,21 +20,24 @@
|
||||
#define IPHONE_4_NAMESTRING @"iPhone 4"
|
||||
#define IPHONE_4S_NAMESTRING @"iPhone 4S"
|
||||
#define IPHONE_5_NAMESTRING @"iPhone 5"
|
||||
#define IPHONE_5S_NAMESTRING @"iPhone 5S"
|
||||
#define IPHONE_5S_NAMESTRING @"iPhone 5S"
|
||||
#define IPHONE_6_NAMESTRING @"iPhone 6"
|
||||
#define IPHONE_6Plus_NAMESTRING @"iPhone 6 Plus"
|
||||
#define IPHONE_6S_NAMESTRING @"iPhone 6S"
|
||||
#define IPHONE_6SPlus_NAMESTRING @"iPhone 6S Plus"
|
||||
#define IPHONE_6Plus_NAMESTRING @"iPhone 6 Plus"
|
||||
#define IPHONE_6S_NAMESTRING @"iPhone 6S"
|
||||
#define IPHONE_6SPlus_NAMESTRING @"iPhone 6S Plus"
|
||||
#define IPHONE_7_NAMESTRING @"iPhone 7"
|
||||
#define IPHONE_7Plus_NAMESTRING @"iPhone 7 Plus"
|
||||
#define IPHONE_7Plus_NAMESTRING @"iPhone 7 Plus"
|
||||
#define IPHONE_8_NAMESTRING @"iPhone 8"
|
||||
#define IPHONE_8Plus_NAMESTRING @"iPhone 8 Plus"
|
||||
#define IPHONE_8Plus_NAMESTRING @"iPhone 8 Plus"
|
||||
#define IPHONE_X_NAMESTRING @"iPhone X"
|
||||
#define IPHONE_SE_NAMESTRING @"iPhone SE"
|
||||
#define IPHONE_SE_NAMESTRING @"iPhone SE"
|
||||
#define IPHONE_XS_NAMESTRING @"iPhone XS"
|
||||
#define IPHONE_XSMAX_NAMESTRING @"iPhone XS Max"
|
||||
#define IPHONE_XR_NAMESTRING @"iPhone XR"
|
||||
#define IPHONE_11_NAMESTRING @"iPhone 11"
|
||||
#define IPHONE_11PRO_NAMESTRING @"iPhone 11 Pro"
|
||||
#define IPHONE_11PROMAX_NAMESTRING @"iPhone 11 Pro Max"
|
||||
#define IPHONE_UNKNOWN_NAMESTRING @"Unknown iPhone"
|
||||
#define IPHONE_XS_NAMESTRING @"iPhone XS"
|
||||
#define IPHONE_XSMAX_NAMESTRING @"iPhone XS Max"
|
||||
#define IPHONE_XR_NAMESTRING @"iPhone XR"
|
||||
|
||||
#define IPOD_1G_NAMESTRING @"iPod touch 1G"
|
||||
#define IPOD_2G_NAMESTRING @"iPod touch 2G"
|
||||
@ -42,6 +45,7 @@
|
||||
#define IPOD_4G_NAMESTRING @"iPod touch 4G"
|
||||
#define IPOD_5G_NAMESTRING @"iPod touch 5G"
|
||||
#define IPOD_6G_NAMESTRING @"iPod touch 6G"
|
||||
#define IPOD_7G_NAMESTRING @"iPod touch 7G"
|
||||
#define IPOD_UNKNOWN_NAMESTRING @"Unknown iPod"
|
||||
|
||||
#define IPAD_1G_NAMESTRING @"iPad 1G"
|
||||
@ -49,12 +53,12 @@
|
||||
#define IPAD_3G_NAMESTRING @"iPad 3G"
|
||||
#define IPAD_4G_NAMESTRING @"iPad 4G"
|
||||
#define IPAD_5G_NAMESTRING @"iPad Air 2"
|
||||
#define IPAD_6G_NAMESTRING @"iPad Pro"
|
||||
#define IPAD_PRO_3G_NAMESTRING @"iPad Pro 12.9 (3rd gen)"
|
||||
#define IPAD_PRO_11_NAMESTRING @"iPad Pro 11"
|
||||
#define IPAD_PRO_6G_NAMESTRING @"iPad (6th gen)"
|
||||
#define IPAD_PRO_10_5_NAMESTRING @"iPad Pro 10.5"
|
||||
#define IPAD_PRO_12_9_NAMESTRING @"iPad Pro 12.9"
|
||||
#define IPAD_6G_NAMESTRING @"iPad Pro"
|
||||
#define IPAD_PRO_3G_NAMESTRING @"iPad Pro 12.9 inch (3rd gen)"
|
||||
#define IPAD_PRO_11_NAMESTRING @"iPad Pro 11 inch"
|
||||
#define IPAD_PRO_6G_NAMESTRING @"iPad (6th gen)"
|
||||
#define IPAD_PRO_10_5_NAMESTRING @"iPad Pro 10.5 inch"
|
||||
#define IPAD_PRO_12_9_NAMESTRING @"iPad Pro 12.9 inch"
|
||||
#define IPAD_UNKNOWN_NAMESTRING @"Unknown iPad"
|
||||
|
||||
#define APPLETV_2G_NAMESTRING @"Apple TV 2G"
|
||||
@ -67,7 +71,7 @@
|
||||
#define SIMULATOR_NAMESTRING @"iPhone Simulator"
|
||||
#define SIMULATOR_IPHONE_NAMESTRING @"iPhone Simulator"
|
||||
#define SIMULATOR_IPAD_NAMESTRING @"iPad Simulator"
|
||||
#define SIMULATOR_APPLETV_NAMESTRING @"Apple TV Simulator" // :)
|
||||
#define SIMULATOR_APPLETV_NAMESTRING @"Apple TV Simulator"
|
||||
|
||||
/*
|
||||
iPad8,5, iPad8,6, iPad8,7, iPad8,8 - iPad Pro 12.9" (3rd gen)
|
||||
@ -105,6 +109,9 @@ typedef enum {
|
||||
UIDeviceXSiPhone,
|
||||
UIDeviceXSMaxiPhone,
|
||||
UIDeviceXRiPhone,
|
||||
UIDevice11iPhone,
|
||||
UIDevice11ProiPhone,
|
||||
UIDevice11ProMaxiPhone,
|
||||
|
||||
UIDevice1GiPod,
|
||||
UIDevice2GiPod,
|
||||
@ -112,6 +119,7 @@ typedef enum {
|
||||
UIDevice4GiPod,
|
||||
UIDevice5GiPod,
|
||||
UIDevice6GiPod,
|
||||
UIDevice7GiPod,
|
||||
|
||||
UIDevice1GiPad,
|
||||
UIDevice2GiPad,
|
||||
@ -592,7 +600,9 @@ NSString *suffix = @"";
|
||||
case UIDeviceXSiPhone: return IPHONE_XS_NAMESTRING;
|
||||
case UIDeviceXSMaxiPhone: return IPHONE_XSMAX_NAMESTRING;
|
||||
case UIDeviceXRiPhone: return IPHONE_XR_NAMESTRING;
|
||||
|
||||
case UIDevice11iPhone: return IPHONE_11_NAMESTRING;
|
||||
case UIDevice11ProiPhone: return IPHONE_11PRO_NAMESTRING;
|
||||
case UIDevice11ProMaxiPhone: return IPHONE_11PROMAX_NAMESTRING;
|
||||
case UIDeviceUnknowniPhone: return IPHONE_UNKNOWN_NAMESTRING;
|
||||
|
||||
case UIDevice1GiPod: return IPOD_1G_NAMESTRING;
|
||||
@ -601,6 +611,7 @@ NSString *suffix = @"";
|
||||
case UIDevice4GiPod: return IPOD_4G_NAMESTRING;
|
||||
case UIDevice5GiPod: return IPOD_5G_NAMESTRING;
|
||||
case UIDevice6GiPod: return IPOD_6G_NAMESTRING;
|
||||
case UIDevice7GiPod: return IPOD_7G_NAMESTRING;
|
||||
case UIDeviceUnknowniPod: return IPOD_UNKNOWN_NAMESTRING;
|
||||
|
||||
case UIDevice1GiPad : return IPAD_1G_NAMESTRING;
|
||||
@ -684,6 +695,10 @@ NSString *suffix = @"";
|
||||
if ([platform isEqualToString:@"iPhone11,4"]) return UIDeviceXSMaxiPhone;
|
||||
if ([platform isEqualToString:@"iPhone11,8"]) return UIDeviceXRiPhone;
|
||||
|
||||
if ([platform isEqualToString:@"iPhone12,1"]) return UIDevice11iPhone;
|
||||
if ([platform isEqualToString:@"iPhone12,3"]) return UIDevice11ProiPhone;
|
||||
if ([platform isEqualToString:@"iPhone12,5"]) return UIDevice11ProMaxiPhone;
|
||||
|
||||
if ([platform isEqualToString:@"iPhone8,4"]) return UIDeviceSEPhone;
|
||||
|
||||
// iPod
|
||||
@ -693,6 +708,7 @@ NSString *suffix = @"";
|
||||
if ([platform hasPrefix:@"iPod4"]) return UIDevice4GiPod;
|
||||
if ([platform hasPrefix:@"iPod5"]) return UIDevice5GiPod;
|
||||
if ([platform hasPrefix:@"iPod7"]) return UIDevice6GiPod;
|
||||
if ([platform hasPrefix:@"iPod9"]) return UIDevice7GiPod;
|
||||
|
||||
// iPad
|
||||
if ([platform hasPrefix:@"iPad1"]) return UIDevice1GiPad;
|
||||
|
@ -119,6 +119,9 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
return .openUrl(url: "ddgQuickLink://\(url)")
|
||||
}))
|
||||
|
||||
options.append(OpenInOption(application: .other(title: "Alook Browser", identifier: 1261944766, scheme: "alook", store: nil), action: {
|
||||
return .openUrl(url: "alook://\(url)")
|
||||
}))
|
||||
|
||||
case let .location(location, withDirections):
|
||||
let lat = location.latitude
|
||||
|
@ -156,8 +156,7 @@ private func processedLegacySecureIdAttachmentItems(postbox: Postbox, signal: SS
|
||||
guard let image = image else {
|
||||
return []
|
||||
}
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let randomId = arc4random64()
|
||||
let tempFilePath = NSTemporaryDirectory() + "\(randomId).jpeg"
|
||||
let scaledSize = image.size.aspectFitted(CGSize(width: 2048.0, height: 2048.0))
|
||||
if let scaledImage = TGScaleImageToPixelSize(image, scaledSize), let scaledImageData = compressImageToJPEG(scaledImage, quality: 0.84) {
|
||||
|
@ -1238,10 +1238,10 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
|
||||
guard let controller = controller else {
|
||||
return
|
||||
}
|
||||
controller.view.endEditing(true)
|
||||
if let navigationController = controller.navigationController as? NavigationController {
|
||||
navigationController.filterController(controller, animated: animated)
|
||||
} else {
|
||||
controller.view.endEditing(true)
|
||||
controller.dismiss()
|
||||
}
|
||||
}
|
||||
|
@ -45,13 +45,33 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
||||
|> map { data, size, bytesPerRow in
|
||||
return { arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: true)
|
||||
|
||||
let start = CFAbsoluteTimeGetCurrent()
|
||||
|
||||
let mid = Int(size / 2)
|
||||
|
||||
let cutout: (Int, Int)?
|
||||
if case .none = icon {
|
||||
cutout = nil
|
||||
} else {
|
||||
switch size {
|
||||
case 39:
|
||||
cutout = (14, 24)
|
||||
case 43:
|
||||
cutout = (15, 27)
|
||||
case 47:
|
||||
cutout = (17, 29)
|
||||
case 51:
|
||||
cutout = (19, 31)
|
||||
case 55:
|
||||
cutout = (21, 33)
|
||||
case 59:
|
||||
cutout = (22, 36)
|
||||
case 63:
|
||||
cutout = (23, 39)
|
||||
default:
|
||||
cutout = (16, 26)
|
||||
}
|
||||
}
|
||||
func valueAt(x: Int, y: Int) -> Bool {
|
||||
if x >= 0 && x < size && y >= 0 && y < size {
|
||||
if x > (mid - 5) && x < (mid + 5) && y > (mid - 5) && y < (mid + 5) {
|
||||
if let cutout = cutout, x > cutout.0 && x < cutout.1 && y > cutout.0 && y < cutout.1 {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -67,8 +87,8 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
||||
}
|
||||
}
|
||||
|
||||
let side = Int(floor(arguments.drawingSize.width / CGFloat(size)))
|
||||
let padding: CGFloat = floor((arguments.drawingSize.width - CGFloat(side * size)) / 2.0)
|
||||
let side = floorToScreenPixels(arguments.drawingSize.width / CGFloat(size))
|
||||
let padding: CGFloat = floor((arguments.drawingSize.width - CGFloat(side * CGFloat(size))) / 2.0)
|
||||
|
||||
let squareSize = CGSize(width: side, height: side)
|
||||
let tmpContext = DrawingContext(size: CGSize(width: squareSize.width * 4.0, height: squareSize.height), scale: arguments.scale ?? 0.0, clear: true)
|
||||
@ -106,14 +126,14 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
||||
let halfLen = scaledSquareSize * 4 / 2
|
||||
let blockLen = scaledSquareSize * 4 * 2
|
||||
|
||||
func drawAt(x: Int, y: Int, black: Bool, corners: UIRectCorner) {
|
||||
if !black && corners.isEmpty {
|
||||
func drawAt(x: Int, y: Int, fill: Bool, corners: UIRectCorner) {
|
||||
if !fill && corners.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
for i in 0 ..< scaledSquareSize {
|
||||
var dst = context.bytes.advanced(by: (scaledPadding + y * scaledSquareSize + i) * context.bytesPerRow + (scaledPadding + x * scaledSquareSize) * 4)
|
||||
let srcOffset = (black ? 0 : scaledSquareSize * 4)
|
||||
let srcOffset = (fill ? 0 : scaledSquareSize * 4)
|
||||
let src = tmpContext.bytes.advanced(by: i * tmpContext.bytesPerRow + srcOffset)
|
||||
|
||||
if corners.contains(i < scaledSquareSize / 2 ? .topLeft : .bottomLeft) {
|
||||
@ -169,8 +189,7 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
||||
corners.remove(.topRight)
|
||||
corners.remove(.bottomRight)
|
||||
}
|
||||
|
||||
drawAt(x: x, y: y, black: true, corners: corners)
|
||||
drawAt(x: x, y: y, fill: true, corners: corners)
|
||||
} else {
|
||||
if valueAt(x: x - 1, y: y - 1) && valueAt(x: x - 1, y: y) && valueAt(x: x, y: y - 1) {
|
||||
corners.insert(.topLeft)
|
||||
@ -184,8 +203,7 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
||||
if valueAt(x: x + 1, y: y + 1) && valueAt(x: x + 1, y: y) && valueAt(x: x, y: y + 1) {
|
||||
corners.insert(.bottomRight)
|
||||
}
|
||||
|
||||
drawAt(x: x, y: y, black: false, corners: corners)
|
||||
drawAt(x: x, y: y, fill: false, corners: corners)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,30 +236,14 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
||||
let drawingRect = arguments.drawingRect
|
||||
let fittedSize = arguments.imageSize.aspectFilled(arguments.boundingSize).fitted(arguments.imageSize)
|
||||
let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
|
||||
|
||||
if let backgroundColor = backgroundColor {
|
||||
c.setFillColor(backgroundColor.cgColor)
|
||||
} else {
|
||||
c.setBlendMode(.clear)
|
||||
c.setFillColor(UIColor.clear.cgColor)
|
||||
}
|
||||
|
||||
let codeScale = 43.0 / CGFloat(size)
|
||||
|
||||
let codeScale: CGFloat = 43.0 / 39.0
|
||||
let clipSide = 56.0 * fittedRect.width / 267.0 * codeScale
|
||||
let clipRect = CGRect(x: fittedRect.midX - clipSide / 2.0, y: fittedRect.midY - clipSide / 2.0, width: clipSide, height: clipSide)
|
||||
switch icon {
|
||||
case .cutout, .proxy, .custom:
|
||||
break
|
||||
//c.fill(clipRect)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
c.setBlendMode(.normal)
|
||||
|
||||
|
||||
switch icon {
|
||||
case .proxy:
|
||||
let iconScale = fittedRect.width / 445.0 * codeScale
|
||||
let iconScale = fittedRect.width / 420.0 * codeScale
|
||||
let iconSize = CGSize(width: 65.0 * iconScale, height: 79.0 * iconScale)
|
||||
let point = CGPoint(x: fittedRect.midX - iconSize.width / 2.0, y: fittedRect.midY - iconSize.height / 2.0)
|
||||
c.translateBy(x: point.x, y: point.y)
|
||||
@ -275,124 +277,6 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
||||
}
|
||||
}
|
||||
|
||||
let end = CFAbsoluteTimeGetCurrent() - start
|
||||
print(end)
|
||||
|
||||
return context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func aqrCode(string: String, color: UIColor, backgroundColor: UIColor? = nil, icon: QrCodeIcon, ecl: String = "M") -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
var icon: QrCodeIcon = .none
|
||||
return Signal<CIImage, NoError> { subscriber in
|
||||
if let data = string.data(using: .isoLatin1, allowLossyConversion: false), let filter = CIFilter(name: "CIQRCodeGenerator") {
|
||||
filter.setValue(data, forKey: "inputMessage")
|
||||
filter.setValue(ecl, forKey: "inputCorrectionLevel")
|
||||
|
||||
if let output = filter.outputImage {
|
||||
subscriber.putNext(output)
|
||||
}
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
return EmptyDisposable
|
||||
}
|
||||
|> map { inputImage in
|
||||
return { arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: true)
|
||||
|
||||
let drawingRect = arguments.drawingRect
|
||||
let fittedSize = arguments.imageSize.aspectFilled(arguments.boundingSize).fitted(arguments.imageSize)
|
||||
let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
|
||||
|
||||
let scale = arguments.drawingRect.size.width / inputImage.extent.width * context.scale
|
||||
let transformed = inputImage.transformed(by: CGAffineTransform.init(scaleX: scale, y: scale))
|
||||
|
||||
let codeScale = 43.0 / inputImage.extent.width
|
||||
|
||||
let invertFilter = CIFilter(name: "CIColorInvert")
|
||||
invertFilter?.setValue(transformed, forKey: kCIInputImageKey)
|
||||
let alphaFilter = CIFilter(name: "CIMaskToAlpha")
|
||||
alphaFilter?.setValue(invertFilter?.outputImage, forKey: kCIInputImageKey)
|
||||
|
||||
var image: CGImage?
|
||||
let ciContext = CIContext(options: nil)
|
||||
if let finalImage = alphaFilter?.outputImage, let cgImage = ciContext.createCGImage(finalImage, from: finalImage.extent) {
|
||||
image = cgImage
|
||||
}
|
||||
|
||||
context.withContext { c in
|
||||
if let backgroundColor = backgroundColor {
|
||||
c.setFillColor(backgroundColor.cgColor)
|
||||
c.fill(drawingRect)
|
||||
}
|
||||
|
||||
c.setBlendMode(.normal)
|
||||
if let image = image {
|
||||
c.saveGState()
|
||||
c.translateBy(x: fittedRect.midX, y: fittedRect.midY)
|
||||
c.scaleBy(x: 1.0, y: -1.0)
|
||||
c.translateBy(x: -fittedRect.midX, y: -fittedRect.midY)
|
||||
|
||||
c.clip(to: fittedRect, mask: image)
|
||||
c.setFillColor(color.cgColor)
|
||||
c.fill(fittedRect)
|
||||
c.restoreGState()
|
||||
}
|
||||
if let backgroundColor = backgroundColor {
|
||||
c.setFillColor(backgroundColor.cgColor)
|
||||
} else {
|
||||
c.setBlendMode(.clear)
|
||||
c.setFillColor(UIColor.clear.cgColor)
|
||||
}
|
||||
|
||||
let clipSide = 56.0 * fittedRect.width / 267.0 * codeScale
|
||||
let clipRect = CGRect(x: fittedRect.midX - clipSide / 2.0, y: fittedRect.midY - clipSide / 2.0, width: clipSide, height: clipSide)
|
||||
switch icon {
|
||||
case .cutout, .proxy, .custom:
|
||||
c.fill(clipRect)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
c.setBlendMode(.normal)
|
||||
|
||||
switch icon {
|
||||
case .proxy:
|
||||
let iconScale = fittedRect.width / 445.0 * codeScale
|
||||
let iconSize = CGSize(width: 65.0 * iconScale, height: 79.0 * iconScale)
|
||||
let point = CGPoint(x: fittedRect.midX - iconSize.width / 2.0, y: fittedRect.midY - iconSize.height / 2.0)
|
||||
c.translateBy(x: point.x, y: point.y)
|
||||
c.scaleBy(x: iconScale, y: iconScale)
|
||||
c.setFillColor(color.cgColor)
|
||||
let _ = try? drawSvgPath(c, path: "M0.0,40 C0,20.3664202 20.1230605,0.0 32.5,0.0 C44.8769395,0.0 65,20.3664202 65,40 C65,47.217934 65,55.5505326 65,64.9977957 L32.5,79 L0.0,64.9977957 C0.0,55.0825772 0.0,46.7499786 0.0,40 Z")
|
||||
|
||||
if let backgroundColor = backgroundColor {
|
||||
c.setFillColor(backgroundColor.cgColor)
|
||||
} else {
|
||||
c.setBlendMode(.clear)
|
||||
c.setFillColor(UIColor.clear.cgColor)
|
||||
}
|
||||
let _ = try? drawSvgPath(c, path: "M7.03608247,43.556701 L18.9836689,32.8350515 L32.5,39.871134 L45.8888139,32.8350515 L57.9639175,43.556701 L57.9639175,60.0 L32.5,71.0 L7.03608247,60.0 Z")
|
||||
|
||||
c.setBlendMode(.normal)
|
||||
c.setFillColor(color.cgColor)
|
||||
let _ = try? drawSvgPath(c, path: "M24.1237113,50.5927835 L40.8762887,50.5927835 L40.8762887,60.9793814 L32.5,64.0928525 L24.1237113,60.9793814 Z")
|
||||
case let .custom(image):
|
||||
if let image = image {
|
||||
let fittedSize = image.size.aspectFitted(clipRect.size)
|
||||
let fittedRect = CGRect(origin: CGPoint(x: fittedRect.midX - fittedSize.width / 2.0, y: fittedRect.midY - fittedSize.height / 2.0), size: fittedSize)
|
||||
c.translateBy(x: fittedRect.midX, y: fittedRect.midY)
|
||||
c.scaleBy(x: 1.0, y: -1.0)
|
||||
c.translateBy(x: -fittedRect.midX, y: -fittedRect.midY)
|
||||
c.draw(image.cgImage!, in: fittedRect)
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +261,7 @@ class AutodownloadDataUsagePickerItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -231,7 +231,7 @@ class AutodownloadSizeLimitItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -184,7 +184,7 @@ class CalculatingCacheSizeItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -188,7 +188,7 @@ class ProxySettingsActionItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -370,7 +370,7 @@ class ProxySettingsServerItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -104,7 +104,7 @@ private enum LogoutOptionsEntry: ItemListNodeEntry, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private func logoutOptionsEntries(presentationData: PresentationData, canAddAccounts: Bool, hasPasscode: Bool) -> [LogoutOptionsEntry] {
|
||||
private func logoutOptionsEntries(presentationData: PresentationData, canAddAccounts: Bool, hasPasscode: Bool, hasWallets: Bool) -> [LogoutOptionsEntry] {
|
||||
var entries: [LogoutOptionsEntry] = []
|
||||
entries.append(.alternativeHeader(presentationData.theme, presentationData.strings.LogoutOptions_AlternativeOptionsSection))
|
||||
if canAddAccounts {
|
||||
@ -117,7 +117,11 @@ private func logoutOptionsEntries(presentationData: PresentationData, canAddAcco
|
||||
entries.append(.changePhoneNumber(presentationData.theme, presentationData.strings.LogoutOptions_ChangePhoneNumberTitle, presentationData.strings.LogoutOptions_ChangePhoneNumberText))
|
||||
entries.append(.contactSupport(presentationData.theme, presentationData.strings.LogoutOptions_ContactSupportTitle, presentationData.strings.LogoutOptions_ContactSupportText))
|
||||
entries.append(.logout(presentationData.theme, presentationData.strings.LogoutOptions_LogOut))
|
||||
entries.append(.logoutInfo(presentationData.theme, presentationData.strings.LogoutOptions_LogOutInfo))
|
||||
if hasWallets {
|
||||
entries.append(.logoutInfo(presentationData.theme, presentationData.strings.LogoutOptions_LogOutWalletInfo))
|
||||
} else {
|
||||
entries.append(.logoutInfo(presentationData.theme, presentationData.strings.LogoutOptions_LogOutInfo))
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
@ -211,8 +215,17 @@ func logoutOptionsController(context: AccountContext, navigationController: Navi
|
||||
presentControllerImpl?(alertController, nil)
|
||||
})
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.accessChallengeData())
|
||||
|> map { presentationData, accessChallengeData -> (ItemListControllerState, (ItemListNodeState<LogoutOptionsEntry>, LogoutOptionsEntry.ItemGenerationArguments)) in
|
||||
let hasWallets = availableWallets(postbox: context.account.postbox)
|
||||
|> map { wallets in
|
||||
return !wallets.wallets.isEmpty
|
||||
}
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(),
|
||||
context.sharedContext.presentationData,
|
||||
context.sharedContext.accountManager.accessChallengeData(),
|
||||
hasWallets
|
||||
)
|
||||
|> map { presentationData, accessChallengeData, hasWallets -> (ItemListControllerState, (ItemListNodeState<LogoutOptionsEntry>, LogoutOptionsEntry.ItemGenerationArguments)) in
|
||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||
dismissImpl?()
|
||||
})
|
||||
@ -226,7 +239,7 @@ func logoutOptionsController(context: AccountContext, navigationController: Navi
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.LogoutOptions_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(entries: logoutOptionsEntries(presentationData: presentationData, canAddAccounts: canAddAccounts, hasPasscode: hasPasscode), style: .blocks)
|
||||
let listState = ItemListNodeState(entries: logoutOptionsEntries(presentationData: presentationData, canAddAccounts: canAddAccounts, hasPasscode: hasPasscode, hasWallets: hasWallets), style: .blocks)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -345,7 +345,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -37,7 +37,7 @@ final class ThemeAccentColorController: ViewController {
|
||||
color = defaultDayAccentColor
|
||||
}
|
||||
self.initialColor = color
|
||||
self.initialTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true)
|
||||
self.initialTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true) ?? defaultPresentationTheme
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
@ -70,7 +70,7 @@ final class ThemeAccentColorController: ViewController {
|
||||
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
|
||||
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: strongSelf.currentTheme, accentColor: UIColor(rgb: strongSelf.controllerNode.color), serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor)
|
||||
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: strongSelf.currentTheme, accentColor: UIColor(rgb: strongSelf.controllerNode.color), serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) ?? defaultPresentationTheme
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
if let wallpaper = current.themeSpecificChatWallpapers[current.theme.index], wallpaper.hasWallpaper {
|
||||
} else {
|
||||
|
@ -140,7 +140,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
self.colorDisposable = (self.colorValue.get()
|
||||
|> deliverOn(Queue.concurrentDefaultQueue())
|
||||
|> map { color -> PresentationTheme in
|
||||
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true)
|
||||
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true) ?? defaultPresentationTheme
|
||||
|
||||
let wallpaper = context.sharedContext.currentPresentationData.with { $0 }.chatWallpaper
|
||||
let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: theme, wallpaper: wallpaper, gradientBubbles: context.sharedContext.immediateExperimentalUISettings.gradientBubbles)
|
||||
@ -191,10 +191,12 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
self.scrollNode.view.showsHorizontalScrollIndicator = false
|
||||
self.scrollNode.view.isPagingEnabled = true
|
||||
self.scrollNode.view.delegate = self
|
||||
self.pageControlNode.setPage(0.0)
|
||||
self.colorPanelNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
|
@ -497,7 +497,9 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon
|
||||
}
|
||||
}))
|
||||
}, updateTheme: { theme in
|
||||
let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, serviceBackgroundColor: .black, baseColor: nil)
|
||||
guard let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, serviceBackgroundColor: .black, baseColor: nil) else {
|
||||
return
|
||||
}
|
||||
|
||||
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
|
||||
if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
|
||||
|
@ -78,6 +78,8 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
private var customColorItem: ItemListActionItem
|
||||
|
||||
let gridNode: GridNode
|
||||
private let leftOverlayNode: ASDisplayNode
|
||||
private let rightOverlayNode: ASDisplayNode
|
||||
|
||||
private var queuedTransitions: [ThemeColorsGridEntryTransition] = []
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
@ -92,6 +94,10 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
|
||||
self.gridNode = GridNode()
|
||||
self.gridNode.showVerticalScrollIndicator = true
|
||||
self.leftOverlayNode = ASDisplayNode()
|
||||
self.leftOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
self.rightOverlayNode = ASDisplayNode()
|
||||
self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
@ -210,7 +216,9 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
|
||||
|
||||
self.leftOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
|
||||
self.customColorItem = ItemListActionItem(theme: presentationData.theme, title: presentationData.strings.WallpaperColors_SetCustomColor, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: { [weak self] in
|
||||
self?.presentColorPicker()
|
||||
})
|
||||
@ -259,8 +267,29 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
let imageSize = referenceImageSize.aspectFilled(CGSize(width: floor((layout.size.width - CGFloat(imageCount + 1) * minSpacing) / CGFloat(imageCount)), height: referenceImageSize.height))
|
||||
let spacing = floor((layout.size.width - CGFloat(imageCount) * imageSize.width) / CGFloat(imageCount + 1))
|
||||
|
||||
var listInsets = insets
|
||||
if layout.size.width > 480.0 {
|
||||
let inset = max(20.0, floor((layout.size.width - 674.0) / 2.0))
|
||||
listInsets.left += inset
|
||||
listInsets.right += inset
|
||||
|
||||
if self.leftOverlayNode.supernode == nil {
|
||||
self.gridNode.addSubnode(self.leftOverlayNode)
|
||||
}
|
||||
if self.rightOverlayNode.supernode == nil {
|
||||
self.gridNode.addSubnode(self.rightOverlayNode)
|
||||
}
|
||||
} else {
|
||||
if self.leftOverlayNode.supernode != nil {
|
||||
self.leftOverlayNode.removeFromSupernode()
|
||||
}
|
||||
if self.rightOverlayNode.supernode != nil {
|
||||
self.rightOverlayNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
let makeColorLayout = self.customColorItemNode.asyncLayout()
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: insets.left, rightInset: insets.right)
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: listInsets.left, rightInset: listInsets.right)
|
||||
let (colorLayout, colorApply) = makeColorLayout(self.customColorItem, params, ItemListNeighbors(top: .none, bottom: .none))
|
||||
colorApply()
|
||||
|
||||
@ -275,6 +304,9 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset + buttonInset - UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.customColorItemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset + buttonTopInset), size: colorLayout.contentSize))
|
||||
|
||||
self.leftOverlayNode.frame = CGRect(x: 0.0, y: -buttonOffset, width: listInsets.left, height: buttonTopInset + colorLayout.contentSize.height)
|
||||
self.rightOverlayNode.frame = CGRect(x: layout.size.width - listInsets.right, y: -buttonOffset, width: listInsets.right, height: buttonTopInset + colorLayout.contentSize.height)
|
||||
|
||||
insets.top += spacing + buttonInset
|
||||
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: insets, scrollIndicatorInsets: scrollIndicatorInsets, preloadSize: 300.0, type: .fixed(itemSize: imageSize, fillWidth: nil, lineSpacing: spacing, itemSpacing: nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
|
@ -218,6 +218,8 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
private var selectionPanelBackgroundNode: ASDisplayNode?
|
||||
|
||||
let gridNode: GridNode
|
||||
private let leftOverlayNode: ASDisplayNode
|
||||
private let rightOverlayNode: ASDisplayNode
|
||||
var navigationBar: NavigationBar?
|
||||
|
||||
private var queuedTransitions: [ThemeGridEntryTransition] = []
|
||||
@ -244,6 +246,10 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
|
||||
self.gridNode = GridNode()
|
||||
self.gridNode.showVerticalScrollIndicator = true
|
||||
self.leftOverlayNode = ASDisplayNode()
|
||||
self.leftOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
self.rightOverlayNode = ASDisplayNode()
|
||||
self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
@ -516,6 +522,8 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
|
||||
self.searchDisplayController?.updatePresentationData(self.presentationData)
|
||||
|
||||
self.leftOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
self.backgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
self.bottomBackgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
@ -609,7 +617,28 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
let makeGalleryLayout = self.galleryItemNode.asyncLayout()
|
||||
let makeDescriptionLayout = self.descriptionItemNode.asyncLayout()
|
||||
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: insets.left, rightInset: insets.right)
|
||||
var listInsets = insets
|
||||
if layout.size.width > 480.0 {
|
||||
let inset = max(20.0, floor((layout.size.width - 674.0) / 2.0))
|
||||
listInsets.left += inset
|
||||
listInsets.right += inset
|
||||
|
||||
if self.leftOverlayNode.supernode == nil {
|
||||
self.gridNode.addSubnode(self.leftOverlayNode)
|
||||
}
|
||||
if self.rightOverlayNode.supernode == nil {
|
||||
self.gridNode.addSubnode(self.rightOverlayNode)
|
||||
}
|
||||
} else {
|
||||
if self.leftOverlayNode.supernode != nil {
|
||||
self.leftOverlayNode.removeFromSupernode()
|
||||
}
|
||||
if self.rightOverlayNode.supernode != nil {
|
||||
self.rightOverlayNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: listInsets.left, rightInset: listInsets.right)
|
||||
let (colorLayout, colorApply) = makeColorLayout(self.colorItem, params, ItemListNeighbors(top: .none, bottom: .sameSection(alwaysPlain: false)))
|
||||
let (galleryLayout, galleryApply) = makeGalleryLayout(self.galleryItem, params, ItemListNeighbors(top: .sameSection(alwaysPlain: false), bottom: .sameSection(alwaysPlain: true)))
|
||||
let (descriptionLayout, descriptionApply) = makeDescriptionLayout(self.descriptionItem, params, ItemListNeighbors(top: .none, bottom: .none))
|
||||
@ -632,6 +661,9 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
transition.updateFrame(node: self.galleryItemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset + buttonTopInset + colorLayout.contentSize.height), size: galleryLayout.contentSize))
|
||||
transition.updateFrame(node: self.descriptionItemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset + buttonTopInset + colorLayout.contentSize.height + galleryLayout.contentSize.height), size: descriptionLayout.contentSize))
|
||||
|
||||
self.leftOverlayNode.frame = CGRect(x: 0.0, y: -buttonOffset, width: listInsets.left, height: buttonTopInset + colorLayout.contentSize.height + galleryLayout.contentSize.height)
|
||||
self.rightOverlayNode.frame = CGRect(x: layout.size.width - listInsets.right, y: -buttonOffset, width: listInsets.right, height: buttonTopInset + colorLayout.contentSize.height + galleryLayout.contentSize.height)
|
||||
|
||||
insets.top += spacing + buttonInset
|
||||
|
||||
if self.currentState.editing {
|
||||
|
@ -270,7 +270,7 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -241,7 +241,7 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -176,7 +176,7 @@ class ThemeSettingsBrightnessItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -231,7 +231,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -58,7 +58,7 @@ func themeDisplayName(strings: PresentationStrings, reference: PresentationTheme
|
||||
|
||||
private final class ThemeSettingsControllerArguments {
|
||||
let context: AccountContext
|
||||
let updateTheme: (PresentationThemeReference) -> Void
|
||||
let selectTheme: (PresentationThemeReference) -> Void
|
||||
let selectFontSize: (PresentationFontSize) -> Void
|
||||
let openWallpaperSettings: () -> Void
|
||||
let selectAccentColor: (PresentationThemeAccentColor) -> Void
|
||||
@ -70,9 +70,9 @@ private final class ThemeSettingsControllerArguments {
|
||||
let editTheme: (PresentationCloudTheme) -> Void
|
||||
let contextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void
|
||||
|
||||
init(context: AccountContext, updateTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, contextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void) {
|
||||
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, contextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void) {
|
||||
self.context = context
|
||||
self.updateTheme = updateTheme
|
||||
self.selectTheme = selectTheme
|
||||
self.selectFontSize = selectFontSize
|
||||
self.openWallpaperSettings = openWallpaperSettings
|
||||
self.selectAccentColor = selectAccentColor
|
||||
@ -319,7 +319,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
arguments.editTheme(theme)
|
||||
}
|
||||
} else {
|
||||
arguments.updateTheme(theme)
|
||||
arguments.selectTheme(theme)
|
||||
}
|
||||
}, contextAction: { theme, node, gesture in
|
||||
arguments.contextAction(theme.index == currentTheme.index, theme, node, gesture)
|
||||
@ -393,7 +393,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
var presentInGlobalOverlayImpl: ((ViewController, Any?) -> Void)?
|
||||
var getNavigationControllerImpl: (() -> NavigationController?)?
|
||||
|
||||
var updateThemeImpl: ((PresentationThemeReference) -> Void)?
|
||||
var selectThemeImpl: ((PresentationThemeReference) -> Void)?
|
||||
var moreImpl: (() -> Void)?
|
||||
|
||||
let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start()
|
||||
@ -414,8 +414,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
let updatedCloudThemes = telegramThemes(postbox: context.account.postbox, network: context.account.network, accountManager: context.sharedContext.accountManager)
|
||||
cloudThemes.set(updatedCloudThemes)
|
||||
|
||||
let arguments = ThemeSettingsControllerArguments(context: context, updateTheme: { theme in
|
||||
updateThemeImpl?(theme)
|
||||
let arguments = ThemeSettingsControllerArguments(context: context, selectTheme: { theme in
|
||||
selectThemeImpl?(theme)
|
||||
}, selectFontSize: { size in
|
||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
@ -424,12 +424,13 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
pushControllerImpl?(ThemeGridController(context: context))
|
||||
}, selectAccentColor: { color in
|
||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
guard let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: current.theme, accentColor: color.color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) else {
|
||||
return current
|
||||
}
|
||||
|
||||
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
||||
themeSpecificAccentColors[current.theme.index] = color
|
||||
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
|
||||
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: current.theme, accentColor: color.color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor)
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
if let wallpaper = current.themeSpecificChatWallpapers[current.theme.index], wallpaper.hasWallpaper {
|
||||
} else {
|
||||
@ -468,6 +469,9 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
return makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: nil, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: .blue)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { theme in
|
||||
guard let theme = theme else {
|
||||
return
|
||||
}
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let strings = presentationData.strings
|
||||
let themeController = ThemePreviewController(context: context, previewTheme: theme, source: .settings(reference))
|
||||
@ -476,7 +480,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
if !isCurrent {
|
||||
items.append(.action(ContextMenuActionItem(text: strings.Theme_Context_Apply, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) }, action: { c, f in
|
||||
c.dismiss(completion: {
|
||||
updateThemeImpl?(reference)
|
||||
selectThemeImpl?(reference)
|
||||
})
|
||||
})))
|
||||
}
|
||||
@ -517,7 +521,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
} else {
|
||||
newTheme = .builtin(.nightAccent)
|
||||
}
|
||||
updateThemeImpl?(newTheme)
|
||||
selectThemeImpl?(newTheme)
|
||||
}
|
||||
|
||||
let _ = deleteThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: theme.theme).start()
|
||||
@ -548,7 +552,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
let disableAnimations = presentationData.disableAnimations
|
||||
|
||||
let accentColor = settings.themeSpecificAccentColors[settings.theme.index]?.color
|
||||
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: settings.theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: settings.themeSpecificAccentColors[settings.theme.index]?.baseColor ?? .blue, preview: true)
|
||||
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: settings.theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: settings.themeSpecificAccentColors[settings.theme.index]?.baseColor ?? .blue, preview: true) ?? defaultPresentationTheme
|
||||
|
||||
let wallpaper: TelegramWallpaper
|
||||
if let themeSpecificWallpaper = settings.themeSpecificChatWallpapers[settings.theme.index] {
|
||||
@ -590,8 +594,10 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
getNavigationControllerImpl = { [weak controller] in
|
||||
return controller?.navigationController as? NavigationController
|
||||
}
|
||||
updateThemeImpl = { theme in
|
||||
let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, serviceBackgroundColor: .black, baseColor: nil)
|
||||
selectThemeImpl = { theme in
|
||||
guard let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, serviceBackgroundColor: .black, baseColor: nil) else {
|
||||
return
|
||||
}
|
||||
|
||||
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
|
||||
if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
|
||||
@ -628,7 +634,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[updatedTheme.index] {
|
||||
chatWallpaper = themeSpecificWallpaper
|
||||
} else {
|
||||
let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: updatedTheme, accentColor: current.themeSpecificAccentColors[updatedTheme.index]?.color, serviceBackgroundColor: .black, baseColor: nil)
|
||||
let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: updatedTheme, accentColor: current.themeSpecificAccentColors[updatedTheme.index]?.color, serviceBackgroundColor: .black, baseColor: nil) ?? defaultPresentationTheme
|
||||
chatWallpaper = resolvedWallpaper ?? presentationTheme.chat.defaultWallpaper
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ class ThemeSettingsFontSizeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -346,7 +346,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = params.width > 480
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
@ -384,9 +384,16 @@ public class WallpaperGalleryController: ViewController {
|
||||
let wallpaper = wallpaper.withUpdatedSettings(updatedSettings)
|
||||
|
||||
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[current.theme.index] = wallpaper
|
||||
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
var chatWallpaper = current.chatWallpaper
|
||||
if automaticThemeShouldSwitchNow(settings: current.automaticThemeSwitchSetting, currentTheme: current.theme) {
|
||||
themeSpecificChatWallpapers[current.automaticThemeSwitchSetting.theme.index] = wallpaper
|
||||
} else {
|
||||
themeSpecificChatWallpapers[current.theme.index] = wallpaper
|
||||
chatWallpaper = wallpaper
|
||||
}
|
||||
|
||||
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
}) |> deliverOnMainQueue).start(completed: {
|
||||
self?.dismiss(forceAway: true)
|
||||
})
|
||||
|
@ -21,6 +21,7 @@ static_library(
|
||||
"//submodules/RadialStatusNode:RadialStatusNode",
|
||||
"//submodules/ActivityIndicator:ActivityIndicator",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -13,6 +13,7 @@ import LocalizedPeerData
|
||||
import UrlEscaping
|
||||
import StickerResources
|
||||
import SaveToCameraRoll
|
||||
import TelegramStringFormatting
|
||||
|
||||
public struct ShareControllerAction {
|
||||
let title: String
|
||||
@ -90,10 +91,12 @@ private enum ExternalShareItemsState {
|
||||
private struct CollectableExternalShareItem {
|
||||
let url: String?
|
||||
let text: String
|
||||
let author: String?
|
||||
let timestamp: Int32?
|
||||
let mediaReference: AnyMediaReference?
|
||||
}
|
||||
|
||||
private func collectExternalShareItems(strings: PresentationStrings, postbox: Postbox, collectableItems: [CollectableExternalShareItem], takeOne: Bool = true) -> Signal<ExternalShareItemsState, NoError> {
|
||||
private func collectExternalShareItems(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, postbox: Postbox, collectableItems: [CollectableExternalShareItem], takeOne: Bool = true) -> Signal<ExternalShareItemsState, NoError> {
|
||||
var signals: [Signal<ExternalShareItemStatus, NoError>] = []
|
||||
for item in collectableItems {
|
||||
if let mediaReference = item.mediaReference, let file = mediaReference.media as? TelegramMediaFile {
|
||||
@ -163,7 +166,18 @@ private func collectExternalShareItems(strings: PresentationStrings, postbox: Po
|
||||
}
|
||||
signals.append(.single(.done(.text(text))))
|
||||
} else if let mediaReference = item.mediaReference, let contact = mediaReference.media as? TelegramMediaContact {
|
||||
let contactData: DeviceContactExtendedData
|
||||
if let vCard = contact.vCardData, let vCardData = vCard.data(using: .utf8), let parsed = DeviceContactExtendedData(vcard: vCardData) {
|
||||
contactData = parsed
|
||||
} else {
|
||||
contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName, lastName: contact.lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: contact.phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [])
|
||||
}
|
||||
|
||||
if let vCard = contactData.serializedVCard() {
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64()).lz4v"
|
||||
let fullName = [contact.firstName, contact.lastName].filter { $0.isEmpty }.joined(separator: " ")
|
||||
signals.append(.single(.done(.file(URL(fileURLWithPath: path), "\(fullName).vcf", "text/x-vcard"))))
|
||||
}
|
||||
}
|
||||
if let url = item.url, let parsedUrl = URL(string: url) {
|
||||
if signals.isEmpty || !takeOne {
|
||||
@ -172,7 +186,18 @@ private func collectExternalShareItems(strings: PresentationStrings, postbox: Po
|
||||
}
|
||||
if !item.text.isEmpty {
|
||||
if signals.isEmpty || !takeOne {
|
||||
signals.append(.single(.done(.text(item.text))))
|
||||
var text: String = item.text
|
||||
var metadata: [String] = []
|
||||
if let author = item.author {
|
||||
metadata.append(author)
|
||||
}
|
||||
if let timestamp = item.timestamp {
|
||||
metadata.append("[\(stringForFullDate(timestamp: timestamp, strings: strings, dateTimeFormat: dateTimeFormat))]")
|
||||
}
|
||||
if !metadata.isEmpty {
|
||||
text = metadata.joined(separator: ", ") + "\n" + text + "\n"
|
||||
}
|
||||
signals.append(.single(.done(.text(text))))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -519,19 +544,19 @@ public final class ShareController: ViewController {
|
||||
var collectableItems: [CollectableExternalShareItem] = []
|
||||
switch strongSelf.subject {
|
||||
case let .url(text):
|
||||
collectableItems.append(CollectableExternalShareItem(url: explicitUrl(text), text: "", mediaReference: nil))
|
||||
collectableItems.append(CollectableExternalShareItem(url: explicitUrl(text), text: "", author: nil, timestamp: nil, mediaReference: nil))
|
||||
case let .text(string):
|
||||
collectableItems.append(CollectableExternalShareItem(url: "", text: string, mediaReference: nil))
|
||||
collectableItems.append(CollectableExternalShareItem(url: "", text: string, author: nil, timestamp: nil, mediaReference: nil))
|
||||
case let .quote(text, url):
|
||||
collectableItems.append(CollectableExternalShareItem(url: "", text: "\"\(text)\"\n\n\(url)", mediaReference: nil))
|
||||
collectableItems.append(CollectableExternalShareItem(url: "", text: "\"\(text)\"\n\n\(url)", author: nil, timestamp: nil, mediaReference: nil))
|
||||
case let .image(representations):
|
||||
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil)
|
||||
collectableItems.append(CollectableExternalShareItem(url: "", text: "", mediaReference: .standalone(media: media)))
|
||||
collectableItems.append(CollectableExternalShareItem(url: "", text: "", author: nil, timestamp: nil, mediaReference: .standalone(media: media)))
|
||||
case let .media(mediaReference):
|
||||
collectableItems.append(CollectableExternalShareItem(url: "", text: "", mediaReference: mediaReference))
|
||||
collectableItems.append(CollectableExternalShareItem(url: "", text: "", author: nil, timestamp: nil, mediaReference: mediaReference))
|
||||
case let .mapMedia(media):
|
||||
let latLong = "\(media.latitude),\(media.longitude)"
|
||||
collectableItems.append(CollectableExternalShareItem(url: "https://maps.apple.com/maps?ll=\(latLong)&q=\(latLong)&t=m", text: "", mediaReference: nil))
|
||||
collectableItems.append(CollectableExternalShareItem(url: "https://maps.apple.com/maps?ll=\(latLong)&q=\(latLong)&t=m", text: "", author: nil, timestamp: nil, mediaReference: nil))
|
||||
case let .messages(messages):
|
||||
for message in messages {
|
||||
var url: String?
|
||||
@ -561,12 +586,18 @@ public final class ShareController: ViewController {
|
||||
url = "https://t.me/\(addressName)/\(message.id.id)"
|
||||
}
|
||||
}
|
||||
collectableItems.append(CollectableExternalShareItem(url: url, text: message.text, mediaReference: selectedMedia.flatMap({ AnyMediaReference.message(message: MessageReference(message), media: $0) })))
|
||||
var peer: Peer?
|
||||
if let authorPeerId = message.author?.id {
|
||||
peer = message.peers[authorPeerId]
|
||||
} else if let mainPeer = messageMainPeer(message) {
|
||||
peer = mainPeer
|
||||
}
|
||||
collectableItems.append(CollectableExternalShareItem(url: url, text: message.text, author: peer?.displayTitle, timestamp: message.timestamp, mediaReference: selectedMedia.flatMap({ AnyMediaReference.message(message: MessageReference(message), media: $0) })))
|
||||
}
|
||||
case .fromExternal:
|
||||
break
|
||||
}
|
||||
return (collectExternalShareItems(strings: strongSelf.presentationData.strings, postbox: strongSelf.currentAccount.postbox, collectableItems: collectableItems, takeOne: !strongSelf.immediateExternalShare)
|
||||
return (collectExternalShareItems(strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat, postbox: strongSelf.currentAccount.postbox, collectableItems: collectableItems, takeOne: !strongSelf.immediateExternalShare)
|
||||
|> deliverOnMainQueue)
|
||||
|> map { state in
|
||||
switch state {
|
||||
|
@ -20,9 +20,7 @@ public func requestStartBot(account: Account, botPeerId: PeerId, payload: String
|
||||
return account.postbox.loadedPeerWithId(botPeerId)
|
||||
|> mapToSignal { botPeer -> Signal<Void, NoError> in
|
||||
if let inputUser = apiInputUser(botPeer) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let r = account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: randomId, startParam: payload))
|
||||
let r = account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: arc4random64(), startParam: payload))
|
||||
|> mapToSignal { result -> Signal<Void, MTRpcError> in
|
||||
account.stateManager.addUpdates(result)
|
||||
return .complete()
|
||||
@ -59,9 +57,7 @@ public func requestStartBotInGroup(account: Account, botPeerId: PeerId, groupPee
|
||||
|> mapError { _ -> RequestStartBotInGroupError in return .generic }
|
||||
|> mapToSignal { botPeer, groupPeer -> Signal<StartBotInGroupResult, RequestStartBotInGroupError> in
|
||||
if let botPeer = botPeer, let inputUser = apiInputUser(botPeer), let groupPeer = groupPeer, let inputGroup = apiInputPeer(groupPeer) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let request = account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: inputGroup, randomId: randomId, startParam: payload ?? ""))
|
||||
let request = account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: inputGroup, randomId: arc4random64(), startParam: payload ?? ""))
|
||||
|> mapError { _ -> RequestStartBotInGroupError in
|
||||
return .generic
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ private func mergedState(transaction: Transaction, state: SearchMessagesPeerStat
|
||||
return lhs.index > rhs.index
|
||||
})
|
||||
|
||||
let completed = renderedMessages.isEmpty
|
||||
let completed = renderedMessages.isEmpty || renderedMessages.count == totalCount
|
||||
if let previous = state {
|
||||
var currentIds = Set<MessageId>()
|
||||
var mergedMessages: [Message] = []
|
||||
|
@ -215,8 +215,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
)
|
||||
|
||||
let message = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
|
||||
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f))),
|
||||
infoPrimaryTextColor: .white,
|
||||
infoLinkTextColor: accentColor,
|
||||
|
@ -193,8 +193,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
let buttonStrokeColor = accentColor.withMultiplied(hue: 1.014, saturation: 0.56, brightness: 0.64).withAlphaComponent(0.15)
|
||||
|
||||
let message = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
|
||||
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)),
|
||||
infoPrimaryTextColor: UIColor(rgb: 0xffffff),
|
||||
infoLinkTextColor: accentColor,
|
||||
|
@ -216,8 +216,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
|
||||
)
|
||||
|
||||
let messageDay = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: outgoingSelectionBaseColor.withAlphaComponent(0.2), textSelectionKnobColor: outgoingSelectionBaseColor),
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: outgoingSelectionBaseColor.withAlphaComponent(0.2), textSelectionKnobColor: outgoingSelectionBaseColor),
|
||||
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xE5E5EA))),
|
||||
infoPrimaryTextColor: UIColor(rgb: 0x000000),
|
||||
infoLinkTextColor: UIColor(rgb: 0x004bad),
|
||||
|
@ -18,7 +18,7 @@ public func makeDefaultPresentationTheme(reference: PresentationBuiltinThemeRefe
|
||||
return theme
|
||||
}
|
||||
|
||||
public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, accentColor: UIColor?, serviceBackgroundColor: UIColor, baseColor: PresentationThemeBaseColor?, preview: Bool = false) -> PresentationTheme {
|
||||
public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, accentColor: UIColor?, serviceBackgroundColor: UIColor, baseColor: PresentationThemeBaseColor?, preview: Bool = false) -> PresentationTheme? {
|
||||
let theme: PresentationTheme
|
||||
switch themeReference {
|
||||
case let .builtin(reference):
|
||||
@ -27,13 +27,13 @@ public func makePresentationTheme(mediaBox: MediaBox, themeReference: Presentati
|
||||
if let path = mediaBox.completedResourcePath(info.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) {
|
||||
theme = loadedTheme
|
||||
} else {
|
||||
theme = makeDefaultPresentationTheme(reference: .dayClassic, accentColor: nil, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, preview: preview)
|
||||
return nil
|
||||
}
|
||||
case let .cloud(info):
|
||||
if let file = info.theme.file, let path = mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) {
|
||||
theme = loadedTheme
|
||||
} else {
|
||||
theme = makeDefaultPresentationTheme(reference: .dayClassic, accentColor: nil, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, preview: preview)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return theme
|
||||
|
@ -261,7 +261,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager) -
|
||||
}
|
||||
|
||||
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color
|
||||
themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue)
|
||||
themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) ?? defaultPresentationTheme
|
||||
|
||||
if effectiveTheme != themeSettings.theme {
|
||||
switch effectiveChatWallpaper {
|
||||
@ -514,7 +514,7 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
|
||||
}
|
||||
|
||||
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color
|
||||
let themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue)
|
||||
let themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) ?? defaultPresentationTheme
|
||||
|
||||
if effectiveTheme != themeSettings.theme && themeSettings.themeSpecificChatWallpapers[effectiveTheme.index] == nil {
|
||||
switch effectiveChatWallpaper {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -565,7 +565,7 @@ public struct PresentationResourcesChat {
|
||||
|
||||
public static func chatHistoryNavigationButtonBadgeImage(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.chatHistoryNavigationButtonBadgeImage.rawValue, { theme in
|
||||
return generateFilledCircleImage(diameter: 18.0, color: theme.chat.historyNavigation.badgeBackgroundColor, strokeColor: theme.chat.historyNavigation.badgeStrokeColor, strokeWidth: 1.0, backgroundColor: nil)
|
||||
return generateStretchableFilledCircleImage(diameter: 18.0, color: theme.chat.historyNavigation.badgeBackgroundColor, strokeColor: theme.chat.historyNavigation.badgeStrokeColor, strokeWidth: 1.0, backgroundColor: nil)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,12 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
let panelHeight = defaultHeight(metrics: metrics)
|
||||
|
||||
if self.discussButton.isHidden {
|
||||
self.button.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: width - leftInset - rightInset, height: panelHeight))
|
||||
if let action = self.action, action == .muteNotifications || action == .unmuteNotifications {
|
||||
let buttonWidth = self.button.titleNode.calculateSizeThatFits(CGSize(width: width, height: panelHeight)).width + 24.0
|
||||
self.button.frame = CGRect(origin: CGPoint(x: floor((width - buttonWidth) / 2.0), y: 0.0), size: CGSize(width: buttonWidth, height: panelHeight))
|
||||
} else {
|
||||
self.button.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: width - leftInset - rightInset, height: panelHeight))
|
||||
}
|
||||
} else {
|
||||
let availableWidth = min(600.0, width - leftInset - rightInset)
|
||||
let leftOffset = floor((width - availableWidth) / 2.0)
|
||||
|
@ -273,6 +273,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private weak var currentContextController: ContextController?
|
||||
|
||||
private weak var sendMessageActionsController: ChatSendMessageActionSheetController?
|
||||
private var searchResultsController: ChatSearchResultsController?
|
||||
|
||||
private var screenCaptureEventsDisposable: Disposable?
|
||||
private let chatAdditionalDataDisposable = MetaDisposable()
|
||||
@ -413,7 +414,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, message: message, standalone: false, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.navigationController as? NavigationController, dismissInput: {
|
||||
return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, message: message, standalone: false, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.effectiveNavigationController, dismissInput: {
|
||||
self?.chatDisplayNode.dismissInput()
|
||||
}, present: { c, a in
|
||||
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
@ -760,7 +761,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case let .url(url):
|
||||
if isGame {
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
|
||||
strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
|
||||
} else {
|
||||
strongSelf.openUrl(url, concealed: false)
|
||||
}
|
||||
@ -973,7 +974,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.sendMessages([.message(text: command, attributes: attributes, mediaReference: nil, replyToMessageId: (postAsReply && messageId != nil) ? messageId! : nil, localGroupingKey: nil)])
|
||||
}
|
||||
}, openInstantPage: { [weak self] message, associatedData in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, let navigationController = strongSelf.navigationController as? NavigationController, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, let navigationController = strongSelf.effectiveNavigationController, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
|
||||
openChatInstantPage(context: strongSelf.context, message: message, sourcePeerType: associatedData?.automaticDownloadPeerType, navigationController: navigationController)
|
||||
}
|
||||
}, openWallpaper: { [weak self] message in
|
||||
@ -1047,7 +1048,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, !hashtag.isEmpty {
|
||||
let searchController = HashtagSearchController(context: strongSelf.context, peer: peer, query: hashtag)
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(searchController)
|
||||
strongSelf.effectiveNavigationController?.pushViewController(searchController)
|
||||
}
|
||||
}))
|
||||
}, updateInputState: { [weak self] f in
|
||||
@ -1280,7 +1281,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self {
|
||||
let searchController = HashtagSearchController(context: strongSelf.context, peer: peer, query: hashtag)
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(searchController)
|
||||
strongSelf.effectiveNavigationController?.pushViewController(searchController)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1447,7 +1448,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self?.present(controller, in: .window(.root), with: arguments)
|
||||
}, pushController: { [weak self] controller in
|
||||
if let strongSelf = self {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
strongSelf.effectiveNavigationController?.pushViewController(controller)
|
||||
}
|
||||
}, completed: {})
|
||||
}
|
||||
@ -1638,7 +1639,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
case .lookup:
|
||||
let controller = UIReferenceLibraryViewController(term: text)
|
||||
if let window = strongSelf.navigationController?.view.window {
|
||||
if let window = strongSelf.effectiveNavigationController?.view.window {
|
||||
controller.popoverPresentationController?.sourceView = window
|
||||
controller.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0))
|
||||
window.rootViewController?.present(controller, animated: true)
|
||||
@ -1688,7 +1689,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.chatTitleView?.pressed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.chatLocation == .peer(strongSelf.context.account.peerId) {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(PeerMediaCollectionController(context: strongSelf.context, peerId: strongSelf.context.account.peerId))
|
||||
strongSelf.effectiveNavigationController?.pushViewController(PeerMediaCollectionController(context: strongSelf.context, peerId: strongSelf.context.account.peerId))
|
||||
} else {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
return $0.updatedTitlePanelContext {
|
||||
@ -1988,7 +1989,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
strongSelf.updateReminderActivity()
|
||||
if let upgradedToPeerId = upgradedToPeerId {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
if let navigationController = strongSelf.effectiveNavigationController {
|
||||
var viewControllers = navigationController.viewControllers
|
||||
if let index = viewControllers.firstIndex(where: { $0 === strongSelf }) {
|
||||
viewControllers[index] = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(upgradedToPeerId))
|
||||
@ -3172,6 +3173,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return current.updatedSearch(nil)
|
||||
})
|
||||
strongSelf.updateItemNodesSearchTextHighlightStates()
|
||||
strongSelf.searchResultsController = nil
|
||||
}
|
||||
}, updateMessageSearch: { [weak self] query in
|
||||
if let strongSelf = self {
|
||||
@ -3183,24 +3185,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
strongSelf.updateItemNodesSearchTextHighlightStates()
|
||||
strongSelf.searchResultsController = nil
|
||||
}
|
||||
}, openSearchResults: { [weak self] in
|
||||
if let strongSelf = self, let searchData = strongSelf.presentationInterfaceState.search, let results = searchData.resultsState {
|
||||
let _ = (strongSelf.searchResult.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] searchResult in
|
||||
if let strongSelf = self, let searchResult = searchResult?.0 {
|
||||
let controller = ChatSearchResultsController(context: strongSelf.context, searchQuery: searchData.query, messages: searchResult.messages, navigateToMessageIndex: { index in
|
||||
strongSelf.interfaceInteraction?.navigateMessageSearch(.index(index))
|
||||
})
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
if case let .inline(navigationController) = strongSelf.presentationInterfaceState.mode {
|
||||
navigationController?.pushViewController(controller)
|
||||
} else {
|
||||
strongSelf.push(controller)
|
||||
}
|
||||
if let controller = strongSelf.searchResultsController {
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
if case let .inline(navigationController) = strongSelf.presentationInterfaceState.mode {
|
||||
navigationController?.pushViewController(controller)
|
||||
} else {
|
||||
strongSelf.push(controller)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let _ = (strongSelf.searchResult.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] searchResult in
|
||||
if let strongSelf = self, let searchResult = searchResult?.0 {
|
||||
let controller = ChatSearchResultsController(context: strongSelf.context, searchQuery: searchData.query, messages: searchResult.messages, navigateToMessageIndex: { index in
|
||||
strongSelf.interfaceInteraction?.navigateMessageSearch(.index(index))
|
||||
})
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
if case let .inline(navigationController) = strongSelf.presentationInterfaceState.mode {
|
||||
navigationController?.pushViewController(controller)
|
||||
} else {
|
||||
strongSelf.push(controller)
|
||||
}
|
||||
strongSelf.searchResultsController = controller
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, navigateMessageSearch: { [weak self] action in
|
||||
if let strongSelf = self {
|
||||
@ -3286,7 +3299,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
if let navigationController = strongSelf.effectiveNavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always))
|
||||
}
|
||||
}, openPeerInfo: { [weak self] in
|
||||
@ -4803,11 +4816,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
private func updateItemNodesSearchTextHighlightStates() {
|
||||
var searchString: String?
|
||||
var resultsMessageIndices: [MessageIndex]?
|
||||
if let search = self.presentationInterfaceState.search, let resultsState = search.resultsState, !resultsState.messageIndices.isEmpty {
|
||||
searchString = search.query
|
||||
resultsMessageIndices = resultsState.messageIndices
|
||||
}
|
||||
if searchString != self.controllerInteraction?.searchTextHighightState {
|
||||
self.controllerInteraction?.searchTextHighightState = searchString
|
||||
if searchString != self.controllerInteraction?.searchTextHighightState?.0 || resultsMessageIndices?.count != self.controllerInteraction?.searchTextHighightState?.1.count {
|
||||
var searchTextHighightState: (String, [MessageIndex])?
|
||||
if let searchString = searchString, let resultsMessageIndices = resultsMessageIndices {
|
||||
searchTextHighightState = (searchString, resultsMessageIndices)
|
||||
}
|
||||
self.controllerInteraction?.searchTextHighightState = searchTextHighightState
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateSearchTextHighlightState()
|
||||
@ -4940,7 +4959,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView in
|
||||
if let strongSelf = self, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios") == nil && !strongSelf.presentationInterfaceState.isNotAccessible {
|
||||
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
|
||||
strongSelf.effectiveNavigationController?.pushViewController(infoController)
|
||||
}
|
||||
}
|
||||
}))
|
||||
@ -5319,11 +5338,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, enqueueMediaMessages: { signals in
|
||||
if let strongSelf = self {
|
||||
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting)
|
||||
if editingMedia {
|
||||
strongSelf.editMessageMediaWithLegacySignals(signals)
|
||||
} else {
|
||||
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting)
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
strongSelf.effectiveNavigationController?.pushViewController(controller)
|
||||
}
|
||||
}, presentSelectionLimitExceeded: {
|
||||
guard let strongSelf = self else {
|
||||
@ -5363,7 +5386,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(legacyController)
|
||||
strongSelf.effectiveNavigationController?.pushViewController(legacyController)
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -5390,7 +5413,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, enqueueMediaMessages: { [weak self] signals in
|
||||
if let strongSelf = self {
|
||||
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting)
|
||||
if editingMessage {
|
||||
strongSelf.editMessageMediaWithLegacySignals(signals)
|
||||
} else {
|
||||
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting)
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
@ -5418,7 +5445,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(legacyLocationPickerController(context: strongSelf.context, selfPeer: selfPeer, peer: peer, sendLocation: { coordinate, venue, _ in
|
||||
strongSelf.effectiveNavigationController?.pushViewController(legacyLocationPickerController(context: strongSelf.context, selfPeer: selfPeer, peer: peer, sendLocation: { coordinate, venue, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -5463,7 +5490,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: self.context, title: { $0.Contacts_Title }, displayDeviceContacts: true))
|
||||
contactsController.navigationPresentation = .modal
|
||||
self.chatDisplayNode.dismissInput()
|
||||
(self.navigationController as? NavigationController)?.pushViewController(contactsController)
|
||||
self.effectiveNavigationController?.pushViewController(contactsController)
|
||||
self.controllerNavigationDisposable.set((contactsController.result
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
@ -5542,7 +5569,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.sendMessages([message])
|
||||
}
|
||||
}), completed: nil, cancelled: nil)
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(contactController)
|
||||
strongSelf.effectiveNavigationController?.pushViewController(contactController)
|
||||
}
|
||||
}
|
||||
}))
|
||||
@ -5552,7 +5579,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
private func presentPollCreation() {
|
||||
if case let .peer(peerId) = self.chatLocation {
|
||||
(self.navigationController as? NavigationController)?.pushViewController(createPollController(context: self.context, peerId: peerId, completion: { [weak self] message in
|
||||
self.effectiveNavigationController?.pushViewController(createPollController(context: self.context, peerId: peerId, completion: { [weak self] message in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -5834,10 +5861,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.recorderFeedback?.error()
|
||||
strongSelf.recorderFeedback = nil
|
||||
} else if let waveform = data.waveform {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
|
||||
let resource = LocalFileMediaResource(fileId: randomId, size: data.compressedData.count)
|
||||
let resource = LocalFileMediaResource(fileId: arc4random64(), size: data.compressedData.count)
|
||||
|
||||
strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData)
|
||||
|
||||
@ -6151,7 +6175,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (self.presentationInterfaceState.isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) {
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
if let navigationController = self.effectiveNavigationController {
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(messageId), keepStack: .always))
|
||||
}
|
||||
} else if case let .peer(peerId) = self.chatLocation, (messageLocation.peerId == peerId || forceInCurrentChat) {
|
||||
@ -6292,7 +6316,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.chatDisplayNode.historyNode.scrollToMessage(from: fromIndex, to: index, animated: animated, scrollPosition: scrollPosition)
|
||||
completion?()
|
||||
} else {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message($0) }))
|
||||
strongSelf.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message($0) }))
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
@ -6380,14 +6404,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let parentController = strongSelf.parentController {
|
||||
(parentController.navigationController as? NavigationController)?.replaceTopController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)), animated: false, ready: ready)
|
||||
} else {
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceTopController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)), animated: false, ready: ready)
|
||||
strongSelf.effectiveNavigationController?.replaceTopController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)), animated: false, ready: ready)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
self.chatDisplayNode.dismissInput()
|
||||
(self.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
self.effectiveNavigationController?.pushViewController(controller)
|
||||
}
|
||||
|
||||
private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: Message?) {
|
||||
@ -6427,7 +6451,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.navigationActionDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
|
||||
strongSelf.effectiveNavigationController?.pushViewController(infoController)
|
||||
}
|
||||
}
|
||||
}))
|
||||
@ -6443,15 +6467,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
})
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
if let strongSelf = self, let navigationController = strongSelf.effectiveNavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, updateTextInputState: textInputState))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
(self.navigationController as? NavigationController)?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), subject: subject))
|
||||
self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), subject: subject))
|
||||
}
|
||||
case let .withBotStartPayload(botStart):
|
||||
(self.navigationController as? NavigationController)?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), botStart: botStart))
|
||||
self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), botStart: botStart))
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -6497,14 +6521,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}))
|
||||
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceTopController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)), animated: false, ready: ready)
|
||||
strongSelf.effectiveNavigationController?.replaceTopController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)), animated: false, ready: ready)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
self.chatDisplayNode.dismissInput()
|
||||
(self.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
self.effectiveNavigationController?.pushViewController(controller)
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -6675,7 +6699,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
if deleteChat {
|
||||
let _ = removePeerChat(account: strongSelf.context.account, peerId: chatPeer.id, reportChatSpam: reportSpam).start()
|
||||
(strongSelf.navigationController as? NavigationController)?.filterController(strongSelf, animated: true)
|
||||
strongSelf.effectiveNavigationController?.filterController(strongSelf, animated: true)
|
||||
} else if reportSpam {
|
||||
let _ = TelegramCore.reportPeer(account: strongSelf.context.account, peerId: peer.id, reason: .spam).start()
|
||||
}
|
||||
@ -6808,7 +6832,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.commitPurposefulAction()
|
||||
self.chatDisplayNode.historyNode.disconnect()
|
||||
let _ = removePeerChat(account: self.context.account, peerId: peerId, reportChatSpam: reportChatSpam).start()
|
||||
(self.navigationController as? NavigationController)?.popToRoot(animated: true)
|
||||
self.effectiveNavigationController?.popToRoot(animated: true)
|
||||
|
||||
let _ = requestUpdatePeerIsBlocked(account: self.context.account, peerId: peerId, isBlocked: true).start()
|
||||
}
|
||||
@ -6830,7 +6854,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
private func openResolved(_ result: ResolvedUrl) {
|
||||
self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat, navigationController: self.navigationController as? NavigationController, openPeer: { [weak self] peerId, navigation in
|
||||
self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat, navigationController: self.effectiveNavigationController, openPeer: { [weak self] peerId, navigation in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -6840,7 +6864,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let subject = subject, case let .message(messageId) = subject {
|
||||
strongSelf.navigateToMessage(from: nil, to: .id(messageId))
|
||||
}
|
||||
} else if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
} else if let navigationController = strongSelf.effectiveNavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always))
|
||||
}
|
||||
case .info:
|
||||
@ -6849,7 +6873,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self, peer.restrictionText(platform: "ios") == nil {
|
||||
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
|
||||
strongSelf.effectiveNavigationController?.pushViewController(infoController)
|
||||
}
|
||||
}
|
||||
}))
|
||||
@ -6858,7 +6882,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedBotStartPayload(startPayload.payload)
|
||||
})
|
||||
} else if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
} else if let navigationController = strongSelf.effectiveNavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), botStart: startPayload))
|
||||
}
|
||||
default:
|
||||
@ -6947,7 +6971,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
private func openUrlIn(_ url: String) {
|
||||
let actionSheet = OpenInActionSheetController(context: self.context, item: .url(url: url), openUrl: { [weak self] url in
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
if let strongSelf = self, let navigationController = strongSelf.effectiveNavigationController {
|
||||
strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: url, forceExternal: true, presentationData: strongSelf.presentationData, navigationController: navigationController, dismissInput: {
|
||||
self?.chatDisplayNode.dismissInput()
|
||||
})
|
||||
@ -7003,7 +7027,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if let selectedTransitionNode = selectedTransitionNode {
|
||||
if let previewData = chatMessagePreviewControllerData(context: self.context, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: self.navigationController as? NavigationController) {
|
||||
if let previewData = chatMessagePreviewControllerData(context: self.context, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: self.effectiveNavigationController) {
|
||||
switch previewData {
|
||||
case let .gallery(gallery):
|
||||
gallery.setHintWillBePresentedInPreviewingContext(true)
|
||||
@ -7086,7 +7110,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
if let safariController = viewControllerToCommit as? SFSafariViewController {
|
||||
if let window = self.navigationController?.view.window {
|
||||
if let window = self.effectiveNavigationController?.view.window {
|
||||
window.rootViewController?.present(safariController, animated: true)
|
||||
}
|
||||
}
|
||||
@ -7171,9 +7195,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private func debugStreamSingleVideo(_ id: MessageId) {
|
||||
let gallery = GalleryController(context: self.context, source: .peerMessagesAtId(id), streamSingleVideo: true, replaceRootController: { [weak self] controller, ready in
|
||||
if let strongSelf = self {
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceTopController(controller, animated: false, ready: ready)
|
||||
strongSelf.effectiveNavigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
}
|
||||
}, baseNavigationController: self.navigationController as? NavigationController)
|
||||
}, baseNavigationController: self.effectiveNavigationController)
|
||||
|
||||
self.chatDisplayNode.dismissInput()
|
||||
self.present(gallery, in: .window(.root), with: GalleryControllerPresentationArguments(transitionArguments: { [weak self] messageId, media in
|
||||
@ -7741,6 +7765,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private func openScheduledMessages() {
|
||||
let controller = ChatControllerImpl(context: self.context, chatLocation: self.chatLocation, subject: .scheduledMessages)
|
||||
controller.navigationPresentation = .modal
|
||||
(self.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
self.effectiveNavigationController?.pushViewController(controller)
|
||||
}
|
||||
|
||||
private var effectiveNavigationController: NavigationController? {
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
return navigationController
|
||||
} else if case let .inline(navigationController) = self.presentationInterfaceState.mode {
|
||||
return navigationController
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ public final class ChatControllerInteraction {
|
||||
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
||||
var pollActionState: ChatInterfacePollActionState
|
||||
var stickerSettings: ChatInterfaceStickerSettings
|
||||
var searchTextHighightState: String?
|
||||
var searchTextHighightState: (String, [MessageIndex])?
|
||||
var seenOneTimeAnimatedMedia = Set<MessageId>()
|
||||
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, TapLongTapOrDoubleTapGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, String, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String) -> Void, openMessageReactions: @escaping (MessageId) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
|
||||
|
@ -149,7 +149,7 @@ class ChatMessageBubbleContentNode: ASDisplayNode {
|
||||
return false
|
||||
}
|
||||
|
||||
func updateSearchTextHighlightState(text: String?) {
|
||||
func updateSearchTextHighlightState(text: String?, messages: [MessageIndex]?) {
|
||||
}
|
||||
|
||||
func updateAutomaticMediaDownloadSettings(_ settings: MediaAutoDownloadSettings) {
|
||||
|
@ -2676,7 +2676,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
|
||||
override func updateSearchTextHighlightState() {
|
||||
for contentNode in self.contentNodes {
|
||||
contentNode.updateSearchTextHighlightState(text: self.item?.controllerInteraction.searchTextHighightState)
|
||||
contentNode.updateSearchTextHighlightState(text: self.item?.controllerInteraction.searchTextHighightState?.0, messages: self.item?.controllerInteraction.searchTextHighightState?.1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,12 +496,12 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
override func updateSearchTextHighlightState(text: String?) {
|
||||
override func updateSearchTextHighlightState(text: String?, messages: [MessageIndex]?) {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
let rectsSet: [[CGRect]]
|
||||
if let text = text, !text.isEmpty {
|
||||
if let text = text, let messages = messages, !text.isEmpty, messages.contains(item.message.index) {
|
||||
rectsSet = self.textNode.textRangesRects(text: text)
|
||||
} else {
|
||||
rectsSet = []
|
||||
|
@ -160,9 +160,7 @@ private func fetchCachedStickerAJpegRepresentation(account: Account, resource: M
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||
if let image = WebP.convert(fromWebP: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
let colorData = NSMutableData()
|
||||
@ -239,9 +237,7 @@ private func fetchCachedScaledImageRepresentation(resource: MediaResource, resou
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||
if let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
let size: CGSize
|
||||
@ -314,9 +310,7 @@ private func fetchCachedVideoFirstFrameRepresentation(account: Account, resource
|
||||
|
||||
let fullSizeImage = try imageGenerator.copyCGImage(at: CMTime(seconds: 0.0, preferredTimescale: asset.duration.timescale), actualTime: nil)
|
||||
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
if let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) {
|
||||
@ -350,9 +344,7 @@ private func fetchCachedScaledVideoFirstFrameRepresentation(account: Account, re
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: firstFrame.path), options: [.mappedIfSafe]) {
|
||||
if let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
let size = representation.size
|
||||
@ -387,9 +379,7 @@ private func fetchCachedBlurredWallpaperRepresentation(resource: MediaResource,
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||
if let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
if let colorImage = blurredImage(image, radius: 45.0), let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) {
|
||||
@ -416,9 +406,7 @@ private func fetchCachedPatternWallpaperMaskRepresentation(resource: MediaResour
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||
if let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
let size = representation.size != nil ? image.size.aspectFitted(representation.size!) : CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale)
|
||||
@ -455,9 +443,7 @@ private func fetchCachedPatternWallpaperRepresentation(resource: MediaResource,
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||
if let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
let size = CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale)
|
||||
@ -541,9 +527,7 @@ private func fetchCachedBlurredWallpaperRepresentation(account: Account, resourc
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||
if let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
if let colorImage = blurredImage(image, radius: 45.0), let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) {
|
||||
@ -570,9 +554,7 @@ private func fetchCachedPatternWallpaperMaskRepresentation(account: Account, res
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||
if let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
let size = representation.size != nil ? image.size.aspectFitted(representation.size!) : CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale)
|
||||
@ -609,9 +591,7 @@ private func fetchCachedPatternWallpaperRepresentation(account: Account, resourc
|
||||
return Signal({ subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
|
||||
if let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
let size = CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale)
|
||||
@ -663,9 +643,7 @@ private func fetchCachedAlbumArtworkRepresentation(account: Account, resource: M
|
||||
switch result {
|
||||
case let .artworkData(data):
|
||||
if let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
var size = image.size
|
||||
@ -707,9 +685,7 @@ private func fetchEmojiThumbnailRepresentation(account: Account, resource: Media
|
||||
return .never()
|
||||
}
|
||||
return Signal({ subscriber in
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
let nsString = (resource.emoji as NSString)
|
||||
@ -818,9 +794,7 @@ private func fetchEmojiRepresentation(account: Account, resource: MediaResource,
|
||||
|> mapToSignal { data in
|
||||
return Signal({ subscriber in
|
||||
if let data = data, let image = UIImage(data: data) {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
let path = NSTemporaryDirectory() + "\(arc4random64())"
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
let size = CGSize(width: 160.0, height: 160.0)
|
||||
|
@ -302,7 +302,7 @@ class MediaInputPaneTrendingItemNode: ListViewItemNode {
|
||||
|
||||
return (layout, { [weak self] synchronousLoads in
|
||||
if let strongSelf = self {
|
||||
if item.topItems.count < Int(item.info.count) && item.topItems.count < 5 && strongSelf.item?.info.id != item.info.id {
|
||||
if (item.topItems.count < Int(item.info.count) || item.topItems.count < 5) && strongSelf.item?.info.id != item.info.id {
|
||||
strongSelf.preloadDisposable.set(preloadedFeaturedStickerSet(network: item.account.network, postbox: item.account.postbox, id: item.info.id).start())
|
||||
}
|
||||
strongSelf.item = item
|
||||
|
Binary file not shown.
@ -18,6 +18,7 @@ import WalletUI
|
||||
import LegacyMediaPickerUI
|
||||
import LocalMediaResources
|
||||
import OverlayStatusController
|
||||
import AlertUI
|
||||
|
||||
private enum CallStatusText: Equatable {
|
||||
case none
|
||||
@ -1042,10 +1043,18 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
let tonContext = storedContext.context(config: config, blockchainName: blockchainName)
|
||||
|
||||
if wallets.wallets.isEmpty {
|
||||
if let _ = currentPublicKey {
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .intro, walletCreatedPreloadState: nil))
|
||||
if case .send = walletContext {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = textAlertController(context: context, title: presentationData.strings.Conversation_WalletRequiredTitle, text: presentationData.strings.Conversation_WalletRequiredText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Conversation_WalletRequiredNotNow, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Conversation_WalletRequiredSetup, action: { [weak self] in
|
||||
self?.openWallet(context: context, walletContext: .generic, present: present)
|
||||
})])
|
||||
present(controller)
|
||||
} else {
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .secureStorageNotAvailable, walletCreatedPreloadState: nil))
|
||||
if let _ = currentPublicKey {
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .intro, walletCreatedPreloadState: nil))
|
||||
} else {
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .secureStorageNotAvailable, walletCreatedPreloadState: nil))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let walletInfo = wallets.wallets[0].info
|
||||
|
@ -426,7 +426,6 @@ final class SharedMediaPlayer {
|
||||
switch playbackItem {
|
||||
case let .audio(player):
|
||||
player.setBaseRate(rateValue)
|
||||
|
||||
case let .instantVideo(node):
|
||||
node.setBaseRate(rateValue)
|
||||
}
|
||||
|
@ -174,6 +174,10 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
|
||||
}
|
||||
|
||||
func setup(item: StickerPaneSearchGlobalItem) {
|
||||
if item.topItems.count < Int(item.info.count) && item.topItems.count < 5 && self.item?.info.id != item.info.id {
|
||||
self.preloadDisposable.set(preloadedFeaturedStickerSet(network: item.account.network, postbox: item.account.postbox, id: item.info.id).start())
|
||||
}
|
||||
|
||||
self.item = item
|
||||
self.setNeedsLayout()
|
||||
|
||||
@ -210,17 +214,8 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.info.title, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset - 20.0 - installLayout.size.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (descriptionLayout, descriptionApply) = makeDescriptionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.StickerPack_StickerCount(item.info.count), font: statusFont, textColor: item.theme.chat.inputMediaPanel.stickersSectionTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
var topItems = item.topItems
|
||||
if topItems.count > 5 {
|
||||
topItems.removeSubrange(5 ..< topItems.count)
|
||||
}
|
||||
|
||||
|
||||
let strongSelf = self
|
||||
if item.topItems.count < Int(item.info.count) && item.topItems.count < 5 && strongSelf.item?.info.id != item.info.id {
|
||||
strongSelf.preloadDisposable.set(preloadedFeaturedStickerSet(network: item.account.network, postbox: item.account.postbox, id: item.info.id).start())
|
||||
}
|
||||
strongSelf.item = item
|
||||
|
||||
let _ = installApply()
|
||||
let _ = titleApply()
|
||||
@ -268,6 +263,11 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
|
||||
var offset = sideInset
|
||||
let itemSpacing = (max(0, availableWidth - 5.0 * itemSide - sideInset * 2.0)) / 4.0
|
||||
|
||||
var topItems = item.topItems
|
||||
if topItems.count > 5 {
|
||||
topItems.removeSubrange(5 ..< topItems.count)
|
||||
}
|
||||
|
||||
for i in 0 ..< topItems.count {
|
||||
let file = topItems[i].file
|
||||
let node: TrendingTopItemNode
|
||||
|
@ -125,7 +125,7 @@ private final class WalletQrViewScreenNode: ViewControllerTracingNode {
|
||||
|
||||
self.imageNode = TransformImageNode()
|
||||
self.imageNode.clipsToBounds = true
|
||||
self.imageNode.cornerRadius = 12.0
|
||||
self.imageNode.cornerRadius = 14.0
|
||||
|
||||
self.iconNode = AnimatedStickerNode()
|
||||
if let path = getAppBundle().path(forResource: "WalletIntroStatic", ofType: "tgs") {
|
||||
|
@ -480,8 +480,8 @@ public func walletSendScreen(context: AccountContext, tonContext: TonContext, ra
|
||||
var emptyItem: ItemListControllerEmptyStateItem?
|
||||
if let walletState = walletState {
|
||||
let textLength: Int = state.comment.data(using: .utf8, allowLossyConversion: true)?.count ?? 0
|
||||
|
||||
sendEnabled = isValidAddress(state.address, exactLength: true) && amount > 0 && amount <= walletState.balance && state.comment.count <= walletTextLimit
|
||||
sendEnabled = isValidAddress(state.address, exactLength: true) && amount > 0 && amount <= walletState.balance && textLength <= walletTextLimit
|
||||
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Wallet_Send_Send), style: .bold, enabled: sendEnabled, action: {
|
||||
arguments.proceed()
|
||||
})
|
||||
@ -489,7 +489,7 @@ public func walletSendScreen(context: AccountContext, tonContext: TonContext, ra
|
||||
rightNavigationButton = nil
|
||||
emptyItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
|
||||
}
|
||||
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_Send_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: walletState?.balance, state: state), style: .blocks, focusItemTag: focusItemTag, emptyStateItem: emptyItem, animateChanges: false)
|
||||
|
||||
|
@ -140,14 +140,17 @@ public final class WalletSplashScreen: ViewController {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var title: String?
|
||||
let text: String
|
||||
switch error {
|
||||
case .generic:
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
case .network:
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_NetworkError
|
||||
title = strongSelf.presentationData.strings.Wallet_Send_NetworkErrorTitle
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_NetworkErrorText
|
||||
case .notEnoughFunds:
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_ErrorNotEnoughFunds
|
||||
title = strongSelf.presentationData.strings.Wallet_Send_ErrorNotEnoughFundsTitle
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_ErrorNotEnoughFundsText
|
||||
case .messageTooLong:
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
case .invalidAddress:
|
||||
@ -173,7 +176,7 @@ public final class WalletSplashScreen: ViewController {
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
}
|
||||
let controller = textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
let controller = textAlertController(context: strongSelf.context, title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
navigationController.popViewController(animated: true)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user