Various fixes

This commit is contained in:
Ilya Laktyushin 2019-10-17 15:22:44 +03:00
parent 7c0c73d9bf
commit 4da3336dfb
47 changed files with 4143 additions and 3554 deletions

3
BUCK
View File

@ -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",
],
)

View File

@ -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!";

View File

@ -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,

View File

@ -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;

View File

@ -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
}

View File

@ -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 {

View File

@ -8,7 +8,6 @@ public enum ActionSheetButtonColor {
case disabled
}
public enum ActionSheetButtonFont {
case `default`
case bold

View File

@ -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)
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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)
}

View File

@ -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)

View File

@ -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()

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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",

View File

@ -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()

View File

@ -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
}
})

View File

@ -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))
})

View File

@ -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))
}

View File

@ -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()
}

View File

@ -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)

View File

@ -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(

View File

@ -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(

View File

@ -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(

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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))
}
}
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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?()
}

View File

@ -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

View File

@ -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

View File

@ -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
)
)
}

View File

@ -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))
}
}

View File

@ -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

View File

@ -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

View File

@ -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)) {

View File

@ -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))

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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))