mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-25 01:22:41 +00:00
Various fixes
This commit is contained in:
parent
7c0c73d9bf
commit
4da3336dfb
3
BUCK
3
BUCK
@ -383,8 +383,9 @@ apple_binary(
|
||||
"//submodules/BuildConfig:BuildConfig",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
"$SDKROOT/System/Library/Frameworks/Intents.framework",
|
||||
"$SDKROOT/System/Library/Frameworks/Contacts.framework",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -4870,11 +4870,11 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Wallet.TransactionInfo.SenderHeader" = "SENDER";
|
||||
"Wallet.TransactionInfo.CopyAddress" = "Copy Wallet Address";
|
||||
"Wallet.TransactionInfo.AddressCopied" = "Address copied to clipboard.";
|
||||
"Wallet.TransactionInfo.SendGrams" = "Send Grams";
|
||||
"Wallet.TransactionInfo.SendGrams" = "Send Grams to This Address";
|
||||
"Wallet.TransactionInfo.CommentHeader" = "COMMENT";
|
||||
"Wallet.TransactionInfo.StorageFeeHeader" = "STORAGE FEE";
|
||||
"Wallet.TransactionInfo.OtherFeeHeader" = "TRANSACTION FEE";
|
||||
"Wallet.TransactionInfo.StorageFeeInfo" = "Blockchain validators collect a tiny fee for storing information about your decentralized wallet. [More info]()";
|
||||
"Wallet.TransactionInfo.StorageFeeInfo" = "Blockchain validators collect a tiny fee for storing information about your decentralized wallet and processing your transactions. [More info]()";
|
||||
"Wallet.TransactionInfo.OtherFeeInfo" = "Blockchain validators collect a tiny fee for processing your decentralized transactions. [More info]()";
|
||||
"Wallet.TransactionInfo.FeeInfoURL" = "https://telegram.org/wallet/fee";
|
||||
"Wallet.WordCheck.Title" = "Test Time!";
|
||||
@ -4908,6 +4908,7 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"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";
|
||||
@ -4973,3 +4974,9 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Wallet.VoiceOver.Editing.ClearText" = "Clear text";
|
||||
|
||||
"Wallet.Receive.ShareInvoiceUrlInfo" = "Share this link with other Gram wallet owners to receive %@ Grams from them.";
|
||||
|
||||
"Conversation.ClearCache" = "Clear Cache";
|
||||
"ClearCache.Description" = "Media files will be deleted from your phone, but available for re-downloading when necessary.";
|
||||
"ClearCache.FreeSpaceDescription" = "If you want to save space on your device, you don't need to delete anything.\n\nYou can use cache settings to remove unnecessary media — and re-download files if you need them again.";
|
||||
"ClearCache.FreeSpace" = "Free Space";
|
||||
"ClearCache.Success" = "**%@** freed on your phone!";
|
||||
|
||||
@ -430,6 +430,9 @@ private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerCon
|
||||
info: WalletInfoTheme(
|
||||
incomingFundsTitleColor: UIColor(rgb: 0x00b12c),
|
||||
outgoingFundsTitleColor: UIColor(rgb: 0xff3b30)
|
||||
), transaction: WalletTransactionTheme(
|
||||
descriptionBackgroundColor: UIColor(rgb: 0xf1f1f4),
|
||||
descriptionTextColor: .black
|
||||
), setup: WalletSetupTheme(
|
||||
buttonFillColor: accentColor,
|
||||
buttonForegroundColor: .white,
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
#import "TGExtensionDelegate.h"
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#import "TGWatchCommon.h"
|
||||
#import "TGFileCache.h"
|
||||
#import "TGBridgeClient.h"
|
||||
#import "TGDateUtils.h"
|
||||
#import "TGNeoChatsController.h"
|
||||
|
||||
@interface TGExtensionDelegate ()
|
||||
@interface TGExtensionDelegate () <UNUserNotificationCenterDelegate>
|
||||
{
|
||||
NSString *_cachedContentSize;
|
||||
TGContentSizeCategory _sizeCategory;
|
||||
@ -26,10 +27,16 @@
|
||||
_audioCache.defaultFileExtension = @"m4a";
|
||||
|
||||
_imageCache = [[TGFileCache alloc] initWithName:@"images" useMemoryCache:true];
|
||||
|
||||
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
|
||||
|
||||
}
|
||||
|
||||
- (TGNeoChatsController *)chatsController
|
||||
{
|
||||
return (TGNeoChatsController *)[WKExtension sharedExtension].rootInterfaceController;
|
||||
|
||||
@ -301,7 +301,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
strongSelf.didShowProxyUnavailableTooltipController = true
|
||||
let tooltipController = TooltipController(content: .text(strongSelf.presentationData.strings.Proxy_TooltipUnavailable), timeout: 60.0, dismissByTapOutside: true)
|
||||
strongSelf.proxyUnavailableTooltipController = tooltipController
|
||||
tooltipController.dismissed = { [weak tooltipController] in
|
||||
tooltipController.dismissed = { [weak tooltipController] _ in
|
||||
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.proxyUnavailableTooltipController === tooltipController {
|
||||
strongSelf.proxyUnavailableTooltipController = nil
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ import AccountContext
|
||||
public enum DeleteChatPeerAction {
|
||||
case delete
|
||||
case clearHistory
|
||||
case clearCache
|
||||
case clearCacheSuggestion
|
||||
}
|
||||
|
||||
public final class DeleteChatPeerActionSheetItem: ActionSheetItem {
|
||||
@ -80,8 +82,20 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode {
|
||||
self.avatarNode.setPeer(account: context.account, theme: (context.sharedContext.currentPresentationData.with { $0 }).theme, peer: peer, overrideImage: overrideImage)
|
||||
}
|
||||
|
||||
let text: (String, [(Int, NSRange)])
|
||||
var attributedText: NSAttributedString?
|
||||
switch action {
|
||||
case .clearCache, .clearCacheSuggestion:
|
||||
switch action {
|
||||
case .clearCache:
|
||||
attributedText = NSAttributedString(string: strings.ClearCache_Description, font: Font.regular(14.0), textColor: theme.primaryTextColor)
|
||||
case .clearCacheSuggestion:
|
||||
attributedText = NSAttributedString(string: strings.ClearCache_FreeSpaceDescription, font: Font.regular(14.0), textColor: theme.primaryTextColor)
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
var text: (String, [(Int, NSRange)])?
|
||||
switch action {
|
||||
case .delete:
|
||||
if chatPeer.id == context.account.peerId {
|
||||
text = (strings.ChatList_DeleteSavedMessagesConfirmation, [])
|
||||
@ -96,16 +110,24 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode {
|
||||
}
|
||||
case .clearHistory:
|
||||
text = strings.ChatList_ClearChatConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder))
|
||||
}
|
||||
let attributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: text.0, font: Font.regular(14.0), textColor: theme.primaryTextColor))
|
||||
for (_, range) in text.1 {
|
||||
attributedText.addAttribute(.font, value: Font.semibold(14.0), range: range)
|
||||
default:
|
||||
break
|
||||
}
|
||||
if let text = text {
|
||||
var formattedAttributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: text.0, font: Font.regular(14.0), textColor: theme.primaryTextColor))
|
||||
for (_, range) in text.1 {
|
||||
formattedAttributedText.addAttribute(.font, value: Font.semibold(14.0), range: range)
|
||||
}
|
||||
attributedText = formattedAttributedText
|
||||
}
|
||||
}
|
||||
|
||||
self.textNode.attributedText = attributedText
|
||||
|
||||
self.accessibilityArea.accessibilityLabel = attributedText.string
|
||||
self.accessibilityArea.accessibilityTraits = .staticText
|
||||
if let attributedText = attributedText {
|
||||
self.textNode.attributedText = attributedText
|
||||
|
||||
self.accessibilityArea.accessibilityLabel = attributedText.string
|
||||
self.accessibilityArea.accessibilityTraits = .staticText
|
||||
}
|
||||
}
|
||||
|
||||
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
|
||||
@ -8,7 +8,6 @@ public enum ActionSheetButtonColor {
|
||||
case disabled
|
||||
}
|
||||
|
||||
|
||||
public enum ActionSheetButtonFont {
|
||||
case `default`
|
||||
case bold
|
||||
|
||||
@ -63,11 +63,12 @@ open class TooltipController: ViewController, StandalonePresentableController {
|
||||
|
||||
public private(set) var content: TooltipControllerContent
|
||||
|
||||
open func updateContent(_ content: TooltipControllerContent, animated: Bool, extendTimer: Bool) {
|
||||
open func updateContent(_ content: TooltipControllerContent, animated: Bool, extendTimer: Bool, arrowOnBottom: Bool = true) {
|
||||
if self.content != content {
|
||||
self.content = content
|
||||
if self.isNodeLoaded {
|
||||
self.controllerNode.updateText(self.content.text, transition: animated ? .animated(duration: 0.25, curve: .easeInOut) : .immediate)
|
||||
self.controllerNode.arrowOnBottom = arrowOnBottom
|
||||
if extendTimer, self.timeoutTimer != nil {
|
||||
self.timeoutTimer?.invalidate()
|
||||
self.timeoutTimer = nil
|
||||
@ -84,15 +85,17 @@ open class TooltipController: ViewController, StandalonePresentableController {
|
||||
private var timeoutTimer: SwiftSignalKit.Timer?
|
||||
|
||||
private var layout: ContainerViewLayout?
|
||||
private var initialArrowOnBottom: Bool
|
||||
|
||||
public var dismissed: (() -> Void)?
|
||||
public var dismissed: ((Bool) -> Void)?
|
||||
|
||||
public init(content: TooltipControllerContent, timeout: Double = 2.0, dismissByTapOutside: Bool = false, dismissByTapOutsideSource: Bool = false, dismissImmediatelyOnLayoutUpdate: Bool = false) {
|
||||
public init(content: TooltipControllerContent, timeout: Double = 2.0, dismissByTapOutside: Bool = false, dismissByTapOutsideSource: Bool = false, dismissImmediatelyOnLayoutUpdate: Bool = false, arrowOnBottom: Bool = true) {
|
||||
self.content = content
|
||||
self.timeout = timeout
|
||||
self.dismissByTapOutside = dismissByTapOutside
|
||||
self.dismissByTapOutsideSource = dismissByTapOutsideSource
|
||||
self.dismissImmediatelyOnLayoutUpdate = dismissImmediatelyOnLayoutUpdate
|
||||
self.initialArrowOnBottom = arrowOnBottom
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
@ -108,9 +111,10 @@ open class TooltipController: ViewController, StandalonePresentableController {
|
||||
}
|
||||
|
||||
override open func loadDisplayNode() {
|
||||
self.displayNode = TooltipControllerNode(content: self.content, dismiss: { [weak self] in
|
||||
self?.dismiss()
|
||||
self.displayNode = TooltipControllerNode(content: self.content, dismiss: { [weak self] tappedInside in
|
||||
self?.dismiss(tappedInside: tappedInside)
|
||||
}, dismissByTapOutside: self.dismissByTapOutside, dismissByTapOutsideSource: self.dismissByTapOutsideSource)
|
||||
self.controllerNode.arrowOnBottom = self.initialArrowOnBottom
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
@ -154,7 +158,7 @@ open class TooltipController: ViewController, StandalonePresentableController {
|
||||
if self.timeoutTimer == nil {
|
||||
let timeoutTimer = SwiftSignalKit.Timer(timeout: self.timeout, repeat: false, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.dismissed?()
|
||||
strongSelf.dismissed?(false)
|
||||
strongSelf.controllerNode.animateOut {
|
||||
self?.presentingViewController?.dismiss(animated: false)
|
||||
}
|
||||
@ -165,16 +169,20 @@ open class TooltipController: ViewController, StandalonePresentableController {
|
||||
}
|
||||
}
|
||||
|
||||
override open func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.dismissed?()
|
||||
private func dismiss(tappedInside: Bool, completion: (() -> Void)? = nil) {
|
||||
self.dismissed?(tappedInside)
|
||||
self.controllerNode.animateOut { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false)
|
||||
completion?()
|
||||
self?.presentingViewController?.dismiss(animated: false)
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
override open func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.dismiss(tappedInside: false, completion: completion)
|
||||
}
|
||||
|
||||
open func dismissImmediately() {
|
||||
self.dismissed?()
|
||||
self.dismissed?(false)
|
||||
self.presentingViewController?.dismiss(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import UIKit
|
||||
import AsyncDisplayKit
|
||||
|
||||
final class TooltipControllerNode: ASDisplayNode {
|
||||
private let dismiss: () -> Void
|
||||
private let dismiss: (Bool) -> Void
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
@ -19,7 +19,7 @@ final class TooltipControllerNode: ASDisplayNode {
|
||||
private var dismissedByTouchOutside = false
|
||||
private var dismissByTapOutsideSource = false
|
||||
|
||||
init(content: TooltipControllerContent, dismiss: @escaping () -> Void, dismissByTapOutside: Bool, dismissByTapOutsideSource: Bool) {
|
||||
init(content: TooltipControllerContent, dismiss: @escaping (Bool) -> Void, dismissByTapOutside: Bool, dismissByTapOutsideSource: Bool) {
|
||||
self.dismissByTapOutside = dismissByTapOutside
|
||||
self.dismissByTapOutsideSource = dismissByTapOutsideSource
|
||||
|
||||
@ -129,15 +129,16 @@ final class TooltipControllerNode: ASDisplayNode {
|
||||
eventIsPresses = event.type == .presses
|
||||
}
|
||||
if event.type == .touches || eventIsPresses {
|
||||
let pointInside = self.containerNode.frame.contains(point)
|
||||
if self.containerNode.frame.contains(point) || self.dismissByTapOutside {
|
||||
if !self.dismissedByTouchOutside {
|
||||
self.dismissedByTouchOutside = true
|
||||
self.dismiss()
|
||||
self.dismiss(pointInside)
|
||||
}
|
||||
} else if self.dismissByTapOutsideSource, let sourceRect = self.sourceRect, !sourceRect.contains(point) {
|
||||
if !self.dismissedByTouchOutside {
|
||||
self.dismissedByTouchOutside = true
|
||||
self.dismiss()
|
||||
self.dismiss(false)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -101,7 +101,7 @@ struct PasscodeKeyboardLayout {
|
||||
self.verticalThird = 176.0
|
||||
self.verticalFourth = 264.0
|
||||
self.size = CGSize(width: 265.0, height: 339.0)
|
||||
self.topOffset = 0.0
|
||||
self.topOffset = 120.0 + (layout.size.height - self.size.height - 120.0) / 2.0
|
||||
self.biometricsOffset = 30.0
|
||||
self.deleteOffset = 20.0
|
||||
}
|
||||
|
||||
@ -892,6 +892,8 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi
|
||||
}, reportChannel: {
|
||||
presentControllerImpl?(peerReportOptionsController(context: context, subject: .peer(peerId), present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
}, push: { c in
|
||||
pushControllerImpl?(c)
|
||||
}, completion: { _ in }), nil)
|
||||
}, leaveChannel: {
|
||||
let _ = (context.account.postbox.transaction { transaction -> Peer? in
|
||||
|
||||
@ -2071,7 +2071,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
|
||||
}, sendLiveLocation: { _, _ in }, theme: presentationData.theme, customLocationPicker: true, presentationCompleted: {
|
||||
clearHighlightImpl?()
|
||||
})
|
||||
presentControllerImpl?(controller, nil)
|
||||
pushControllerImpl?(controller)
|
||||
})
|
||||
}, displayLocationContextMenu: { text in
|
||||
displayCopyContextMenuImpl?(text, .location)
|
||||
|
||||
@ -91,7 +91,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
|
||||
})
|
||||
}
|
||||
} else {
|
||||
parent?.present(peerReportController(context: context, subject: subject, completion: completion), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
parent?.push(peerReportController(context: context, subject: subject, completion: completion))
|
||||
}
|
||||
f(.dismissWithoutContent)
|
||||
})))
|
||||
@ -102,11 +102,13 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
|
||||
parent.view.endEditing(true)
|
||||
parent.present(peerReportOptionsController(context: context, subject: subject, present: { [weak parent] c, a in
|
||||
parent?.present(c, in: .window(.root), with: a)
|
||||
}, push: { [weak parent] c in
|
||||
parent?.push(c)
|
||||
}, completion: completion), in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
||||
public func peerReportOptionsController(context: AccountContext, subject: PeerReportSubject, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (Bool) -> Void) -> ViewController {
|
||||
public func peerReportOptionsController(context: AccountContext, subject: PeerReportSubject, present: @escaping (ViewController, Any?) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping (Bool) -> Void) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = ActionSheetController(theme: ActionSheetControllerTheme(presentationTheme: presentationData.theme))
|
||||
|
||||
@ -169,7 +171,7 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
|
||||
})
|
||||
}
|
||||
} else {
|
||||
controller?.present(peerReportController(context: context, subject: subject, completion: completion), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
push(peerReportController(context: context, subject: subject, completion: completion))
|
||||
}
|
||||
|
||||
controller?.dismissAnimated()
|
||||
@ -348,6 +350,7 @@ private func peerReportController(context: AccountContext, subject: PeerReportSu
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
|
||||
@ -1179,6 +1179,8 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
|
||||
}, report: {
|
||||
presentControllerImpl?(peerReportOptionsController(context: context, subject: .peer(peerId), present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
}, push: { c in
|
||||
pushControllerImpl?(c)
|
||||
}, completion: { _ in }), nil)
|
||||
})
|
||||
|
||||
@ -1577,8 +1579,6 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
|
||||
let text: String = presentationData.strings.UserInfo_TapToCall
|
||||
|
||||
let tooltipController = TooltipController(content: .text(text), dismissByTapOutside: true)
|
||||
tooltipController.dismissed = {
|
||||
}
|
||||
controller.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in
|
||||
if let resultItemNode = resultItemNode {
|
||||
return (resultItemNode, callButtonFrame)
|
||||
|
||||
@ -2479,18 +2479,23 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
}
|
||||
|
||||
func enumerateMedia(lowerBound: MessageIndex?, limit: Int) -> ([PeerId: Set<MediaId>], [MediaId: Media], MessageIndex?) {
|
||||
func enumerateMedia(lowerBound: MessageIndex?, upperBound: MessageIndex?, limit: Int) -> ([PeerId: Set<MediaId>], [MediaId: Media], MessageIndex?) {
|
||||
var mediaRefs: [MediaId: Media] = [:]
|
||||
var result: [PeerId: Set<MediaId>] = [:]
|
||||
var lastIndex: MessageIndex?
|
||||
var count = 0
|
||||
self.valueBox.range(self.table, start: self.key(lowerBound == nil ? MessageIndex.absoluteLowerBound() : lowerBound!), end: self.key(MessageIndex.absoluteUpperBound()), values: { key, value in
|
||||
self.valueBox.range(self.table, start: self.key(lowerBound == nil ? MessageIndex.absoluteLowerBound() : lowerBound!), end: self.key(upperBound == nil ? MessageIndex.absoluteUpperBound() : upperBound!), values: { key, value in
|
||||
count += 1
|
||||
|
||||
let entry = self.readIntermediateEntry(key, value: value)
|
||||
lastIndex = entry.message.index
|
||||
|
||||
let message = entry.message
|
||||
|
||||
if let upperBound = upperBound, message.id.peerId != upperBound.id.peerId {
|
||||
return true
|
||||
}
|
||||
|
||||
var parsedMedia: [Media] = []
|
||||
|
||||
let embeddedMediaData = message.embeddedMediaData.sharedBufferNoCopy()
|
||||
|
||||
@ -756,10 +756,10 @@ public final class Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
public func enumerateMedia(lowerBound: MessageIndex?, limit: Int) -> ([PeerId: Set<MediaId>], [MediaId: Media], MessageIndex?) {
|
||||
public func enumerateMedia(lowerBound: MessageIndex?, upperBound: MessageIndex?, limit: Int) -> ([PeerId: Set<MediaId>], [MediaId: Media], MessageIndex?) {
|
||||
assert(!self.disposed)
|
||||
if let postbox = self.postbox {
|
||||
return postbox.messageHistoryTable.enumerateMedia(lowerBound: lowerBound, limit: limit)
|
||||
return postbox.messageHistoryTable.enumerateMedia(lowerBound: lowerBound, upperBound: upperBound, limit: limit)
|
||||
} else {
|
||||
return ([:], [:], nil)
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ public final class SegmentedControlTheme: Equatable {
|
||||
|
||||
public extension SegmentedControlTheme {
|
||||
convenience init(theme: PresentationTheme) {
|
||||
self.init(backgroundColor: theme.rootController.navigationSearchBar.inputFillColor, foregroundColor: theme.rootController.navigationBar.backgroundColor, shadowColor: .black, textColor: theme.rootController.navigationBar.primaryTextColor, dividerColor: theme.list.freeInputField.strokeColor)
|
||||
self.init(backgroundColor: theme.rootController.navigationBar.segmentedBackgroundColor, foregroundColor: theme.rootController.navigationBar.segmentedForegroundColor, shadowColor: .black, textColor: theme.rootController.navigationBar.segmentedTextColor, dividerColor: theme.rootController.navigationBar.segmentedDividerColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -79,6 +79,8 @@ static_library(
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
"//submodules/WalletUI:WalletUI",
|
||||
"//submodules/Markdown:Markdown",
|
||||
"//submodules/UndoUI:UndoUI",
|
||||
"//submodules/DeleteChatPeerActionSheetItem:DeleteChatPeerActionSheetItem",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
||||
@ -11,6 +11,8 @@ import PresentationDataUtils
|
||||
import OverlayStatusController
|
||||
import AccountContext
|
||||
import ItemListPeerItem
|
||||
import DeleteChatPeerActionSheetItem
|
||||
import UndoUI
|
||||
|
||||
private final class StorageUsageControllerArguments {
|
||||
let account: Account
|
||||
@ -284,7 +286,7 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
return cacheSettings
|
||||
})
|
||||
|
||||
var presentControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
|
||||
let statsPromise = Promise<CacheUsageStatsResult?>()
|
||||
let resetStats: () -> Void = {
|
||||
@ -338,7 +340,7 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
ActionSheetItemGroup(items: timeoutItems),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
presentControllerImpl?(controller)
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, openClearAll: {
|
||||
let _ = (statsPromise.get()
|
||||
|> take(1)
|
||||
@ -505,7 +507,7 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
presentControllerImpl?(controller)
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
return ActionDisposable { [weak controller] in
|
||||
Queue.mainQueue().async() {
|
||||
controller?.dismiss()
|
||||
@ -529,6 +531,8 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
clearDisposable.set((signal
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
statsPromise.set(.single(.result(resultStats)))
|
||||
|
||||
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(totalSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))").0), elevatedLayout: false, action: { _ in }), nil)
|
||||
}))
|
||||
}
|
||||
|
||||
@ -539,7 +543,7 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
ActionSheetItemGroup(items: items),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
presentControllerImpl?(controller)
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -547,8 +551,8 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
let _ = (statsPromise.get() |> take(1) |> deliverOnMainQueue).start(next: { [weak statsPromise] result in
|
||||
if let result = result, case let .result(stats) = result {
|
||||
var additionalPeerId: PeerId?
|
||||
if var categories = stats.media[peerId] {
|
||||
if let channel = stats.peers[peerId] as? TelegramChannel, case .group = channel.info {
|
||||
if var categories = stats.media[peerId], let peer = stats.peers[peerId] {
|
||||
if let channel = peer as? TelegramChannel, case .group = channel.info {
|
||||
for (_, peer) in stats.peers {
|
||||
if let group = peer as? TelegramGroup, let migrationReference = group.migrationReference, migrationReference.peerId == peerId {
|
||||
if let additionalCategories = stats.media[group.id] {
|
||||
@ -605,6 +609,8 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
}
|
||||
var items: [ActionSheetItem] = []
|
||||
|
||||
items.append(DeleteChatPeerActionSheetItem(context: context, peer: peer, chatPeer: peer, action: .clearCache, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder))
|
||||
|
||||
let validCategories: [PeerCacheUsageCategory] = [.image, .video, .audio, .file]
|
||||
|
||||
var totalSize: Int64 = 0
|
||||
@ -680,7 +686,7 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
presentControllerImpl?(controller)
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
return ActionDisposable { [weak controller] in
|
||||
Queue.mainQueue().async() {
|
||||
controller?.dismiss()
|
||||
@ -704,6 +710,8 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
clearDisposable.set((signal
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
statsPromise.set(.single(.result(resultStats)))
|
||||
|
||||
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(totalSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))").0), elevatedLayout: false, action: { _ in }), nil)
|
||||
}))
|
||||
}
|
||||
|
||||
@ -714,7 +722,7 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
ActionSheetItemGroup(items: items),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
presentControllerImpl?(controller)
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -738,8 +746,8 @@ public func storageUsageController(context: AccountContext, isModal: Bool = fals
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
presentControllerImpl = { [weak controller] c in
|
||||
controller?.present(c, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
|
||||
@ -282,16 +282,11 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
let horizontalOrigin: CGFloat = floor(min(max(8.0, sourceRect.midX - contentSize.width / 2.0), layout.size.width - contentSize.width - 8.0))
|
||||
|
||||
strongSelf.tooltipContainerNode.frame = CGRect(origin: CGPoint(x: horizontalOrigin, y: verticalOrigin), size: contentSize)
|
||||
//transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: horizontalOrigin, y: verticalOrigin), size: contentSize))
|
||||
strongSelf.tooltipContainerNode.relativeArrowPosition = (sourceRect.midX - horizontalOrigin, arrowOnBottom)
|
||||
|
||||
strongSelf.tooltipContainerNode.updateLayout(transition: .immediate)
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: 6.0, y: 17.0), size: textSize)
|
||||
// if transition.isAnimated, textFrame.size != self.textNode.frame.size {
|
||||
// transition.animatePositionAdditive(node: self.textNode, offset: CGPoint(x: textFrame.minX - self.textNode.frame.minX, y: 0.0))
|
||||
// }
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: 6.0, y: 17.0), size: textSize)
|
||||
strongSelf.textNode.frame = textFrame
|
||||
}
|
||||
})
|
||||
|
||||
@ -189,6 +189,10 @@ public final class CallController: ViewController {
|
||||
c.presentationArguments = a
|
||||
window.present(c, on: .root, blockInteraction: false, completion: {})
|
||||
}
|
||||
}, push: { [weak self] c in
|
||||
if let strongSelf = self {
|
||||
strongSelf.push(c)
|
||||
}
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
})
|
||||
|
||||
@ -274,6 +274,7 @@ public func callFeedbackController(sharedContext: SharedAccountContext, account:
|
||||
|
||||
|
||||
let controller = ItemListController(sharedContext: sharedContext, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
presentControllerImpl = { [weak controller] c in
|
||||
controller?.present(c, in: .window(.root))
|
||||
}
|
||||
|
||||
@ -265,7 +265,7 @@ func rateCallAndSendLogs(account: Account, callId: CallId, starsCount: Int, comm
|
||||
}
|
||||
}
|
||||
|
||||
public func callRatingController(sharedContext: SharedAccountContext, account: Account, callId: CallId, userInitiated: Bool, present: @escaping (ViewController, Any) -> Void) -> AlertController {
|
||||
public func callRatingController(sharedContext: SharedAccountContext, account: Account, callId: CallId, userInitiated: Bool, present: @escaping (ViewController, Any) -> Void, push: @escaping (ViewController) -> Void) -> AlertController {
|
||||
let presentationData = sharedContext.currentPresentationData.with { $0 }
|
||||
let theme = presentationData.theme
|
||||
let strings = presentationData.strings
|
||||
@ -281,8 +281,7 @@ public func callRatingController(sharedContext: SharedAccountContext, account: A
|
||||
}, apply: { rating in
|
||||
dismissImpl?(true)
|
||||
if rating < 4 {
|
||||
let controller = callFeedbackController(sharedContext: sharedContext, account: account, callId: callId, rating: rating, userInitiated: userInitiated)
|
||||
present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
push(callFeedbackController(sharedContext: sharedContext, account: account, callId: callId, rating: rating, userInitiated: userInitiated))
|
||||
} else {
|
||||
let _ = rateCallAndSendLogs(account: account, callId: callId, starsCount: rating, comment: "", userInitiated: userInitiated, includeLogs: false).start()
|
||||
}
|
||||
|
||||
@ -53,10 +53,17 @@ private final class CacheUsageStatsState {
|
||||
var mediaResourceIds: [MediaId: [MediaResourceId]] = [:]
|
||||
var allResourceIds = Set<WrappedMediaResourceId>()
|
||||
var lowerBound: MessageIndex?
|
||||
var upperBound: MessageIndex?
|
||||
}
|
||||
|
||||
public func collectCacheUsageStats(account: Account, additionalCachePaths: [String], logFilesPath: String) -> Signal<CacheUsageStatsResult, NoError> {
|
||||
let state = Atomic<CacheUsageStatsState>(value: CacheUsageStatsState())
|
||||
public func collectCacheUsageStats(account: Account, peerId: PeerId? = nil, additionalCachePaths: [String] = [], logFilesPath: String? = nil) -> Signal<CacheUsageStatsResult, NoError> {
|
||||
var initialState = CacheUsageStatsState()
|
||||
if let peerId = peerId {
|
||||
initialState.lowerBound = MessageIndex.lowerBound(peerId: peerId)
|
||||
initialState.upperBound = MessageIndex.upperBound(peerId: peerId)
|
||||
}
|
||||
|
||||
let state = Atomic<CacheUsageStatsState>(value: initialState)
|
||||
|
||||
let excludeResourceIds = account.postbox.transaction { transaction -> Set<WrappedMediaResourceId> in
|
||||
var result = Set<WrappedMediaResourceId>()
|
||||
@ -70,7 +77,7 @@ public func collectCacheUsageStats(account: Account, additionalCachePaths: [Stri
|
||||
return excludeResourceIds
|
||||
|> mapToSignal { excludeResourceIds -> Signal<CacheUsageStatsResult, NoError> in
|
||||
let fetch = account.postbox.transaction { transaction -> ([PeerId : Set<MediaId>], [MediaId : Media], MessageIndex?) in
|
||||
return transaction.enumerateMedia(lowerBound: state.with { $0.lowerBound }, limit: 1000)
|
||||
return transaction.enumerateMedia(lowerBound: state.with { $0.lowerBound }, upperBound: state.with { $0.upperBound }, limit: 1000)
|
||||
}
|
||||
|> mapError { _ -> CollectCacheUsageStatsError in preconditionFailure() }
|
||||
|
||||
@ -167,6 +174,27 @@ public func collectCacheUsageStats(account: Account, additionalCachePaths: [Stri
|
||||
}
|
||||
}
|
||||
if updatedLowerBound == nil {
|
||||
if peerId != nil {
|
||||
let (finalMedia, finalMediaResourceIds, allResourceIds) = state.with { state -> ([PeerId: [PeerCacheUsageCategory: [MediaId: Int64]]], [MediaId: [MediaResourceId]], Set<WrappedMediaResourceId>) in
|
||||
return (state.media, state.mediaResourceIds, state.allResourceIds)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> CacheUsageStats in
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
for peerId in finalMedia.keys {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
peers[peer.id] = peer
|
||||
if let associatedPeerId = peer.associatedPeerId, let associatedPeer = transaction.getPeer(associatedPeerId) {
|
||||
peers[associatedPeer.id] = associatedPeer
|
||||
}
|
||||
}
|
||||
}
|
||||
return CacheUsageStats(media: finalMedia, mediaResourceIds: finalMediaResourceIds, peers: peers, otherSize: 0, otherPaths: [], cacheSize: 0, tempPaths: [], tempSize: 0, immutableSize: 0)
|
||||
} |> mapError { _ -> CollectCacheUsageStatsError in preconditionFailure() }
|
||||
|> mapToSignal { stats -> Signal<CacheUsageStatsResult, CollectCacheUsageStatsError> in
|
||||
return .fail(.done(stats))
|
||||
}
|
||||
}
|
||||
|
||||
let (finalMedia, finalMediaResourceIds, allResourceIds) = state.with { state -> ([PeerId: [PeerCacheUsageCategory: [MediaId: Int64]]], [MediaId: [MediaResourceId]], Set<WrappedMediaResourceId>) in
|
||||
return (state.media, state.mediaResourceIds, state.allResourceIds)
|
||||
}
|
||||
@ -203,7 +231,7 @@ public func collectCacheUsageStats(account: Account, additionalCachePaths: [Stri
|
||||
}
|
||||
}
|
||||
}
|
||||
if let files = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: logFilesPath), includingPropertiesForKeys: [URLResourceKey.fileSizeKey], options: []) {
|
||||
if let logFilesPath = logFilesPath, let files = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: logFilesPath), includingPropertiesForKeys: [URLResourceKey.fileSizeKey], options: []) {
|
||||
for url in files {
|
||||
if let fileSize = (try? url.resourceValues(forKeys: Set([.fileSizeKey])))?.fileSize {
|
||||
immutableSize += Int64(fileSize)
|
||||
|
||||
@ -85,7 +85,11 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
separatorColor: UIColor(rgb: 0x3d3d40),
|
||||
badgeBackgroundColor: badgeFillColor,
|
||||
badgeStrokeColor: UIColor(rgb: 0x1c1c1d),
|
||||
badgeTextColor: badgeTextColor
|
||||
badgeTextColor: badgeTextColor,
|
||||
segmentedBackgroundColor: UIColor(rgb: 0x3a3b3d),
|
||||
segmentedForegroundColor: UIColor(rgb: 0x6f7075),
|
||||
segmentedTextColor: UIColor(rgb: 0xffffff),
|
||||
segmentedDividerColor: UIColor(rgb: 0x505155)
|
||||
)
|
||||
|
||||
let navigationSearchBar = PresentationThemeNavigationSearchBar(
|
||||
|
||||
@ -61,7 +61,11 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
separatorColor: mainSeparatorColor,
|
||||
badgeBackgroundColor: UIColor(rgb: 0xef5b5b),
|
||||
badgeStrokeColor: UIColor(rgb: 0xef5b5b),
|
||||
badgeTextColor: UIColor(rgb: 0xffffff)
|
||||
badgeTextColor: UIColor(rgb: 0xffffff),
|
||||
segmentedBackgroundColor: mainInputColor,
|
||||
segmentedForegroundColor: mainBackgroundColor,
|
||||
segmentedTextColor: UIColor(rgb: 0xffffff),
|
||||
segmentedDividerColor: mainSecondaryTextColor.withAlphaComponent(0.5)
|
||||
)
|
||||
|
||||
let navigationSearchBar = PresentationThemeNavigationSearchBar(
|
||||
|
||||
@ -68,7 +68,11 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
|
||||
separatorColor: UIColor(rgb: 0xb1b1b1),
|
||||
badgeBackgroundColor: UIColor(rgb: 0xff3b30),
|
||||
badgeStrokeColor: UIColor(rgb: 0xff3b30),
|
||||
badgeTextColor: .white
|
||||
badgeTextColor: .white,
|
||||
segmentedBackgroundColor: UIColor(rgb: 0xe9e9e9),
|
||||
segmentedForegroundColor: UIColor(rgb: 0xf7f7f7),
|
||||
segmentedTextColor: UIColor(rgb: 0x000000),
|
||||
segmentedDividerColor: UIColor(rgb: 0xd6d6dc)
|
||||
)
|
||||
|
||||
let navigationSearchBar = PresentationThemeNavigationSearchBar(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -105,8 +105,12 @@ public final class PresentationThemeRootNavigationBar {
|
||||
public let badgeBackgroundColor: UIColor
|
||||
public let badgeStrokeColor: UIColor
|
||||
public let badgeTextColor: UIColor
|
||||
public let segmentedBackgroundColor: UIColor
|
||||
public let segmentedForegroundColor: UIColor
|
||||
public let segmentedTextColor: UIColor
|
||||
public let segmentedDividerColor: UIColor
|
||||
|
||||
public init(buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, controlColor: UIColor, accentTextColor: UIColor, backgroundColor: UIColor, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor) {
|
||||
public init(buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, controlColor: UIColor, accentTextColor: UIColor, backgroundColor: UIColor, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor, segmentedBackgroundColor: UIColor, segmentedForegroundColor: UIColor, segmentedTextColor: UIColor, segmentedDividerColor: UIColor) {
|
||||
self.buttonColor = buttonColor
|
||||
self.disabledButtonColor = disabledButtonColor
|
||||
self.primaryTextColor = primaryTextColor
|
||||
@ -118,6 +122,10 @@ public final class PresentationThemeRootNavigationBar {
|
||||
self.badgeBackgroundColor = badgeBackgroundColor
|
||||
self.badgeStrokeColor = badgeStrokeColor
|
||||
self.badgeTextColor = badgeTextColor
|
||||
self.segmentedBackgroundColor = segmentedBackgroundColor
|
||||
self.segmentedForegroundColor = segmentedForegroundColor
|
||||
self.segmentedTextColor = segmentedTextColor
|
||||
self.segmentedDividerColor = segmentedDividerColor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -338,6 +338,10 @@ extension PresentationThemeRootNavigationBar: Codable {
|
||||
case badgeFill
|
||||
case badgeStroke
|
||||
case badgeText
|
||||
case segmentedBg
|
||||
case segmentedFg
|
||||
case segmentedText
|
||||
case segmentedDivider
|
||||
}
|
||||
|
||||
public convenience init(from decoder: Decoder) throws {
|
||||
@ -352,7 +356,11 @@ extension PresentationThemeRootNavigationBar: Codable {
|
||||
separatorColor: try decodeColor(values, .separator),
|
||||
badgeBackgroundColor: try decodeColor(values, .badgeFill),
|
||||
badgeStrokeColor: try decodeColor(values, .badgeStroke),
|
||||
badgeTextColor: try decodeColor(values, .badgeText))
|
||||
badgeTextColor: try decodeColor(values, .badgeText),
|
||||
segmentedBackgroundColor: try decodeColor(values, .segmentedBg),
|
||||
segmentedForegroundColor: try decodeColor(values, .segmentedFg),
|
||||
segmentedTextColor: try decodeColor(values, .segmentedText),
|
||||
segmentedDividerColor: try decodeColor(values, .segmentedDivider))
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -368,6 +376,10 @@ extension PresentationThemeRootNavigationBar: Codable {
|
||||
try encodeColor(&values, self.badgeBackgroundColor, .badgeFill)
|
||||
try encodeColor(&values, self.badgeStrokeColor, .badgeStroke)
|
||||
try encodeColor(&values, self.badgeTextColor, .badgeText)
|
||||
try encodeColor(&values, self.segmentedBackgroundColor, .segmentedBg)
|
||||
try encodeColor(&values, self.segmentedForegroundColor, .segmentedFg)
|
||||
try encodeColor(&values, self.segmentedTextColor, .segmentedText)
|
||||
try encodeColor(&values, self.segmentedDividerColor, .segmentedDivider)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,6 +50,7 @@ import AppBundle
|
||||
import WalletUI
|
||||
import WalletUrl
|
||||
import LocalizedPeerData
|
||||
import SettingsUI
|
||||
|
||||
public enum ChatControllerPeekActions {
|
||||
case standard
|
||||
@ -1147,7 +1148,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case let .url(url):
|
||||
var cleanUrl = url
|
||||
var canAddToReadingList = true
|
||||
let canOpenIn = availableOpenInOptions(context: strongSelf.context, item: .url(url: url)).count > 1
|
||||
var canOpenIn = availableOpenInOptions(context: strongSelf.context, item: .url(url: url)).count > 1
|
||||
let mailtoString = "mailto:"
|
||||
let telString = "tel:"
|
||||
var openText = strongSelf.presentationData.strings.Conversation_LinkDialogOpen
|
||||
@ -1160,6 +1161,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
phoneNumber = String(cleanUrl[cleanUrl.index(cleanUrl.startIndex, offsetBy: telString.distance(from: telString.startIndex, to: telString.endIndex))...])
|
||||
cleanUrl = phoneNumber!
|
||||
openText = strongSelf.presentationData.strings.UserInfo_PhoneCall
|
||||
canOpenIn = false
|
||||
} else if canOpenIn {
|
||||
openText = strongSelf.presentationData.strings.Conversation_FileOpenIn
|
||||
}
|
||||
@ -1468,6 +1470,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
}, push: { [weak self] c in
|
||||
if let strongSelf = self {
|
||||
strongSelf.push(c)
|
||||
}
|
||||
})
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
@ -1522,7 +1528,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.messageTooltipController?.dismiss()
|
||||
let tooltipController = TooltipController(content: .text(text), dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true)
|
||||
strongSelf.messageTooltipController = tooltipController
|
||||
tooltipController.dismissed = { [weak tooltipController] in
|
||||
tooltipController.dismissed = { [weak tooltipController] _ in
|
||||
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.messageTooltipController === tooltipController {
|
||||
strongSelf.messageTooltipController = nil
|
||||
}
|
||||
@ -2995,6 +3001,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let strongSelf = self, let messageIds = strongSelf.presentationInterfaceState.interfaceState.selectionState?.selectedIds, !messageIds.isEmpty {
|
||||
strongSelf.present(peerReportOptionsController(context: strongSelf.context, subject: .messages(Array(messageIds).sorted()), present: { c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, push: { c in
|
||||
self?.push(c)
|
||||
}, completion: { _ in }), in: .window(.root))
|
||||
}
|
||||
}, reportMessages: { [weak self] messages, contextController in
|
||||
@ -3536,7 +3544,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let tooltipController = TooltipController(content: .text(banDescription))
|
||||
strongSelf.mediaRestrictedTooltipController = tooltipController
|
||||
strongSelf.mediaRestrictedTooltipControllerMode = isStickers
|
||||
tooltipController.dismissed = { [weak tooltipController] in
|
||||
tooltipController.dismissed = { [weak tooltipController] _ in
|
||||
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.mediaRestrictedTooltipController === tooltipController {
|
||||
strongSelf.mediaRestrictedTooltipController = nil
|
||||
}
|
||||
@ -3571,7 +3579,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.videoUnmuteTooltipController?.dismiss()
|
||||
let tooltipController = TooltipController(content: .iconAndText(icon, strongSelf.presentationInterfaceState.strings.Conversation_PressVolumeButtonForSound), timeout: 3.5, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true)
|
||||
strongSelf.videoUnmuteTooltipController = tooltipController
|
||||
tooltipController.dismissed = { [weak tooltipController] in
|
||||
tooltipController.dismissed = { [weak tooltipController] _ in
|
||||
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.videoUnmuteTooltipController === tooltipController {
|
||||
strongSelf.videoUnmuteTooltipController = nil
|
||||
ApplicationSpecificNotice.setVolumeButtonToUnmute(accountManager: strongSelf.context.sharedContext.accountManager)
|
||||
@ -3826,7 +3834,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
} else if let rect = rect {
|
||||
let tooltipController = TooltipController(content: .text(text))
|
||||
strongSelf.silentPostTooltipController = tooltipController
|
||||
tooltipController.dismissed = { [weak tooltipController] in
|
||||
tooltipController.dismissed = { [weak tooltipController] _ in
|
||||
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.silentPostTooltipController === tooltipController {
|
||||
strongSelf.silentPostTooltipController = nil
|
||||
}
|
||||
@ -4993,19 +5001,211 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch self.chatLocationInfoData {
|
||||
case let .peer(peerView):
|
||||
self.navigationActionDisposable.set((peerView.get()
|
||||
|> take(1)
|
||||
|> 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.effectiveNavigationController?.pushViewController(infoController)
|
||||
}
|
||||
|> take(1)
|
||||
|> 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.effectiveNavigationController?.pushViewController(infoController)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
case .search:
|
||||
self.interfaceInteraction?.beginMessageSearch(.everything, "")
|
||||
case .dismiss:
|
||||
self.dismiss()
|
||||
case .clearCache:
|
||||
let clearDisposable = MetaDisposable()
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
|
||||
|
||||
switch self.chatLocationInfoData {
|
||||
case let .peer(peerView):
|
||||
self.navigationActionDisposable.set((peerView.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView in
|
||||
guard let strongSelf = self, let peer = peerView.peers[peerView.peerId] else {
|
||||
return
|
||||
}
|
||||
let peerId = peer.id
|
||||
|
||||
let cacheUsageStats = (collectCacheUsageStats(account: strongSelf.context.account, peerId: peer.id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let strongSelf = self, case let .result(stats) = result, var categories = stats.media[peer.id] else {
|
||||
return
|
||||
}
|
||||
let presentationData = strongSelf.presentationData
|
||||
let controller = ActionSheetController(presentationTheme: presentationData.theme)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
|
||||
var sizeIndex: [PeerCacheUsageCategory: (Bool, Int64)] = [:]
|
||||
|
||||
var itemIndex = 0
|
||||
|
||||
let updateTotalSize: () -> Void = { [weak controller] in
|
||||
controller?.updateItem(groupIndex: 0, itemIndex: itemIndex, { item in
|
||||
let title: String
|
||||
let filteredSize = sizeIndex.values.reduce(0, { $0 + ($1.0 ? $1.1 : 0) })
|
||||
|
||||
if filteredSize == 0 {
|
||||
title = presentationData.strings.Cache_ClearNone
|
||||
} else {
|
||||
title = presentationData.strings.Cache_Clear("\(dataSizeString(filteredSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))").0
|
||||
}
|
||||
|
||||
if let item = item as? ActionSheetButtonItem {
|
||||
return ActionSheetButtonItem(title: title, color: filteredSize != 0 ? .accent : .disabled, enabled: filteredSize != 0, action: item.action)
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
let toggleCheck: (PeerCacheUsageCategory, Int) -> Void = { [weak controller] category, itemIndex in
|
||||
if let (value, size) = sizeIndex[category] {
|
||||
sizeIndex[category] = (!value, size)
|
||||
}
|
||||
controller?.updateItem(groupIndex: 0, itemIndex: itemIndex, { item in
|
||||
if let item = item as? ActionSheetCheckboxItem {
|
||||
return ActionSheetCheckboxItem(title: item.title, label: item.label, value: !item.value, action: item.action)
|
||||
}
|
||||
return item
|
||||
})
|
||||
updateTotalSize()
|
||||
}
|
||||
var items: [ActionSheetItem] = []
|
||||
|
||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: peer, chatPeer: peer, action: .clearCache, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder))
|
||||
|
||||
let validCategories: [PeerCacheUsageCategory] = [.image, .video, .audio, .file]
|
||||
|
||||
var totalSize: Int64 = 0
|
||||
|
||||
func stringForCategory(strings: PresentationStrings, category: PeerCacheUsageCategory) -> String {
|
||||
switch category {
|
||||
case .image:
|
||||
return strings.Cache_Photos
|
||||
case .video:
|
||||
return strings.Cache_Videos
|
||||
case .audio:
|
||||
return strings.Cache_Music
|
||||
case .file:
|
||||
return strings.Cache_Files
|
||||
}
|
||||
}
|
||||
|
||||
for categoryId in validCategories {
|
||||
if let media = categories[categoryId] {
|
||||
var categorySize: Int64 = 0
|
||||
for (_, size) in media {
|
||||
categorySize += size
|
||||
}
|
||||
sizeIndex[categoryId] = (true, categorySize)
|
||||
totalSize += categorySize
|
||||
if categorySize > 1024 {
|
||||
let index = itemIndex
|
||||
items.append(ActionSheetCheckboxItem(title: stringForCategory(strings: presentationData.strings, category: categoryId), label: dataSizeString(categorySize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator), value: true, action: { value in
|
||||
toggleCheck(categoryId, index)
|
||||
}))
|
||||
itemIndex += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if items.isEmpty {
|
||||
strongSelf.presentClearCacheSuggestion()
|
||||
} else {
|
||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))").0, action: {
|
||||
let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 })
|
||||
var clearMediaIds = Set<MediaId>()
|
||||
|
||||
var media = stats.media
|
||||
if var categories = media[peerId] {
|
||||
for category in clearCategories {
|
||||
if let contents = categories[category] {
|
||||
for (mediaId, _) in contents {
|
||||
clearMediaIds.insert(mediaId)
|
||||
}
|
||||
}
|
||||
categories.removeValue(forKey: category)
|
||||
}
|
||||
|
||||
media[peerId] = categories
|
||||
}
|
||||
// if let additionalPeerId = additionalPeerId {
|
||||
// if var categories = media[additionalPeerId] {
|
||||
// for category in clearCategories {
|
||||
// if let contents = categories[category] {
|
||||
// for (mediaId, _) in contents {
|
||||
// clearMediaIds.insert(mediaId)
|
||||
// }
|
||||
// }
|
||||
// categories.removeValue(forKey: category)
|
||||
// }
|
||||
//
|
||||
// media[additionalPeerId] = categories
|
||||
// }
|
||||
// }
|
||||
|
||||
var clearResourceIds = Set<WrappedMediaResourceId>()
|
||||
for id in clearMediaIds {
|
||||
if let ids = stats.mediaResourceIds[id] {
|
||||
for resourceId in ids {
|
||||
clearResourceIds.insert(WrappedMediaResourceId(resourceId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var signal = clearCachedMediaResources(account: strongSelf.context.account, mediaResourceIds: clearResourceIds)
|
||||
|
||||
let resultStats = CacheUsageStats(media: media, mediaResourceIds: stats.mediaResourceIds, peers: stats.peers, otherSize: stats.otherSize, otherPaths: stats.otherPaths, cacheSize: stats.cacheSize, tempPaths: stats.tempPaths, tempSize: stats.tempSize, immutableSize: stats.immutableSize)
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
return ActionDisposable { [weak controller] in
|
||||
Queue.mainQueue().async() {
|
||||
controller?.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
|> delay(0.15, queue: Queue.mainQueue())
|
||||
let progressDisposable = progressSignal.start()
|
||||
|
||||
signal = signal
|
||||
|> afterDisposed {
|
||||
Queue.mainQueue().async {
|
||||
progressDisposable.dispose()
|
||||
}
|
||||
}
|
||||
cancelImpl = {
|
||||
clearDisposable.set(nil)
|
||||
}
|
||||
clearDisposable.set((signal
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
|
||||
|
||||
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(totalSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))").0), elevatedLayout: false, action: { _ in }), in: .window(.root))
|
||||
}))
|
||||
|
||||
dismissAction()
|
||||
}))
|
||||
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: items),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7444,6 +7644,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
private func presentClearCacheSuggestion() {
|
||||
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
||||
return
|
||||
}
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
|
||||
|
||||
let actionSheet = ActionSheetController(presentationTheme: self.presentationData.theme)
|
||||
var items: [ActionSheetItem] = []
|
||||
|
||||
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: peer, chatPeer: peer, action: .clearCacheSuggestion, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
|
||||
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ClearCache_FreeSpace, color: .accent, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
let controller = storageUsageController(context: strongSelf.context, isModal: true)
|
||||
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}))
|
||||
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
})
|
||||
])])
|
||||
self.chatDisplayNode.dismissInput()
|
||||
self.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 11.0, iOS 11.0, *)
|
||||
public func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool {
|
||||
return session.hasItemsConforming(toTypeIdentifiers: [kUTTypeImage as String])
|
||||
@ -7507,7 +7736,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
} else if let rect = rect {
|
||||
let tooltipController = TooltipController(content: .text(text))
|
||||
self.mediaRecordingModeTooltipController = tooltipController
|
||||
tooltipController.dismissed = { [weak self, weak tooltipController] in
|
||||
tooltipController.dismissed = { [weak self, weak tooltipController] _ in
|
||||
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.mediaRecordingModeTooltipController === tooltipController {
|
||||
strongSelf.mediaRecordingModeTooltipController = nil
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import AccountContext
|
||||
enum ChatNavigationButtonAction {
|
||||
case openChatInfo
|
||||
case clearHistory
|
||||
case clearCache
|
||||
case cancelMessageSelection
|
||||
case search
|
||||
case dismiss
|
||||
@ -44,6 +45,9 @@ func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Cha
|
||||
|
||||
if canClear {
|
||||
return ChatNavigationButton(action: .clearHistory, buttonItem: UIBarButtonItem(title: title, style: .plain, target: target, action: selector))
|
||||
} else {
|
||||
title = strings.Conversation_ClearCache
|
||||
return ChatNavigationButton(action: .clearCache, buttonItem: UIBarButtonItem(title: title, style: .plain, target: target, action: selector))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +148,9 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
self.deleteButton.isHidden = !self.deleteButton.isEnabled
|
||||
self.reportButton.isHidden = !self.reportButton.isEnabled
|
||||
|
||||
self.deleteButton.isHidden = false
|
||||
self.deleteButton.isEnabled = true
|
||||
} else {
|
||||
self.deleteButton.isEnabled = false
|
||||
self.deleteButton.isHidden = true
|
||||
@ -155,6 +158,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
||||
self.reportButton.isHidden = true
|
||||
self.forwardButton.isEnabled = false
|
||||
self.shareButton.isEnabled = false
|
||||
|
||||
self.deleteButton.isHidden = false
|
||||
}
|
||||
|
||||
if self.deleteButton.isHidden && self.reportButton.isHidden {
|
||||
|
||||
@ -1575,7 +1575,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||
if let inputText = current.inputText.mutableCopy() as? NSMutableAttributedString {
|
||||
inputText.replaceCharacters(in: NSMakeRange(current.selectionRange.lowerBound, current.selectionRange.count), with: attributedString)
|
||||
return (ChatTextInputState(inputText: inputText), inputMode)
|
||||
let updatedRange = current.selectionRange.lowerBound + attributedString.length
|
||||
return (ChatTextInputState(inputText: inputText, selectionRange: updatedRange ..< updatedRange), inputMode)
|
||||
} else {
|
||||
return (ChatTextInputState(inputText: attributedString), inputMode)
|
||||
}
|
||||
|
||||
@ -314,6 +314,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
||||
var replaceControllerImpl: ((ViewController) -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var pushImpl: ((ViewController) -> Void)?
|
||||
var endEditingImpl: (() -> Void)?
|
||||
var clearHighlightImpl: (() -> Void)?
|
||||
|
||||
@ -583,7 +584,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
||||
}, sendLiveLocation: { _, _ in }, theme: presentationData.theme, customLocationPicker: true, presentationCompleted: {
|
||||
clearHighlightImpl?()
|
||||
})
|
||||
presentControllerImpl?(controller, nil)
|
||||
pushImpl?(controller)
|
||||
})
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), context.account.postbox.multiplePeersView(peerIds), .single(nil) |> then(addressPromise.get()))
|
||||
@ -619,6 +620,9 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
||||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
pushImpl = { [weak controller] c in
|
||||
controller?.push(c)
|
||||
}
|
||||
controller.willDisappear = { _ in
|
||||
endEditingImpl?()
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ import PeerInfoUI
|
||||
import SettingsUI
|
||||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import ShareController
|
||||
|
||||
private enum ChatMessageGalleryControllerData {
|
||||
case url(String)
|
||||
@ -26,7 +27,7 @@ private enum ChatMessageGalleryControllerData {
|
||||
case map(TelegramMediaMap)
|
||||
case stickerPack(StickerPackReference)
|
||||
case audio(TelegramMediaFile)
|
||||
case document(TelegramMediaFile)
|
||||
case document(TelegramMediaFile, Bool)
|
||||
case gallery(GalleryController)
|
||||
case secretGallery(SecretMediaPreviewController)
|
||||
case chatAvatars(AvatarGalleryController, Media)
|
||||
@ -152,14 +153,10 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
return .gallery(gallery)
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
|
||||
if ext == "mkv" {
|
||||
let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
|
||||
navigationController?.replaceTopController(controller, animated: false, ready: ready)
|
||||
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)
|
||||
return .gallery(gallery)
|
||||
return .document(file, true)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if internalDocumentItemSupportsMimeType(file.mimeType, fileName: file.fileName ?? "file") {
|
||||
@ -170,7 +167,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
||||
}
|
||||
|
||||
if !file.isVideo {
|
||||
return .document(file)
|
||||
return .document(file, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,9 +268,12 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||
params.dismissInput()
|
||||
params.present(controller, nil)
|
||||
return true
|
||||
case let .document(file):
|
||||
case let .document(file, immediateShare):
|
||||
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
|
||||
if let rootController = params.navigationController?.view.window?.rootViewController {
|
||||
if immediateShare {
|
||||
let controller = ShareController(context: params.context, subject: .media(.standalone(media: file)), immediateExternalShare: true)
|
||||
params.present(controller, nil)
|
||||
} else if let rootController = params.navigationController?.view.window?.rootViewController {
|
||||
presentDocumentPreviewController(rootController: rootController, theme: presentationData.theme, strings: presentationData.strings, postbox: params.context.account.postbox, file: file)
|
||||
}
|
||||
return true
|
||||
|
||||
@ -422,6 +422,8 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
||||
if let strongSelf = self, let messageIds = strongSelf.interfaceState.selectionState?.selectedIds, !messageIds.isEmpty {
|
||||
strongSelf.present(peerReportOptionsController(context: strongSelf.context, subject: .messages(Array(messageIds).sorted()), present: { c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, push: { c in
|
||||
self?.push(c)
|
||||
}, completion: { _ in }), in: .window(.root))
|
||||
}
|
||||
}, reportMessages: { _, _ in
|
||||
|
||||
Binary file not shown.
@ -140,60 +140,63 @@ final class WalletContextImpl: WalletContext {
|
||||
buttonTextColor: .white,
|
||||
incomingFundsTitleColor: theme.chatList.secretTitleColor,
|
||||
outgoingFundsTitleColor: theme.list.itemDestructiveColor
|
||||
), setup: WalletSetupTheme(
|
||||
buttonFillColor: theme.list.itemCheckColors.fillColor,
|
||||
buttonForegroundColor: theme.list.itemCheckColors.foregroundColor,
|
||||
inputBackgroundColor: theme.actionSheet.inputBackgroundColor,
|
||||
inputPlaceholderColor: theme.actionSheet.inputPlaceholderColor,
|
||||
inputTextColor: theme.actionSheet.inputTextColor,
|
||||
inputClearButtonColor: theme.actionSheet.inputClearButtonColor.withAlphaComponent(0.8)
|
||||
),
|
||||
list: WalletListTheme(
|
||||
itemPrimaryTextColor: theme.list.itemPrimaryTextColor,
|
||||
itemSecondaryTextColor: theme.list.itemSecondaryTextColor,
|
||||
itemPlaceholderTextColor: theme.list.itemPlaceholderTextColor,
|
||||
itemDestructiveColor: theme.list.itemDestructiveColor,
|
||||
itemAccentColor: theme.list.itemAccentColor,
|
||||
itemDisabledTextColor: theme.list.itemDisabledTextColor,
|
||||
plainBackgroundColor: theme.list.plainBackgroundColor,
|
||||
blocksBackgroundColor: theme.list.blocksBackgroundColor,
|
||||
itemPlainSeparatorColor: theme.list.itemPlainSeparatorColor,
|
||||
itemBlocksBackgroundColor: theme.list.itemBlocksBackgroundColor,
|
||||
itemBlocksSeparatorColor: theme.list.itemBlocksSeparatorColor,
|
||||
itemHighlightedBackgroundColor: theme.list.itemHighlightedBackgroundColor,
|
||||
sectionHeaderTextColor: theme.list.sectionHeaderTextColor,
|
||||
freeTextColor: theme.list.freeTextColor,
|
||||
freeTextErrorColor: theme.list.freeTextErrorColor,
|
||||
inputClearButtonColor: theme.list.inputClearButtonColor
|
||||
),
|
||||
statusBarStyle: theme.rootController.statusBarStyle.style,
|
||||
navigationBar: navigationBarData.theme,
|
||||
keyboardAppearance: theme.rootController.keyboardColor.keyboardAppearance,
|
||||
alert: AlertControllerTheme(presentationTheme: theme),
|
||||
actionSheet: ActionSheetControllerTheme(presentationTheme: theme)
|
||||
), strings: WalletStrings(
|
||||
primaryComponent: WalletStringsComponent(
|
||||
languageCode: strings.primaryComponent.languageCode,
|
||||
localizedName: strings.primaryComponent.localizedName,
|
||||
pluralizationRulesCode: strings.primaryComponent.pluralizationRulesCode,
|
||||
dict: strings.primaryComponent.dict
|
||||
),
|
||||
secondaryComponent: strings.secondaryComponent.flatMap { component in
|
||||
return WalletStringsComponent(
|
||||
languageCode: component.languageCode,
|
||||
localizedName: component.localizedName,
|
||||
pluralizationRulesCode: component.pluralizationRulesCode,
|
||||
dict: component.dict
|
||||
)
|
||||
},
|
||||
groupingSeparator: strings.groupingSeparator
|
||||
), dateTimeFormat: WalletPresentationDateTimeFormat(
|
||||
timeFormat: timeFormat,
|
||||
dateFormat: dateFormat,
|
||||
dateSeparator: presentationData.dateTimeFormat.dateSeparator,
|
||||
decimalSeparator: presentationData.dateTimeFormat.decimalSeparator,
|
||||
groupingSeparator: presentationData.dateTimeFormat.groupingSeparator
|
||||
)
|
||||
), transaction: WalletTransactionTheme(
|
||||
descriptionBackgroundColor: theme.chat.message.incoming.bubble.withoutWallpaper.fill,
|
||||
descriptionTextColor: theme.chat.message.incoming.primaryTextColor
|
||||
), setup: WalletSetupTheme(
|
||||
buttonFillColor: theme.list.itemCheckColors.fillColor,
|
||||
buttonForegroundColor: theme.list.itemCheckColors.foregroundColor,
|
||||
inputBackgroundColor: theme.actionSheet.inputBackgroundColor,
|
||||
inputPlaceholderColor: theme.actionSheet.inputPlaceholderColor,
|
||||
inputTextColor: theme.actionSheet.inputTextColor,
|
||||
inputClearButtonColor: theme.actionSheet.inputClearButtonColor.withAlphaComponent(0.8)
|
||||
),
|
||||
list: WalletListTheme(
|
||||
itemPrimaryTextColor: theme.list.itemPrimaryTextColor,
|
||||
itemSecondaryTextColor: theme.list.itemSecondaryTextColor,
|
||||
itemPlaceholderTextColor: theme.list.itemPlaceholderTextColor,
|
||||
itemDestructiveColor: theme.list.itemDestructiveColor,
|
||||
itemAccentColor: theme.list.itemAccentColor,
|
||||
itemDisabledTextColor: theme.list.itemDisabledTextColor,
|
||||
plainBackgroundColor: theme.list.plainBackgroundColor,
|
||||
blocksBackgroundColor: theme.list.blocksBackgroundColor,
|
||||
itemPlainSeparatorColor: theme.list.itemPlainSeparatorColor,
|
||||
itemBlocksBackgroundColor: theme.list.itemBlocksBackgroundColor,
|
||||
itemBlocksSeparatorColor: theme.list.itemBlocksSeparatorColor,
|
||||
itemHighlightedBackgroundColor: theme.list.itemHighlightedBackgroundColor,
|
||||
sectionHeaderTextColor: theme.list.sectionHeaderTextColor,
|
||||
freeTextColor: theme.list.freeTextColor,
|
||||
freeTextErrorColor: theme.list.freeTextErrorColor,
|
||||
inputClearButtonColor: theme.list.inputClearButtonColor
|
||||
),
|
||||
statusBarStyle: theme.rootController.statusBarStyle.style,
|
||||
navigationBar: navigationBarData.theme,
|
||||
keyboardAppearance: theme.rootController.keyboardColor.keyboardAppearance,
|
||||
alert: AlertControllerTheme(presentationTheme: theme),
|
||||
actionSheet: ActionSheetControllerTheme(presentationTheme: theme)
|
||||
), strings: WalletStrings(
|
||||
primaryComponent: WalletStringsComponent(
|
||||
languageCode: strings.primaryComponent.languageCode,
|
||||
localizedName: strings.primaryComponent.localizedName,
|
||||
pluralizationRulesCode: strings.primaryComponent.pluralizationRulesCode,
|
||||
dict: strings.primaryComponent.dict
|
||||
),
|
||||
secondaryComponent: strings.secondaryComponent.flatMap { component in
|
||||
return WalletStringsComponent(
|
||||
languageCode: component.languageCode,
|
||||
localizedName: component.localizedName,
|
||||
pluralizationRulesCode: component.pluralizationRulesCode,
|
||||
dict: component.dict
|
||||
)
|
||||
},
|
||||
groupingSeparator: strings.groupingSeparator
|
||||
), dateTimeFormat: WalletPresentationDateTimeFormat(
|
||||
timeFormat: timeFormat,
|
||||
dateFormat: dateFormat,
|
||||
dateSeparator: presentationData.dateTimeFormat.dateSeparator,
|
||||
decimalSeparator: presentationData.dateTimeFormat.decimalSeparator,
|
||||
groupingSeparator: presentationData.dateTimeFormat.groupingSeparator
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -184,7 +184,7 @@ protocol WalletCreateInvoiceScreen {
|
||||
|
||||
private final class WalletCreateInvoiceScreenImpl: ItemListController, WalletCreateInvoiceScreen {
|
||||
override func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
|
||||
return CGSize(width: layout.size.width, height: layout.size.height - 174.0)
|
||||
return CGSize(width: layout.size.width, height: min(640.0, layout.size.height))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -113,6 +113,7 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
private let iconNode: ASImageNode
|
||||
private let textNode: TextNode
|
||||
private let descriptionNode: TextNode
|
||||
private let feesNode: TextNode
|
||||
private let dateNode: TextNode
|
||||
private var statusNode: StatusClockNode?
|
||||
|
||||
@ -157,6 +158,11 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
self.descriptionNode.contentMode = .left
|
||||
self.descriptionNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.feesNode = TextNode()
|
||||
self.feesNode.isUserInteractionEnabled = false
|
||||
self.feesNode.contentMode = .left
|
||||
self.feesNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.dateNode = TextNode()
|
||||
self.dateNode.isUserInteractionEnabled = false
|
||||
self.dateNode.contentMode = .left
|
||||
@ -175,6 +181,7 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
self.addSubnode(self.directionNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.descriptionNode)
|
||||
self.addSubnode(self.feesNode)
|
||||
self.addSubnode(self.dateNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
@ -186,6 +193,7 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
let makeDirectionLayout = TextNode.asyncLayout(self.directionNode)
|
||||
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||
let makeDescriptionLayout = TextNode.asyncLayout(self.descriptionNode)
|
||||
let makeFeesLayout = TextNode.asyncLayout(self.feesNode)
|
||||
let makeDateLayout = TextNode.asyncLayout(self.dateNode)
|
||||
|
||||
let currentItem = self.item
|
||||
@ -269,18 +277,15 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
var feeText: String = ""
|
||||
let dateText: String
|
||||
switch item.walletTransaction {
|
||||
case let .completed(transaction):
|
||||
let fee = transaction.storageFee + transaction.otherFee
|
||||
if fee != 0 {
|
||||
let feeText = item.strings.Wallet_Info_TransactionBlockchainFee(formatBalanceText(-fee, decimalSeparator: item.dateTimeFormat.decimalSeparator)).0
|
||||
if !description.isEmpty {
|
||||
description.append("\n")
|
||||
}
|
||||
description += "\(feeText)"
|
||||
feeText = item.strings.Wallet_Info_TransactionBlockchainFee(formatBalanceText(-fee, decimalSeparator: item.dateTimeFormat.decimalSeparator)).0
|
||||
}
|
||||
dateText = stringForMessageTimestamp(timestamp: Int32(clamping: transaction.timestamp), dateTimeFormat: item.dateTimeFormat)
|
||||
dateText = stringForMessageTimestamp(timestamp: Int32(clamping: transaction.timestamp), dateTimeFormat: item.dateTimeFormat)
|
||||
case let .pending(transaction):
|
||||
dateText = stringForMessageTimestamp(timestamp: Int32(clamping: transaction.timestamp), dateTimeFormat: item.dateTimeFormat)
|
||||
}
|
||||
@ -307,7 +312,9 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: textFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (descriptionLayout, descriptionApply) = makeDescriptionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: description, font: descriptionFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (descriptionLayout, descriptionApply) = makeDescriptionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: description, font: descriptionFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (feesLayout, feesApply) = makeFeesLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: feeText, font: descriptionFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
var contentSize: CGSize
|
||||
var insets: UIEdgeInsets
|
||||
@ -328,6 +335,9 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
if !descriptionLayout.size.width.isZero {
|
||||
contentSize.height += descriptionLayout.size.height + textSpacing
|
||||
}
|
||||
if !feesLayout.size.width.isZero {
|
||||
contentSize.height += feesLayout.size.height + textSpacing
|
||||
}
|
||||
insets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
var topHighlightInset: CGFloat = 0.0
|
||||
if dateHeaderAtBottom, let header = item.header {
|
||||
@ -356,6 +366,7 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
let _ = titleApply()
|
||||
let _ = textApply()
|
||||
let _ = descriptionApply()
|
||||
let _ = feesApply()
|
||||
let _ = dateApply()
|
||||
let _ = directionApply()
|
||||
|
||||
@ -385,7 +396,9 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
let textFrame = CGRect(origin: CGPoint(x: leftInset, y: titleFrame.maxY + titleSpacing), size: textLayout.size)
|
||||
strongSelf.textNode.frame = textFrame
|
||||
|
||||
strongSelf.descriptionNode.frame = CGRect(origin: CGPoint(x: leftInset, y: textFrame.maxY + textSpacing), size: descriptionLayout.size)
|
||||
let descriptionFrame = CGRect(origin: CGPoint(x: leftInset, y: textFrame.maxY + textSpacing), size: descriptionLayout.size)
|
||||
strongSelf.descriptionNode.frame = descriptionFrame
|
||||
strongSelf.feesNode.frame = CGRect(origin: CGPoint(x: leftInset, y: descriptionFrame.maxY + textSpacing), size: feesLayout.size)
|
||||
|
||||
let dateFrame = CGRect(origin: CGPoint(x: params.width - leftInset - dateLayout.size.width, y: topInset), size: dateLayout.size)
|
||||
strongSelf.dateNode.frame = dateFrame
|
||||
|
||||
@ -49,6 +49,19 @@ public final class WalletInfoTheme {
|
||||
}
|
||||
}
|
||||
|
||||
public final class WalletTransactionTheme {
|
||||
public let descriptionBackgroundColor: UIColor
|
||||
public let descriptionTextColor: UIColor
|
||||
|
||||
public init(
|
||||
descriptionBackgroundColor: UIColor,
|
||||
descriptionTextColor: UIColor
|
||||
) {
|
||||
self.descriptionBackgroundColor = descriptionBackgroundColor
|
||||
self.descriptionTextColor = descriptionTextColor
|
||||
}
|
||||
}
|
||||
|
||||
public final class WalletSetupTheme {
|
||||
public let buttonFillColor: UIColor
|
||||
public let buttonForegroundColor: UIColor
|
||||
@ -131,6 +144,7 @@ public final class WalletListTheme {
|
||||
|
||||
public final class WalletTheme: Equatable {
|
||||
public let info: WalletInfoTheme
|
||||
public let transaction: WalletTransactionTheme
|
||||
public let setup: WalletSetupTheme
|
||||
public let list: WalletListTheme
|
||||
public let statusBarStyle: StatusBarStyle
|
||||
@ -141,8 +155,9 @@ public final class WalletTheme: Equatable {
|
||||
|
||||
private let resourceCache = WalletThemeResourceCache()
|
||||
|
||||
public init(info: WalletInfoTheme, setup: WalletSetupTheme, list: WalletListTheme, statusBarStyle: StatusBarStyle, navigationBar: NavigationBarTheme, keyboardAppearance: UIKeyboardAppearance, alert: AlertControllerTheme, actionSheet: ActionSheetControllerTheme) {
|
||||
public init(info: WalletInfoTheme, transaction: WalletTransactionTheme, setup: WalletSetupTheme, list: WalletListTheme, statusBarStyle: StatusBarStyle, navigationBar: NavigationBarTheme, keyboardAppearance: UIKeyboardAppearance, alert: AlertControllerTheme, actionSheet: ActionSheetControllerTheme) {
|
||||
self.info = info
|
||||
self.transaction = transaction
|
||||
self.setup = setup
|
||||
self.list = list
|
||||
self.statusBarStyle = statusBarStyle
|
||||
|
||||
@ -119,7 +119,7 @@ public final class WalletQrScanScreen: ViewController {
|
||||
return
|
||||
}
|
||||
strongSelf.context.pickImage(completion: { image in
|
||||
let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])!
|
||||
let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])!
|
||||
if let ciImage = CIImage(image: image) {
|
||||
var options: [String: Any]
|
||||
if ciImage.properties.keys.contains((kCGImagePropertyOrientation as String)) {
|
||||
|
||||
@ -89,6 +89,21 @@ final class WalletReceiveScreen: ViewController {
|
||||
}
|
||||
self?.push(walletCreateInvoiceScreen(context: strongSelf.context, address: strongSelf.mode.address))
|
||||
}
|
||||
(self.displayNode as! WalletReceiveScreenNode).displayCopyContextMenu = { [weak self] node, frame, text in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: strongSelf.presentationData.strings.Wallet_ContextMenuCopy, accessibilityLabel: strongSelf.presentationData.strings.Wallet_ContextMenuCopy), action: {
|
||||
UIPasteboard.general.string = text
|
||||
})])
|
||||
strongSelf.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
return (node, frame.insetBy(dx: 0.0, dy: -2.0), strongSelf.displayNode, strongSelf.displayNode.view.bounds)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}))
|
||||
}
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
@ -120,7 +135,7 @@ final class WalletReceiveScreen: ViewController {
|
||||
}
|
||||
|
||||
override func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
|
||||
return CGSize(width: layout.size.width, height: layout.size.height - 174.0)
|
||||
return CGSize(width: layout.size.width, height: min(640.0, layout.size.height))
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
@ -172,6 +187,7 @@ private final class WalletReceiveScreenNode: ViewControllerTracingNode {
|
||||
private let secondaryButtonNode: HighlightableButtonNode
|
||||
|
||||
var openCreateInvoice: (() -> Void)?
|
||||
var displayCopyContextMenu: ((ASDisplayNode, CGRect, String) -> Void)?
|
||||
|
||||
init(context: WalletContext, presentationData: WalletPresentationData, mode: WalletReceiveScreenMode) {
|
||||
self.context = context
|
||||
@ -234,21 +250,16 @@ private final class WalletReceiveScreenNode: ViewControllerTracingNode {
|
||||
}
|
||||
|
||||
let textFont = Font.regular(16.0)
|
||||
let addressFont = Font.monospace(17.0)
|
||||
let textColor = self.presentationData.theme.list.itemPrimaryTextColor
|
||||
let secondaryTextColor = self.presentationData.theme.list.itemSecondaryTextColor
|
||||
let url = urlForMode(self.mode)
|
||||
switch self.mode {
|
||||
case let .receive(address):
|
||||
self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Wallet_Receive_ShareUrlInfo, font: textFont, textColor: secondaryTextColor)
|
||||
self.urlTextNode.attributedText = NSAttributedString(string: formatAddress(url + " "), font: addressFont, textColor: textColor, paragraphAlignment: .justified)
|
||||
self.buttonNode.title = self.presentationData.strings.Wallet_Receive_ShareAddress
|
||||
self.secondaryButtonNode.setTitle(self.presentationData.strings.Wallet_Receive_CreateInvoice, with: Font.regular(17.0), with: self.presentationData.theme.list.itemAccentColor, for: .normal)
|
||||
case let .invoice(address, amount, comment):
|
||||
self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Wallet_Receive_ShareUrlInfo, font: textFont, textColor: secondaryTextColor, paragraphAlignment: .center)
|
||||
|
||||
let sliced = String(url.enumerated().map { $0 > 0 && $0 % 32 == 0 ? ["\n", $1] : [$1]}.joined())
|
||||
self.urlTextNode.attributedText = NSAttributedString(string: sliced, font: addressFont, textColor: textColor, paragraphAlignment: .justified)
|
||||
self.buttonNode.title = self.presentationData.strings.Wallet_Receive_ShareInvoiceUrl
|
||||
}
|
||||
|
||||
@ -258,6 +269,32 @@ private final class WalletReceiveScreenNode: ViewControllerTracingNode {
|
||||
self.secondaryButtonNode.addTarget(self, action: #selector(createInvoicePressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let addressGestureRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapAddressGesture(_:)))
|
||||
addressGestureRecognizer.tapActionAtPoint = { [weak self] point in
|
||||
return .waitForSingleTap
|
||||
}
|
||||
self.urlTextNode.view.addGestureRecognizer(addressGestureRecognizer)
|
||||
}
|
||||
|
||||
@objc func tapLongTapOrDoubleTapAddressGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
case .longTap:
|
||||
self.displayCopyContextMenu?(self, self.urlTextNode.frame, urlForMode(self.mode))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func qrPressed() {
|
||||
shareInvoiceQrCode(context: self.context, invoice: urlForMode(self.mode))
|
||||
}
|
||||
@ -293,6 +330,18 @@ private final class WalletReceiveScreenNode: ViewControllerTracingNode {
|
||||
transition.updateBounds(node: self.qrIconNode, bounds: CGRect(origin: CGPoint(), size: iconSize))
|
||||
transition.updatePosition(node: self.qrIconNode, position: imageFrame.center.offsetBy(dx: 0.0, dy: -1.0))
|
||||
|
||||
if self.urlTextNode.attributedText?.string.isEmpty ?? true {
|
||||
var url = urlForMode(self.mode)
|
||||
if case .receive = self.mode {
|
||||
url = url + "?"
|
||||
}
|
||||
let count = min(url.count / 2, Int(ceil(min(layout.size.width, layout.size.height) * 0.0853)))
|
||||
let sliced = String(url.enumerated().map { $0 > 0 && $0 % count == 0 ? ["\n", $1] : [$1]}.joined())
|
||||
|
||||
let addressFont = Font.monospace(17.0)
|
||||
self.urlTextNode.attributedText = NSAttributedString(string: sliced, font: addressFont, textColor: self.presentationData.theme.list.itemPrimaryTextColor, paragraphAlignment: .justified)
|
||||
}
|
||||
|
||||
let urlTextSize = self.urlTextNode.updateLayout(CGSize(width: layout.size.width - inset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
transition.updateFrame(node: self.urlTextNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - urlTextSize.width) / 2.0), y: imageFrame.maxY + 25.0), size: urlTextSize))
|
||||
|
||||
|
||||
@ -79,7 +79,6 @@ private func walletSettingsControllerEntries(presentationData: WalletPresentatio
|
||||
entries.append(.deleteWallet(presentationData.theme, presentationData.strings.Wallet_Settings_DeleteWallet))
|
||||
entries.append(.deleteWalletInfo(presentationData.theme, presentationData.strings.Wallet_Settings_DeleteWalletInfo))
|
||||
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
|
||||
@ -202,7 +202,12 @@ final class WalletTransactionInfoScreen: ViewController {
|
||||
}
|
||||
var string = NSMutableAttributedString(string: "Blockchain validators collect a tiny fee for storing information about your decentralized wallet and for processing your transactions. More info", font: Font.regular(14.0), textColor: .white, paragraphAlignment: .center)
|
||||
string.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor(rgb: 0x6bb2ff), range: NSMakeRange(string.string.count - 10, 10))
|
||||
let controller = TooltipController(content: .attributedText(string), timeout: 3.0, dismissByTapOutside: true, dismissByTapOutsideSource: false, dismissImmediatelyOnLayoutUpdate: false)
|
||||
let controller = TooltipController(content: .attributedText(string), timeout: 3.0, dismissByTapOutside: true, dismissByTapOutsideSource: false, dismissImmediatelyOnLayoutUpdate: false, arrowOnBottom: false)
|
||||
controller.dismissed = { [weak self] tappedInside in
|
||||
if let strongSelf = self, tappedInside {
|
||||
strongSelf.context.openUrl(strongSelf.presentationData.strings.Wallet_TransactionInfo_FeeInfoURL)
|
||||
}
|
||||
}
|
||||
strongSelf.present(controller, in: .window(.root), with: TooltipControllerPresentationArguments(sourceViewAndRect: {
|
||||
if let strongSelf = self {
|
||||
return (node.view, rect.insetBy(dx: 0.0, dy: -4.0))
|
||||
@ -210,6 +215,21 @@ final class WalletTransactionInfoScreen: ViewController {
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
(self.displayNode as! WalletTransactionInfoScreenNode).displayCopyContextMenu = { [weak self] node, frame, text in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: strongSelf.presentationData.strings.Wallet_ContextMenuCopy, accessibilityLabel: strongSelf.presentationData.strings.Wallet_ContextMenuCopy), action: {
|
||||
UIPasteboard.general.string = text
|
||||
})])
|
||||
strongSelf.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
return (node, frame.insetBy(dx: 0.0, dy: -2.0), strongSelf.displayNode, strongSelf.displayNode.view.bounds)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}))
|
||||
}
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
@ -223,7 +243,8 @@ final class WalletTransactionInfoScreen: ViewController {
|
||||
textHeight += 24.0
|
||||
}
|
||||
let insets = layout.insets(options: [])
|
||||
return CGSize(width: layout.size.width, height: 428.0 + insets.bottom + textHeight)
|
||||
|
||||
return CGSize(width: layout.size.width, height: min(min(720.0, layout.size.height), 428.0 + insets.bottom + textHeight))
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
@ -240,7 +261,7 @@ final class WalletTransactionInfoScreen: ViewController {
|
||||
private let integralFont = Font.medium(48.0)
|
||||
private let fractionalFont = Font.medium(24.0)
|
||||
|
||||
private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||
private let context: WalletContext
|
||||
private var presentationData: WalletPresentationData
|
||||
private let walletTransaction: WalletInfoTransaction
|
||||
@ -249,14 +270,20 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let timeNode: ImmediateTextNode
|
||||
|
||||
private let navigationBackgroundNode: ASDisplayNode
|
||||
private let navigationSeparatorNode: ASDisplayNode
|
||||
|
||||
private let scrollNode: ASScrollNode
|
||||
private let amountNode: ImmediateTextNode
|
||||
private let iconNode: AnimatedStickerNode
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
private let feesNode: ImmediateTextNode
|
||||
private let feesInfoIconNode: ASImageNode
|
||||
private let feesButtonNode: ASButtonNode
|
||||
|
||||
private let commentBackgroundNode: ASImageNode
|
||||
private let commentTextNode: ImmediateTextNode
|
||||
private let commentSeparatorNode: ASDisplayNode
|
||||
|
||||
private let addressTextNode: ImmediateTextNode
|
||||
|
||||
@ -264,6 +291,7 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
var send: ((String) -> Void)?
|
||||
var displayFeesTooltip: ((ASDisplayNode, CGRect) -> Void)?
|
||||
var displayCopyContextMenu: ((ASDisplayNode, CGRect, String) -> Void)?
|
||||
|
||||
init(context: WalletContext, presentationData: WalletPresentationData, walletTransaction: WalletInfoTransaction) {
|
||||
self.context = context
|
||||
@ -278,6 +306,14 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
self.timeNode.textAlignment = .center
|
||||
self.timeNode.maximumNumberOfLines = 1
|
||||
|
||||
self.navigationBackgroundNode = ASDisplayNode()
|
||||
self.navigationBackgroundNode.backgroundColor = self.presentationData.theme.navigationBar.backgroundColor
|
||||
self.navigationBackgroundNode.alpha = 0.0
|
||||
self.navigationSeparatorNode = ASDisplayNode()
|
||||
self.navigationSeparatorNode.backgroundColor = self.presentationData.theme.navigationBar.separatorColor
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
||||
self.amountNode = ImmediateTextNode()
|
||||
self.amountNode.textAlignment = .center
|
||||
self.amountNode.maximumNumberOfLines = 1
|
||||
@ -293,14 +329,24 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
self.feesNode.maximumNumberOfLines = 2
|
||||
self.feesNode.lineSpacing = 0.35
|
||||
|
||||
self.feesInfoIconNode = ASImageNode()
|
||||
self.feesInfoIconNode.displaysAsynchronously = false
|
||||
self.feesInfoIconNode.displayWithoutProcessing = true
|
||||
self.feesInfoIconNode.image = UIImage(bundleImageName: "Wallet/InfoIcon")
|
||||
|
||||
self.feesButtonNode = ASButtonNode()
|
||||
|
||||
self.commentBackgroundNode = ASImageNode()
|
||||
self.commentBackgroundNode.contentMode = .scaleToFill
|
||||
self.commentBackgroundNode.isUserInteractionEnabled = true
|
||||
|
||||
self.commentTextNode = ImmediateTextNode()
|
||||
self.commentTextNode.textAlignment = .natural
|
||||
self.commentTextNode.maximumNumberOfLines = 0
|
||||
self.commentTextNode.isUserInteractionEnabled = false
|
||||
|
||||
self.commentSeparatorNode = ASDisplayNode()
|
||||
self.commentSeparatorNode.backgroundColor = self.presentationData.theme.list.itemPlainSeparatorColor
|
||||
|
||||
self.addressTextNode = ImmediateTextNode()
|
||||
self.addressTextNode.maximumNumberOfLines = 4
|
||||
@ -327,14 +373,19 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
|
||||
self.addSubnode(self.navigationBackgroundNode)
|
||||
self.addSubnode(self.navigationSeparatorNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.timeNode)
|
||||
self.addSubnode(self.amountNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.feesNode)
|
||||
self.addSubnode(self.feesInfoIconNode)
|
||||
self.addSubnode(self.feesButtonNode)
|
||||
self.addSubnode(self.commentBackgroundNode)
|
||||
self.addSubnode(self.commentTextNode)
|
||||
self.addSubnode(self.scrollNode)
|
||||
self.scrollNode.addSubnode(self.commentBackgroundNode)
|
||||
self.scrollNode.addSubnode(self.commentTextNode)
|
||||
self.addSubnode(self.commentSeparatorNode)
|
||||
self.addSubnode(self.addressTextNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
@ -346,7 +397,6 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.Wallet_TransactionInfo_Title, font: titleFont, textColor: textColor)
|
||||
|
||||
|
||||
self.timeNode.attributedText = NSAttributedString(string: stringForFullDate(timestamp: Int32(clamping: timestamp), strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat), font: subtitleFont, textColor: seccondaryTextColor)
|
||||
|
||||
let amountString: String
|
||||
@ -372,16 +422,18 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
feesString.append(formatBalanceText(transaction.otherFee, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) + " transaction fee")
|
||||
}
|
||||
|
||||
if !feesString.isEmpty {
|
||||
feesString.append("(?)")
|
||||
}
|
||||
self.feesInfoIconNode.isHidden = feesString.isEmpty
|
||||
}
|
||||
self.feesNode.attributedText = NSAttributedString(string: feesString, font: subtitleFont, textColor: seccondaryTextColor)
|
||||
|
||||
self.feesButtonNode.addTarget(self, action: #selector(feesPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.commentBackgroundNode.image = messageBubbleImage(incoming: transferredValue > 0, fillColor: UIColor(rgb: 0xf1f1f5), strokeColor: UIColor(rgb: 0xf1f1f5))
|
||||
self.commentTextNode.attributedText = NSAttributedString(string: extractDescription(walletTransaction), font: Font.regular(17.0), textColor: .black)
|
||||
var commentBackgroundColor = presentationData.theme.transaction.descriptionBackgroundColor
|
||||
if commentBackgroundColor.distance(to: presentationData.theme.list.plainBackgroundColor) < 100 {
|
||||
commentBackgroundColor = UIColor(rgb: 0xf1f1f4)
|
||||
}
|
||||
self.commentBackgroundNode.image = messageBubbleImage(incoming: transferredValue > 0, fillColor: commentBackgroundColor, strokeColor: presentationData.theme.transaction.descriptionBackgroundColor)
|
||||
self.commentTextNode.attributedText = NSAttributedString(string: extractDescription(walletTransaction), font: Font.regular(17.0), textColor: presentationData.theme.transaction.descriptionTextColor)
|
||||
|
||||
let address = extractAddress(walletTransaction)
|
||||
var singleAddress: String?
|
||||
@ -391,7 +443,7 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
if let address = singleAddress {
|
||||
self.addressTextNode.attributedText = NSAttributedString(string: formatAddress(address), font: addressFont, textColor: textColor, paragraphAlignment: .justified)
|
||||
self.buttonNode.title = "Send Grams to This Address"
|
||||
self.buttonNode.title = presentationData.strings.Wallet_TransactionInfo_SendGrams
|
||||
|
||||
self.buttonNode.pressed = { [weak self] in
|
||||
self?.send?(address)
|
||||
@ -399,9 +451,76 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let commentGestureRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapCommentGesture(_:)))
|
||||
commentGestureRecognizer.tapActionAtPoint = { [weak self] point in
|
||||
return .waitForSingleTap
|
||||
}
|
||||
self.commentBackgroundNode.view.addGestureRecognizer(commentGestureRecognizer)
|
||||
|
||||
let addressGestureRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapAddressGesture(_:)))
|
||||
addressGestureRecognizer.tapActionAtPoint = { [weak self] point in
|
||||
return .waitForSingleTap
|
||||
}
|
||||
self.addressTextNode.view.addGestureRecognizer(addressGestureRecognizer)
|
||||
}
|
||||
|
||||
@objc func tapLongTapOrDoubleTapCommentGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
case .longTap:
|
||||
let description = extractDescription(self.walletTransaction)
|
||||
if !description.isEmpty {
|
||||
self.displayCopyContextMenu?(self, self.commentBackgroundNode.frame, description)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@objc func tapLongTapOrDoubleTapAddressGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
case .longTap:
|
||||
let address = extractAddress(self.walletTransaction)
|
||||
var singleAddress: String?
|
||||
if case let .list(list) = address, list.count == 1 {
|
||||
singleAddress = list.first
|
||||
}
|
||||
|
||||
if let address = singleAddress {
|
||||
self.displayCopyContextMenu?(self, self.addressTextNode.frame, address)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func feesPressed() {
|
||||
self.displayFeesTooltip?(self.feesNode, self.feesNode.bounds)
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateTitle()
|
||||
}
|
||||
|
||||
private func updateTitle() {
|
||||
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
var insets = layout.insets(options: [])
|
||||
@ -430,17 +549,6 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
transition.updateFrame(node: self.feesNode, frame: feesFrame)
|
||||
transition.updateFrame(node: self.feesButtonNode, frame: feesFrame)
|
||||
|
||||
let commentSize = self.commentTextNode.updateLayout(CGSize(width: layout.size.width - 36.0 * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
let commentFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - commentSize.width) / 2.0), y: amountFrame.maxY + 84.0), size: CGSize(width: commentSize.width, height: commentSize.height))
|
||||
transition.updateFrame(node: self.commentTextNode, frame: commentFrame)
|
||||
|
||||
var commentBackgroundFrame = commentSize.width > 0.0 ? commentFrame.insetBy(dx: -11.0, dy: -7.0) : CGRect()
|
||||
commentBackgroundFrame.size.width += 7.0
|
||||
if self.incoming {
|
||||
commentBackgroundFrame.origin.x -= 7.0
|
||||
}
|
||||
transition.updateFrame(node: self.commentBackgroundNode, frame: commentBackgroundFrame)
|
||||
|
||||
let buttonSideInset: CGFloat = 16.0
|
||||
let bottomInset = insets.bottom + 10.0
|
||||
let buttonWidth = layout.size.width - buttonSideInset * 2.0
|
||||
@ -451,6 +559,27 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode {
|
||||
self.buttonNode.updateLayout(width: buttonFrame.width, transition: transition)
|
||||
|
||||
let addressSize = self.addressTextNode.updateLayout(CGSize(width: layout.size.width - inset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
transition.updateFrame(node: self.addressTextNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - addressSize.width) / 2.0), y: buttonFrame.minY - addressSize.height - 44.0), size: addressSize))
|
||||
let addressFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - addressSize.width) / 2.0), y: buttonFrame.minY - addressSize.height - 38.0), size: addressSize)
|
||||
transition.updateFrame(node: self.addressTextNode, frame: addressFrame)
|
||||
|
||||
transition.updateFrame(node: self.navigationBackgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: navigationHeight))
|
||||
transition.updateFrame(node: self.navigationSeparatorNode, frame: CGRect(x: 0.0, y: navigationHeight, width: layout.size.width, height: UIScreenPixel))
|
||||
|
||||
let commentSeparatorFrame = CGRect(x: 0.0, y: addressFrame.minY - 36.0, width: layout.size.width, height: UIScreenPixel)
|
||||
transition.updateFrame(node: self.commentSeparatorNode, frame: commentSeparatorFrame)
|
||||
|
||||
let scrollFrame = CGRect(x: 0.0, y: navigationHeight, width: layout.size.width, height: commentSeparatorFrame.minY - navigationHeight)
|
||||
transition.updateFrame(node: self.scrollNode, frame: scrollFrame)
|
||||
|
||||
let commentSize = self.commentTextNode.updateLayout(CGSize(width: layout.size.width - 36.0 * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
let commentFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - commentSize.width) / 2.0), y: amountFrame.maxY + 84.0 - navigationHeight), size: CGSize(width: commentSize.width, height: commentSize.height))
|
||||
transition.updateFrame(node: self.commentTextNode, frame: commentFrame)
|
||||
|
||||
var commentBackgroundFrame = commentSize.width > 0.0 ? commentFrame.insetBy(dx: -11.0, dy: -7.0) : CGRect()
|
||||
commentBackgroundFrame.size.width += 7.0
|
||||
if self.incoming {
|
||||
commentBackgroundFrame.origin.x -= 7.0
|
||||
}
|
||||
transition.updateFrame(node: self.commentBackgroundNode, frame: commentBackgroundFrame)
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,14 +254,16 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
||||
}
|
||||
|
||||
private func updateTitle() {
|
||||
guard let listTitleFrame = self.listTitleFrame else {
|
||||
guard let layout = self.validLayout, let listTitleFrame = self.listTitleFrame else {
|
||||
return
|
||||
}
|
||||
let scrollView = self.scrollNode.view
|
||||
|
||||
let navigationHeight = self.navigationHeight ?? 0.0
|
||||
let minY = navigationHeight - 44.0 + floor(44.0 / 2.0)
|
||||
let maxY = minY + 44.0
|
||||
let nominalNavigationHeight = navigationHeight - (layout.0.statusBarHeight ?? 0.0)
|
||||
|
||||
let minY = navigationHeight - nominalNavigationHeight + floor(nominalNavigationHeight / 2.0)
|
||||
let maxY = minY + nominalNavigationHeight
|
||||
let y = max(minY, -scrollView.contentOffset.y + listTitleFrame.midY)
|
||||
var t = (y - minY) / (maxY - minY)
|
||||
t = max(0.0, min(1.0, t))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user