mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
0a717a2173
174 changed files with 6933 additions and 3737 deletions
24
Makefile
24
Makefile
|
|
@ -168,6 +168,28 @@ package_arm64:
|
|||
PACKAGE_BUNDLE_ID="${BUNDLE_ID}" \
|
||||
sh package_app.sh iphoneos-arm64 $(BUCK) $(BUCK_OPTIONS) ${BUCK_RELEASE_OPTIONS}
|
||||
|
||||
package_debug_arm64:
|
||||
PACKAGE_DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" \
|
||||
PACKAGE_CODE_SIGN_IDENTITY="${DEVELOPMENT_CODE_SIGN_IDENTITY}" \
|
||||
PACKAGE_PROVISIONING_PROFILE_APP="${DEVELOPMENT_PROVISIONING_PROFILE_APP}" \
|
||||
PACKAGE_ENTITLEMENTS_APP="${ENTITLEMENTS_APP}" \
|
||||
PACKAGE_PROVISIONING_PROFILE_EXTENSION_Share="${DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_SHARE}" \
|
||||
PACKAGE_ENTITLEMENTS_EXTENSION_Share="${ENTITLEMENTS_EXTENSION_SHARE}" \
|
||||
PACKAGE_PROVISIONING_PROFILE_EXTENSION_Widget="${DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_WIDGET}" \
|
||||
PACKAGE_ENTITLEMENTS_EXTENSION_Widget="${ENTITLEMENTS_EXTENSION_WIDGET}" \
|
||||
PACKAGE_PROVISIONING_PROFILE_EXTENSION_NotificationService="${DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_NOTIFICATIONSERVICE}" \
|
||||
PACKAGE_ENTITLEMENTS_EXTENSION_NotificationService="${ENTITLEMENTS_EXTENSION_NOTIFICATIONSERVICE}" \
|
||||
PACKAGE_PROVISIONING_PROFILE_EXTENSION_NotificationContent="${DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_NOTIFICATIONCONTENT}" \
|
||||
PACKAGE_ENTITLEMENTS_EXTENSION_NotificationContent="${ENTITLEMENTS_EXTENSION_NOTIFICATIONCONTENT}" \
|
||||
PACKAGE_PROVISIONING_PROFILE_EXTENSION_Intents="${DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_INTENTS}" \
|
||||
PACKAGE_ENTITLEMENTS_EXTENSION_Intents="${ENTITLEMENTS_EXTENSION_INTENTS}" \
|
||||
PACKAGE_PROVISIONING_PROFILE_WATCH_APP="${DEVELOPMENT_PROVISIONING_PROFILE_WATCH_APP}" \
|
||||
PACKAGE_PROVISIONING_PROFILE_WATCH_EXTENSION="${DISTRIBUTION_PROVISIONING_PROFILE_WATCH_EXTENSION}" \
|
||||
PACKAGE_BUNDLE_ID="${BUNDLE_ID}" \
|
||||
ENABLE_GET_TASK_ALLOW=1 \
|
||||
CODESIGNING_PROFILES_VARIANT="development" \
|
||||
sh package_app.sh iphoneos-arm64 $(BUCK) $(BUCK_OPTIONS) ${BUCK_RELEASE_OPTIONS}
|
||||
|
||||
package:
|
||||
PACKAGE_DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" \
|
||||
PACKAGE_CODE_SIGN_IDENTITY="${DISTRIBUTION_CODE_SIGN_IDENTITY}" \
|
||||
|
|
@ -192,7 +214,7 @@ app: build package
|
|||
|
||||
app_arm64: build_arm64 package_arm64
|
||||
|
||||
app_debug_arm64: build_debug_arm64 package_arm64
|
||||
app_debug_arm64: build_debug_arm64 package_debug_arm64
|
||||
|
||||
build_buckdebug: check_env
|
||||
BUCK_DEBUG_MODE=1 $(BUCK) build \
|
||||
|
|
|
|||
|
|
@ -302,6 +302,11 @@
|
|||
<string>remote-notification</string>
|
||||
<string>voip</string>
|
||||
</array>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<false/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
|
@ -357,10 +362,5 @@
|
|||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -4780,6 +4780,7 @@ Any member of this group will be able to see messages in the channel.";
|
|||
"Wallet.Info.Send" = "Send";
|
||||
"Wallet.Info.RefreshErrorTitle" = "An Error Occurred";
|
||||
"Wallet.Info.RefreshErrorText" = "The wallet state can not be retrieved at this time. Please try again later.";
|
||||
"Wallet.Info.RefreshErrorNetworkText" = "The wallet state can not be retrieved at this time. Please try again later.";
|
||||
"Wallet.Info.UnknownTransaction" = "Empty Transaction";
|
||||
"Wallet.Info.TransactionTo" = "to";
|
||||
"Wallet.Info.TransactionFrom" = "from";
|
||||
|
|
@ -4880,12 +4881,15 @@ Any member of this group will be able to see messages in the channel.";
|
|||
"Wallet.Words.NotDoneText" = "You didn't have enough time to write those words down.";
|
||||
"Wallet.Words.NotDoneOk" = "OK, Sorry";
|
||||
"Wallet.Words.NotDoneResponse" = "Apologies Accepted";
|
||||
"Wallet.Send.NetworkError" = "Network Error";
|
||||
"Wallet.Send.ErrorNotEnoughFunds" = "Not Enough Funds";
|
||||
"Wallet.Send.ErrorInvalidAddress" = "Invalid wallet address. Please correct and try again.";
|
||||
"Wallet.Send.ErrorDecryptionFailed" = "Please make sure that your device has a passcode set in iOS Settings and try again.";
|
||||
"Wallet.Send.UninitializedTitle" = "Warning";
|
||||
"Wallet.Send.UninitializedText" = "This address belongs to an empty wallet. Are you sure you want to transfer grams to it?";
|
||||
"Wallet.Send.SendAnyway" = "Send Anyway";
|
||||
"Wallet.Receive.CreateInvoice" = "Create Invoice";
|
||||
"Wallet.Receive.CreateInvoiceInfo" = "You can specify amount and purpose of the payment to save the sender some time.";
|
||||
"Wallet.Send.SendingToYourself" = "Sending Grams from a wallet to the same wallet doesn't make sense, you will simply waste a portion of the value on blockchain fees.";
|
||||
"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";
|
||||
|
|
|
|||
|
|
@ -184,6 +184,12 @@ for ITEM in $APP_ITEMS_WITH_PROVISIONING_PROFILE; do
|
|||
fi
|
||||
done
|
||||
|
||||
if [ "$ENABLE_GET_TASK_ALLOW" == "1" ]; then
|
||||
KEY="com.apple.security.get-task-allow"
|
||||
PLUTIL_KEY=$(echo "$KEY" | sed 's/\./\\\./g')
|
||||
plutil -insert "$PLUTIL_KEY" -xml "<false/>" "$PROFILE_ENTITLEMENTS_PATH"
|
||||
fi
|
||||
|
||||
ENTITLEMENTS_VAR=PACKAGE_ENTITLEMENTS_$ITEM
|
||||
if [ ! -z "${!ENTITLEMENTS_VAR}" ]; then
|
||||
if [ ! -f "${!ENTITLEMENTS_VAR}" ]; then
|
||||
|
|
|
|||
|
|
@ -24,6 +24,6 @@
|
|||
|
||||
+ (void)getHardwareEncryptionAvailableWithBaseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable))completion;
|
||||
+ (void)encryptApplicationSecret:(NSData * _Nonnull)secret baseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable, NSData * _Nullable))completion;
|
||||
+ (void)decryptApplicationSecret:(NSData * _Nonnull)secret publicKey:(NSData * _Nonnull)publicKey baseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable))completion;
|
||||
+ (void)decryptApplicationSecret:(NSData * _Nonnull)secret publicKey:(NSData * _Nonnull)publicKey baseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable, bool))completion;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -694,28 +694,29 @@ API_AVAILABLE(ios(10))
|
|||
});
|
||||
}
|
||||
|
||||
+ (void)decryptApplicationSecret:(NSData * _Nonnull)secret publicKey:(NSData * _Nonnull)publicKey baseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable))completion {
|
||||
+ (void)decryptApplicationSecret:(NSData * _Nonnull)secret publicKey:(NSData * _Nonnull)publicKey baseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable, bool))completion {
|
||||
dispatch_async([self encryptionQueue], ^{
|
||||
LocalPrivateKey *privateKey = [self getApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
if (privateKey == nil) {
|
||||
completion(nil);
|
||||
completion(nil, false);
|
||||
return;
|
||||
}
|
||||
if (privateKey == nil) {
|
||||
completion(nil);
|
||||
completion(nil, false);
|
||||
return;
|
||||
}
|
||||
NSData *currentPublicKey = [privateKey getPublicKey];
|
||||
if (currentPublicKey == nil) {
|
||||
completion(nil);
|
||||
completion(nil, false);
|
||||
return;
|
||||
}
|
||||
if (![publicKey isEqualToData:currentPublicKey]) {
|
||||
completion(nil);
|
||||
completion(nil, false);
|
||||
return;
|
||||
}
|
||||
NSData *result = [privateKey decrypt:secret cancelled:nil];
|
||||
completion(result);
|
||||
bool cancelled = false;
|
||||
NSData *result = [privateKey decrypt:secret cancelled:&cancelled];
|
||||
completion(result, cancelled);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,33 +47,47 @@ private func fetchRawData(prefix: String) -> Signal<Data, FetchError> {
|
|||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
public func cloudDataAdditionalAddressSource(phoneNumber: Signal<String?, NoError>) -> Signal<MTBackupDatacenterData, NoError> {
|
||||
return phoneNumber
|
||||
|> take(1)
|
||||
|> mapToSignal { _ -> Signal<MTBackupDatacenterData, NoError> in
|
||||
let phoneNumber: String? = "7950"
|
||||
var prefix = ""
|
||||
if let phoneNumber = phoneNumber, phoneNumber.count >= 1 {
|
||||
prefix = String(phoneNumber[phoneNumber.startIndex ..< phoneNumber.index(after: phoneNumber.startIndex)])
|
||||
}
|
||||
return fetchRawData(prefix: prefix)
|
||||
|> map { data -> MTBackupDatacenterData? in
|
||||
if let datacenterData = MTIPDataDecode(data, phoneNumber ?? "") {
|
||||
return datacenterData
|
||||
} else {
|
||||
return nil
|
||||
private final class CloudDataPrefixContext {
|
||||
private let prefix: String
|
||||
private let value = Promise<Data?>()
|
||||
|
||||
private var lastRequestTimestamp: Double?
|
||||
|
||||
init(prefix: String) {
|
||||
self.prefix = prefix
|
||||
}
|
||||
|
||||
private func fetch() {
|
||||
let fetchSignal = (
|
||||
fetchRawData(prefix: self.prefix)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { error -> Signal<Data?, NoError> in
|
||||
switch error {
|
||||
case .networkUnavailable:
|
||||
return .complete()
|
||||
default:
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|> restart
|
||||
)
|
||||
|> take(1)
|
||||
self.value.set(fetchSignal)
|
||||
}
|
||||
|
||||
func get() -> Signal<Data?, NoError> {
|
||||
var shouldFetch = false
|
||||
let timestamp = CFAbsoluteTimeGetCurrent()
|
||||
if let lastRequestTimestamp = self.lastRequestTimestamp {
|
||||
shouldFetch = timestamp >= lastRequestTimestamp + 1.0 * 60.0
|
||||
} else {
|
||||
shouldFetch = true
|
||||
}
|
||||
|> `catch` { error -> Signal<MTBackupDatacenterData?, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|> mapToSignal { data -> Signal<MTBackupDatacenterData, NoError> in
|
||||
if let data = data {
|
||||
return .single(data)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
if shouldFetch {
|
||||
self.lastRequestTimestamp = timestamp
|
||||
self.fetch()
|
||||
}
|
||||
return self.value.get()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,32 +95,25 @@ public func cloudDataAdditionalAddressSource(phoneNumber: Signal<String?, NoErro
|
|||
private final class CloudDataContextObject {
|
||||
private let queue: Queue
|
||||
|
||||
private var prefixContexts: [String: CloudDataPrefixContext] = [:]
|
||||
|
||||
init(queue: Queue) {
|
||||
self.queue = queue
|
||||
|
||||
let container = CKContainer.default()
|
||||
let publicDatabase = container.database(with: .public)
|
||||
|
||||
/*let changesOperation = CKFetchDatabaseChangesOperation(previousServerChangeToken: nil)
|
||||
changesOperation.fetchAllChanges = true
|
||||
changesOperation.recordZoneWithIDChangedBlock = { _ in
|
||||
print("recordZoneWithIDChangedBlock")
|
||||
}
|
||||
|
||||
func get(prefix: String) -> Signal<Data?, NoError> {
|
||||
let context: CloudDataPrefixContext
|
||||
if let current = self.prefixContexts[prefix] {
|
||||
context = current
|
||||
} else {
|
||||
context = CloudDataPrefixContext(prefix: prefix)
|
||||
}
|
||||
changesOperation.recordZoneWithIDWasDeletedBlock = { _ in
|
||||
|
||||
}
|
||||
changesOperation.changeTokenUpdatedBlock = { _ in
|
||||
print("changeTokenUpdatedBlock")
|
||||
}
|
||||
changesOperation.fetchDatabaseChangesCompletionBlock = { serverChangeToken, isMoreComing, error in
|
||||
print("done")
|
||||
}
|
||||
publicDatabase.add(changesOperation)*/
|
||||
return context.get()
|
||||
}
|
||||
}
|
||||
|
||||
public protocol CloudDataContext {
|
||||
|
||||
func get(phoneNumber: Signal<String?, NoError>) -> Signal<MTBackupDatacenterData, NoError>
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
|
|
@ -120,4 +127,29 @@ public final class CloudDataContextImpl: CloudDataContext {
|
|||
return CloudDataContextObject(queue: queue)
|
||||
})
|
||||
}
|
||||
|
||||
public func get(phoneNumber: Signal<String?, NoError>) -> Signal<MTBackupDatacenterData, NoError> {
|
||||
return phoneNumber
|
||||
|> take(1)
|
||||
|> mapToSignal { phoneNumber -> Signal<MTBackupDatacenterData, NoError> in
|
||||
var prefix = ""
|
||||
if let phoneNumber = phoneNumber, phoneNumber.count >= 1 {
|
||||
prefix = String(phoneNumber[phoneNumber.startIndex ..< phoneNumber.index(after: phoneNumber.startIndex)])
|
||||
}
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.get(prefix: prefix).start(next: { data in
|
||||
if let data = data, let datacenterData = MTIPDataDecode(data, phoneNumber ?? "") {
|
||||
subscriber.putNext(datacenterData)
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple
|
|||
controller?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.view.endEditing(true)
|
||||
//controller?.view.endEditing(true)
|
||||
controller?.dismiss()
|
||||
}
|
||||
ensureTextVisibleImpl = { [weak controller] in
|
||||
|
|
|
|||
|
|
@ -1234,11 +1234,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||
case let .controller(controller):
|
||||
let controllerPoint = self.view.convert(point, to: controller.controller.view)
|
||||
if let result = controller.controller.view.hitTest(controllerPoint, with: event) {
|
||||
if result is UIScrollView {
|
||||
return result
|
||||
} else if result is ListViewBackingView {
|
||||
return result
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
|||
override public func reset() {
|
||||
super.reset()
|
||||
|
||||
self.endPressedAppearance()
|
||||
|
||||
self.currentProgress = 0.0
|
||||
self.delayTimer?.invalidate()
|
||||
self.delayTimer = nil
|
||||
|
|
@ -152,9 +154,8 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
|||
|
||||
if let touch = touches.first {
|
||||
if !self.currentProgress.isZero, self.isValidated {
|
||||
if #available(iOS 9.0, *) {
|
||||
self.activationProgress?(0.0, .ended(self.currentProgress))
|
||||
}
|
||||
self.currentProgress = 0.0
|
||||
self.activationProgress?(0.0, .ended(self.currentProgress))
|
||||
}
|
||||
|
||||
self.externalEnded?((self.view, touch.location(in: self.view)))
|
||||
|
|
@ -170,9 +171,8 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
|||
super.touchesCancelled(touches, with: event)
|
||||
|
||||
if let touch = touches.first, !self.currentProgress.isZero, self.isValidated {
|
||||
if #available(iOS 9.0, *) {
|
||||
self.activationProgress?(0.0, .ended(self.currentProgress))
|
||||
}
|
||||
self.currentProgress = 0.0
|
||||
self.activationProgress?(0.0, .ended(self.currentProgress))
|
||||
}
|
||||
|
||||
self.delayTimer?.invalidate()
|
||||
|
|
@ -183,6 +183,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
|||
|
||||
public func cancel() {
|
||||
if !self.currentProgress.isZero, self.isValidated {
|
||||
self.currentProgress = 0.0
|
||||
self.activationProgress?(0.0, .ended(self.currentProgress))
|
||||
|
||||
self.delayTimer?.invalidate()
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ private final class AuthorizationSequenceCountrySelectionNavigationContentNode:
|
|||
self.addSubnode(self.searchBar)
|
||||
|
||||
self.searchBar.cancel = { [weak self] in
|
||||
self?.searchBar.deactivate(clear: false)
|
||||
//self?.searchBar.deactivate(clear: false)
|
||||
self?.cancel()
|
||||
}
|
||||
|
||||
|
|
@ -159,6 +159,8 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll
|
|||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: theme), strings: NavigationBarStrings(presentationStrings: strings)))
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
|
||||
self.statusBar.statusBarStyle = theme.rootController.statusBarStyle.style
|
||||
|
||||
let navigationContentNode = AuthorizationSequenceCountrySelectionNavigationContentNode(theme: theme, strings: strings, cancel: { [weak self] in
|
||||
|
|
@ -191,7 +193,6 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll
|
|||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
self.controllerNode.animateIn()
|
||||
self.navigationContentNode?.activate()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,6 @@ final class AuthorizationSequenceCountrySelectionControllerNode: ASDisplayNode,
|
|||
}
|
||||
self.sections = sections
|
||||
var sectionTitles = sections.map { $0.0 }
|
||||
sectionTitles.insert(UITableView.indexSearch, at: 0)
|
||||
self.sectionTitles = sectionTitles
|
||||
|
||||
super.init()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ open class ActionSheetController: ViewController, PresentableController, Standal
|
|||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
self.blocksBackgroundWhenInOverlay = true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ public final class ContextMenuController: ViewController, KeyShortcutResponder,
|
|||
self.hasHapticFeedback = hasHapticFeedback
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
|
|
|||
|
|
@ -95,10 +95,8 @@ final class GlobalOverlayPresentationContext {
|
|||
}
|
||||
return keyboardWindow
|
||||
} else {
|
||||
if underStatusBar, let view = self.parentView {
|
||||
if let view = self.parentView {
|
||||
return view
|
||||
} else {
|
||||
return statusBarHost.statusBarWindow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3313,6 +3313,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||
indicatorFrame.origin.y = self.visibleSize.height - (self.scrollIndicatorInsets.bottom + indicatorBottomInset) - indicatorFrame.height
|
||||
}
|
||||
|
||||
if indicatorFrame.origin.y.isNaN {
|
||||
indicatorFrame.origin.y = indicatorTopInset
|
||||
}
|
||||
|
||||
if indicatorHeight >= visibleHeightWithoutIndicatorInsets {
|
||||
verticalScrollIndicator.isHidden = true
|
||||
verticalScrollIndicator.frame = indicatorFrame
|
||||
|
|
|
|||
|
|
@ -76,13 +76,22 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
private(set) var controllers: [ViewController] = []
|
||||
private var state: State = State(layout: nil, canBeClosed: nil, top: nil, transition: nil, pending: nil)
|
||||
|
||||
private var ignoreInputHeight: Bool = false
|
||||
|
||||
private(set) var isReady: Bool = false
|
||||
var isReadyUpdated: (() -> Void)?
|
||||
var controllerRemoved: (ViewController) -> Void
|
||||
var keyboardViewManager: KeyboardViewManager? {
|
||||
didSet {
|
||||
if self.keyboardViewManager !== oldValue {
|
||||
|
||||
}
|
||||
}
|
||||
var canHaveKeyboardFocus: Bool = false {
|
||||
didSet {
|
||||
if self.canHaveKeyboardFocus != oldValue {
|
||||
if !self.canHaveKeyboardFocus {
|
||||
self.view.endEditing(true)
|
||||
self.performUpdate(transition: .animated(duration: 0.5, curve: .spring))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -147,6 +156,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
guard self.state.transition == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
let beginGesture = self.controllers.count > 1
|
||||
|
||||
if beginGesture {
|
||||
|
|
@ -163,7 +173,11 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
|
||||
topController.viewWillDisappear(true)
|
||||
let topNode = topController.displayNode
|
||||
bottomController.containerLayoutUpdated(layout, transition: .immediate)
|
||||
var bottomControllerLayout = layout
|
||||
if bottomController.view.disableAutomaticKeyboardHandling.isEmpty {
|
||||
bottomControllerLayout = bottomControllerLayout.withUpdatedInputHeight(nil)
|
||||
}
|
||||
bottomController.containerLayoutUpdated(bottomControllerLayout, transition: .immediate)
|
||||
bottomController.viewWillAppear(true)
|
||||
let bottomNode = bottomController.displayNode
|
||||
|
||||
|
|
@ -195,11 +209,16 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
|
||||
let topController = top.value
|
||||
let bottomController = transition.previous.value
|
||||
|
||||
if viewTreeContainsFirstResponder(view: top.value.view) {
|
||||
strongSelf.ignoreInputHeight = true
|
||||
}
|
||||
strongSelf.keyboardViewManager?.dismissEditingWithoutAnimation(view: topController.view)
|
||||
|
||||
strongSelf.state.transition = nil
|
||||
|
||||
strongSelf.controllerRemoved(top.value)
|
||||
strongSelf.ignoreInputHeight = false
|
||||
})
|
||||
} else {
|
||||
navigationTransitionCoordinator.animateCancel({ [weak self] in
|
||||
|
|
@ -259,7 +278,11 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
} else {
|
||||
transitionType = .pop
|
||||
}
|
||||
self.state.pending = PendingChild(value: self.makeChild(layout: layout.withUpdatedInputHeight(nil), value: last), transitionType: transitionType, transition: transition, update: { [weak self] pendingChild in
|
||||
var updatedLayout = layout
|
||||
if last.view.disableAutomaticKeyboardHandling.isEmpty {
|
||||
updatedLayout = updatedLayout.withUpdatedInputHeight(nil)
|
||||
}
|
||||
self.state.pending = PendingChild(value: self.makeChild(layout: updatedLayout, value: last), transitionType: transitionType, transition: transition, update: { [weak self] pendingChild in
|
||||
self?.pendingChildIsReady(pendingChild)
|
||||
})
|
||||
}
|
||||
|
|
@ -274,7 +297,11 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
self.state.pending = nil
|
||||
let previous = self.state.top
|
||||
self.state.top = pending.value
|
||||
self.topTransition(from: previous, to: pending.value, transitionType: pending.transitionType, layout: layout.withUpdatedInputHeight(nil), transition: pending.transition)
|
||||
var updatedLayout = layout
|
||||
if pending.value.value.view.disableAutomaticKeyboardHandling.isEmpty {
|
||||
updatedLayout = updatedLayout.withUpdatedInputHeight(nil)
|
||||
}
|
||||
self.topTransition(from: previous, to: pending.value, transitionType: pending.transitionType, layout: updatedLayout, transition: pending.transition)
|
||||
statusBarTransition = pending.transition
|
||||
if !self.isReady {
|
||||
self.isReady = true
|
||||
|
|
@ -291,7 +318,13 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
|
||||
var updatedStatusBarStyle = self.statusBarStyle
|
||||
if let top = self.state.top {
|
||||
self.applyLayout(layout: layout, to: top, isMaster: true, transition: transition)
|
||||
var updatedLayout = layout
|
||||
if let topTransition = self.state.transition, top.value.view.disableAutomaticKeyboardHandling.isEmpty {
|
||||
if !viewTreeContainsFirstResponder(view: top.value.view) {
|
||||
updatedLayout = updatedLayout.withUpdatedInputHeight(nil)
|
||||
}
|
||||
}
|
||||
self.applyLayout(layout: updatedLayout, to: top, isMaster: true, transition: transition)
|
||||
updatedStatusBarStyle = top.value.statusBar.statusBarStyle
|
||||
} else {
|
||||
updatedStatusBarStyle = .Ignore
|
||||
|
|
@ -349,6 +382,10 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
guard let strongSelf = self, let topTransition = topTransition, strongSelf.state.transition === topTransition else {
|
||||
return
|
||||
}
|
||||
|
||||
if viewTreeContainsFirstResponder(view: topTransition.previous.value.view) {
|
||||
strongSelf.ignoreInputHeight = true
|
||||
}
|
||||
strongSelf.keyboardViewManager?.dismissEditingWithoutAnimation(view: topTransition.previous.value.view)
|
||||
strongSelf.state.transition = nil
|
||||
|
||||
|
|
@ -361,6 +398,8 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
strongSelf.applyLayout(layout: layout, to: toValue, isMaster: true, transition: .immediate)
|
||||
toValue.value.viewDidAppear(true)
|
||||
}
|
||||
|
||||
strongSelf.ignoreInputHeight = false
|
||||
})
|
||||
} else {
|
||||
if let fromValue = fromValue {
|
||||
|
|
@ -383,12 +422,19 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
}
|
||||
|
||||
private func makeChild(layout: ContainerViewLayout, value: ViewController) -> Child {
|
||||
value.containerLayoutUpdated(layout, transition: .immediate)
|
||||
return Child(value: value, layout: layout)
|
||||
var updatedLayout = layout
|
||||
if value.view.disableAutomaticKeyboardHandling.isEmpty {
|
||||
updatedLayout = updatedLayout.withUpdatedInputHeight(nil)
|
||||
}
|
||||
value.containerLayoutUpdated(updatedLayout, transition: .immediate)
|
||||
return Child(value: value, layout: updatedLayout)
|
||||
}
|
||||
|
||||
private func applyLayout(layout: ContainerViewLayout, to child: Child, isMaster: Bool, transition: ContainedViewLayoutTransition) {
|
||||
var childFrame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
var updatedLayout = layout
|
||||
|
||||
var shouldSyncKeyboard = false
|
||||
if let transition = self.state.transition {
|
||||
childFrame.origin.x = child.value.displayNode.frame.origin.x
|
||||
|
|
@ -400,10 +446,21 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
case .push:
|
||||
break
|
||||
}
|
||||
if updatedLayout.inputHeight != nil {
|
||||
if !self.canHaveKeyboardFocus && child.value.view.disableAutomaticKeyboardHandling.isEmpty {
|
||||
updatedLayout = updatedLayout.withUpdatedInputHeight(nil)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if isMaster {
|
||||
shouldSyncKeyboard = true
|
||||
}
|
||||
|
||||
if updatedLayout.inputHeight != nil && child.value.view.disableAutomaticKeyboardHandling.isEmpty {
|
||||
if !self.canHaveKeyboardFocus || self.ignoreInputHeight {
|
||||
updatedLayout = updatedLayout.withUpdatedInputHeight(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
if child.value.displayNode.frame != childFrame {
|
||||
transition.updateFrame(node: child.value.displayNode, frame: childFrame)
|
||||
|
|
@ -411,9 +468,9 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
if shouldSyncKeyboard && isMaster {
|
||||
self.syncKeyboard(leftEdge: childFrame.minX, transition: transition)
|
||||
}
|
||||
if child.layout != layout {
|
||||
child.layout = layout
|
||||
child.value.containerLayoutUpdated(layout, transition: transition)
|
||||
if child.layout != updatedLayout {
|
||||
child.layout = updatedLayout
|
||||
child.value.containerLayoutUpdated(updatedLayout, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -430,13 +487,13 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
private func pendingChildIsReady(_ child: PendingChild) {
|
||||
if let pending = self.state.pending, pending === child {
|
||||
pending.isReady = true
|
||||
self.performUpdate()
|
||||
self.performUpdate(transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
private func performUpdate() {
|
||||
private func performUpdate(transition: ContainedViewLayoutTransition) {
|
||||
if let layout = self.state.layout, let canBeClosed = self.state.canBeClosed {
|
||||
self.update(layout: layout, canBeClosed: canBeClosed, controllers: self.controllers, transition: .immediate)
|
||||
self.update(layout: layout, canBeClosed: canBeClosed, controllers: self.controllers, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,12 +118,19 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
return self.view as! NavigationControllerView
|
||||
}
|
||||
|
||||
var inCallNavigate: (() -> Void)?
|
||||
private var inCallStatusBar: StatusBar?
|
||||
private var globalScrollToTopNode: ScrollToTopNode?
|
||||
private var rootContainer: RootContainer?
|
||||
private var rootModalFrame: NavigationModalFrame?
|
||||
private var modalContainers: [NavigationModalContainer] = []
|
||||
private var overlayContainers: [NavigationOverlayContainer] = []
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var validStatusBarStyle: NavigationStatusBarStyle?
|
||||
private var validStatusBarHidden: Bool = false
|
||||
|
||||
private var ignoreInputHeight: Bool = false
|
||||
private var currentStatusBarExternalHidden: Bool = false
|
||||
|
||||
private var scheduledLayoutTransitionRequestId: Int = 0
|
||||
private var scheduledLayoutTransitionRequest: (Int, ContainedViewLayoutTransition)?
|
||||
|
|
@ -148,10 +155,20 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
|
||||
private var _displayNode: ASDisplayNode?
|
||||
public var displayNode: ASDisplayNode {
|
||||
if let value = self._displayNode {
|
||||
return value
|
||||
}
|
||||
if !self.isViewLoaded {
|
||||
self.loadView()
|
||||
}
|
||||
|
||||
return self._displayNode!
|
||||
}
|
||||
|
||||
var statusBarHost: StatusBarHost?
|
||||
var statusBarHost: StatusBarHost? {
|
||||
didSet {
|
||||
}
|
||||
}
|
||||
var keyboardViewManager: KeyboardViewManager?
|
||||
|
||||
public func updateMasterDetailsBlackout(_ blackout: MasterDetailLayoutBlackout?, transition: ContainedViewLayoutTransition) {
|
||||
|
|
@ -199,6 +216,9 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
for modalContainer in self.modalContainers {
|
||||
supportedOrientations = supportedOrientations.intersection(modalContainer.container.combinedSupportedOrientations(currentOrientationToLock: currentOrientationToLock))
|
||||
}
|
||||
for overlayContrainer in self.overlayContainers {
|
||||
supportedOrientations = supportedOrientations.intersection(overlayContrainer.controller.combinedSupportedOrientations(currentOrientationToLock: currentOrientationToLock))
|
||||
}
|
||||
return supportedOrientations
|
||||
}
|
||||
|
||||
|
|
@ -214,18 +234,10 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
}
|
||||
if self.isViewLoaded {
|
||||
if statusBarStyleUpdated {
|
||||
self.validStatusBarStyle = self.theme.statusBar
|
||||
let normalStatusBarStyle: UIStatusBarStyle
|
||||
switch self.theme.statusBar {
|
||||
case .black:
|
||||
normalStatusBarStyle = .default
|
||||
case .white:
|
||||
normalStatusBarStyle = .lightContent
|
||||
}
|
||||
//self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: false)
|
||||
}
|
||||
self.controllerView.backgroundColor = theme.emptyAreaColor
|
||||
if let layout = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -239,16 +251,41 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
self.validLayout = layout
|
||||
self.updateContainers(layout: layout, transition: transition)
|
||||
|
||||
self.inCallStatusBar?.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: max(40.0, layout.safeInsets.top)))
|
||||
self.inCallStatusBar?.updateState(statusBar: nil, withSafeInsets: false, inCallText: "In Call Text", animated: false)
|
||||
}
|
||||
|
||||
private func updateContainers(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
private weak var currentTopVisibleOverlayContainerStatusBar: NavigationOverlayContainer? = nil
|
||||
|
||||
private func updateContainers(layout rawLayout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
var layout = rawLayout
|
||||
|
||||
if self.ignoreInputHeight {
|
||||
if layout.inputHeight == nil {
|
||||
self.ignoreInputHeight = false
|
||||
} else {
|
||||
layout = layout.withUpdatedInputHeight(nil)
|
||||
}
|
||||
}
|
||||
|
||||
if let globalScrollToTopNode = self.globalScrollToTopNode {
|
||||
globalScrollToTopNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -1.0), size: CGSize(width: layout.size.width, height: 1.0))
|
||||
}
|
||||
|
||||
if let inCallStatusBar = self.inCallStatusBar {
|
||||
let inCallStatusBarFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: max(40.0, layout.safeInsets.top)))
|
||||
if inCallStatusBar.frame.isEmpty {
|
||||
inCallStatusBar.frame = inCallStatusBarFrame
|
||||
} else {
|
||||
transition.updateFrame(node: inCallStatusBar, frame: inCallStatusBarFrame)
|
||||
}
|
||||
layout.statusBarHeight = inCallStatusBarFrame.height
|
||||
self.inCallStatusBar?.frame = inCallStatusBarFrame
|
||||
}
|
||||
|
||||
let navigationLayout = makeNavigationLayout(mode: self.mode, layout: layout, controllers: self._viewControllers)
|
||||
|
||||
var transition = transition
|
||||
var statusBarStyle: StatusBarStyle = .Ignore
|
||||
var statusBarHidden = false
|
||||
var animateStatusBarStyleTransition = transition.isAnimated
|
||||
|
||||
var modalContainers: [NavigationModalContainer] = []
|
||||
|
|
@ -289,14 +326,16 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
strongSelf.updateContainers(layout: layout, transition: transition)
|
||||
}
|
||||
}
|
||||
modalContainer.interactivelyDismissed = { [weak self, weak modalContainer] in
|
||||
modalContainer.interactivelyDismissed = { [weak self, weak modalContainer] hadInputFocus in
|
||||
guard let strongSelf = self, let modalContainer = modalContainer else {
|
||||
return
|
||||
}
|
||||
let controllers = strongSelf._viewControllers.filter { controller in
|
||||
return !modalContainer.container.controllers.contains(where: { $0 === controller })
|
||||
}
|
||||
strongSelf.ignoreInputHeight = hadInputFocus
|
||||
strongSelf.setViewControllers(controllers, animated: false)
|
||||
strongSelf.ignoreInputHeight = false
|
||||
}
|
||||
}
|
||||
modalContainers.append(modalContainer)
|
||||
|
|
@ -304,13 +343,68 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
|
||||
for container in self.modalContainers {
|
||||
if !modalContainers.contains(where: { $0 === container }) {
|
||||
transition = container.dismiss(transition: transition, completion: { [weak container] in
|
||||
if viewTreeContainsFirstResponder(view: container.view) {
|
||||
self.ignoreInputHeight = true
|
||||
container.view.endEditing(true)
|
||||
}
|
||||
|
||||
transition = container.dismiss(transition: transition, completion: { [weak self, weak container] in
|
||||
container?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
self.modalContainers = modalContainers
|
||||
|
||||
var previousOverlayContainer: NavigationOverlayContainer?
|
||||
var topVisibleOverlayContainerWithStatusBar: NavigationOverlayContainer?
|
||||
for i in (0 ..< self.overlayContainers.count).reversed() {
|
||||
let overlayContainer = self.overlayContainers[i]
|
||||
|
||||
let containerTransition: ContainedViewLayoutTransition
|
||||
if overlayContainer.supernode == nil {
|
||||
containerTransition = .immediate
|
||||
} else {
|
||||
containerTransition = transition
|
||||
}
|
||||
|
||||
containerTransition.updateFrame(node: overlayContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
overlayContainer.update(layout: layout, transition: containerTransition)
|
||||
|
||||
if overlayContainer.supernode == nil && overlayContainer.isReady {
|
||||
if let previousOverlayContainer = previousOverlayContainer {
|
||||
self.displayNode.insertSubnode(overlayContainer, belowSubnode: previousOverlayContainer)
|
||||
} else if let globalScrollToTopNode = self.globalScrollToTopNode {
|
||||
self.displayNode.insertSubnode(overlayContainer, belowSubnode: globalScrollToTopNode)
|
||||
} else {
|
||||
self.displayNode.addSubnode(overlayContainer)
|
||||
}
|
||||
overlayContainer.transitionIn()
|
||||
}
|
||||
|
||||
if overlayContainer.supernode != nil {
|
||||
previousOverlayContainer = overlayContainer
|
||||
let controllerStatusBarStyle = overlayContainer.controller.statusBar.statusBarStyle
|
||||
switch controllerStatusBarStyle {
|
||||
case .Black, .White, .Hide:
|
||||
if topVisibleOverlayContainerWithStatusBar == nil {
|
||||
topVisibleOverlayContainerWithStatusBar = overlayContainer
|
||||
}
|
||||
if case .Hide = controllerStatusBarStyle {
|
||||
statusBarHidden = true
|
||||
} else {
|
||||
statusBarHidden = overlayContainer.controller.statusBar.alpha.isZero
|
||||
}
|
||||
case .Ignore:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.currentTopVisibleOverlayContainerStatusBar !== topVisibleOverlayContainerWithStatusBar {
|
||||
animateStatusBarStyleTransition = true
|
||||
self.currentTopVisibleOverlayContainerStatusBar = topVisibleOverlayContainerWithStatusBar
|
||||
}
|
||||
|
||||
var previousModalContainer: NavigationModalContainer?
|
||||
var visibleModalCount = 0
|
||||
var topModalDismissProgress: CGFloat = 0.0
|
||||
|
|
@ -342,6 +436,10 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
self.displayNode.insertSubnode(modalContainer, belowSubnode: previousModalContainer)
|
||||
} else if let inCallStatusBar = self.inCallStatusBar {
|
||||
self.displayNode.insertSubnode(modalContainer, belowSubnode: inCallStatusBar)
|
||||
} else if let previousOverlayContainer = previousOverlayContainer {
|
||||
self.displayNode.insertSubnode(modalContainer, belowSubnode: previousOverlayContainer)
|
||||
} else if let globalScrollToTopNode = self.globalScrollToTopNode {
|
||||
self.displayNode.insertSubnode(modalContainer, belowSubnode: globalScrollToTopNode)
|
||||
} else {
|
||||
self.displayNode.addSubnode(modalContainer)
|
||||
}
|
||||
|
|
@ -354,11 +452,14 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
topModalDismissProgress = modalContainer.dismissProgress
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
modalContainer.keyboardViewManager = self.keyboardViewManager
|
||||
modalContainer.canHaveKeyboardFocus = true
|
||||
} else {
|
||||
modalContainer.keyboardViewManager = nil
|
||||
modalContainer.canHaveKeyboardFocus = true
|
||||
}
|
||||
} else {
|
||||
modalContainer.keyboardViewManager = nil
|
||||
modalContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
previousModalContainer = modalContainer
|
||||
}
|
||||
|
|
@ -371,8 +472,10 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
case let .flat(flatContainer):
|
||||
if previousModalContainer == nil {
|
||||
flatContainer.keyboardViewManager = self.keyboardViewManager
|
||||
flatContainer.canHaveKeyboardFocus = true
|
||||
} else {
|
||||
flatContainer.keyboardViewManager = nil
|
||||
flatContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
transition.updateFrame(node: flatContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: transition)
|
||||
|
|
@ -386,6 +489,13 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
strongSelf.updateContainers(layout: layout, transition: transition)
|
||||
}
|
||||
if previousModalContainer == nil {
|
||||
flatContainer.keyboardViewManager = self.keyboardViewManager
|
||||
flatContainer.canHaveKeyboardFocus = true
|
||||
} else {
|
||||
flatContainer.keyboardViewManager = nil
|
||||
flatContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
self.displayNode.insertSubnode(flatContainer, at: 0)
|
||||
self.rootContainer = .flat(flatContainer)
|
||||
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
|
@ -402,6 +512,13 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
strongSelf.updateContainers(layout: layout, transition: transition)
|
||||
}
|
||||
if previousModalContainer == nil {
|
||||
flatContainer.keyboardViewManager = self.keyboardViewManager
|
||||
flatContainer.canHaveKeyboardFocus = true
|
||||
} else {
|
||||
flatContainer.keyboardViewManager = nil
|
||||
flatContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
self.displayNode.insertSubnode(flatContainer, at: 0)
|
||||
self.rootContainer = .flat(flatContainer)
|
||||
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
|
@ -413,23 +530,42 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
case let .flat(flatContainer):
|
||||
let splitContainer = NavigationSplitContainer(theme: self.theme, controllerRemoved: { [weak self] controller in
|
||||
self?.controllerRemoved(controller)
|
||||
}, scrollToTop: { [weak self] subject in
|
||||
self?.scrollToTop(subject)
|
||||
})
|
||||
self.displayNode.insertSubnode(splitContainer, at: 0)
|
||||
self.rootContainer = .split(splitContainer)
|
||||
if previousModalContainer == nil {
|
||||
splitContainer.canHaveKeyboardFocus = true
|
||||
} else {
|
||||
splitContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: .immediate)
|
||||
flatContainer.statusBarStyleUpdated = nil
|
||||
flatContainer.removeFromSupernode()
|
||||
case let .split(splitContainer):
|
||||
if previousModalContainer == nil {
|
||||
splitContainer.canHaveKeyboardFocus = true
|
||||
} else {
|
||||
splitContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
transition.updateFrame(node: splitContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: transition)
|
||||
}
|
||||
} else {
|
||||
let splitContainer = NavigationSplitContainer(theme: self.theme, controllerRemoved: { [weak self] controller in
|
||||
self?.controllerRemoved(controller)
|
||||
}, scrollToTop: { [weak self] subject in
|
||||
self?.scrollToTop(subject)
|
||||
})
|
||||
self.displayNode.insertSubnode(splitContainer, at: 0)
|
||||
self.rootContainer = .split(splitContainer)
|
||||
if previousModalContainer == nil {
|
||||
splitContainer.canHaveKeyboardFocus = true
|
||||
} else {
|
||||
splitContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: .immediate)
|
||||
}
|
||||
|
|
@ -439,8 +575,9 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
switch rootContainer {
|
||||
case let .flat(container):
|
||||
statusBarStyle = container.statusBarStyle
|
||||
self.globalScrollToTopNode?.isHidden = false
|
||||
case .split:
|
||||
break
|
||||
self.globalScrollToTopNode?.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -559,6 +696,18 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
}
|
||||
|
||||
if self.inCallStatusBar != nil {
|
||||
statusBarStyle = .White
|
||||
}
|
||||
|
||||
if let topVisibleOverlayContainerWithStatusBar = topVisibleOverlayContainerWithStatusBar {
|
||||
statusBarStyle = topVisibleOverlayContainerWithStatusBar.controller.statusBar.statusBarStyle
|
||||
}
|
||||
|
||||
if self.currentStatusBarExternalHidden {
|
||||
statusBarHidden = true
|
||||
}
|
||||
|
||||
let resolvedStatusBarStyle: NavigationStatusBarStyle
|
||||
switch statusBarStyle {
|
||||
case .Ignore, .Hide:
|
||||
|
|
@ -580,6 +729,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: animateStatusBarStyleTransition)
|
||||
}
|
||||
|
||||
if self.validStatusBarHidden != statusBarHidden {
|
||||
self.validStatusBarHidden = statusBarHidden
|
||||
self.statusBarHost?.setStatusBarHidden(statusBarHidden, animated: animateStatusBarStyleTransition)
|
||||
}
|
||||
}
|
||||
|
||||
private func controllerRemoved(_ controller: ViewController) {
|
||||
|
|
@ -589,6 +743,28 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
public func updateModalTransition(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
}
|
||||
|
||||
private func scrollToTop(_ subject: NavigationSplitContainerScrollToTop) {
|
||||
if let _ = self.inCallStatusBar {
|
||||
self.inCallNavigate?()
|
||||
} else if let rootContainer = self.rootContainer {
|
||||
if let modalContainer = self.modalContainers.last {
|
||||
modalContainer.container.controllers.last?.scrollToTop?()
|
||||
} else {
|
||||
switch rootContainer {
|
||||
case let .flat(container):
|
||||
container.controllers.last?.scrollToTop?()
|
||||
case let .split(container):
|
||||
switch subject {
|
||||
case .master:
|
||||
container.masterControllers.last?.scrollToTop?()
|
||||
case .detail:
|
||||
container.detailControllers.last?.scrollToTop?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateToInterfaceOrientation(_ orientation: UIInterfaceOrientation) {
|
||||
/*for record in self._viewControllers {
|
||||
if let controller = record.controller as? ContainableController {
|
||||
|
|
@ -613,9 +789,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
self.navigationBar.removeFromSuperview()
|
||||
|
||||
/*let inCallStatusBar = StatusBar()
|
||||
self.displayNode.addSubnode(inCallStatusBar)
|
||||
self.inCallStatusBar = inCallStatusBar*/
|
||||
let globalScrollToTopNode = ScrollToTopNode(action: { [weak self] in
|
||||
self?.scrollToTop(.master)
|
||||
})
|
||||
self.displayNode.addSubnode(globalScrollToTopNode)
|
||||
self.globalScrollToTopNode = globalScrollToTopNode
|
||||
}
|
||||
|
||||
public func pushViewController(_ controller: ViewController) {
|
||||
|
|
@ -663,7 +841,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
public func filterController(_ controller: ViewController, animated: Bool) {
|
||||
let controllers = self.viewControllers.filter({ $0 !== controller })
|
||||
if controllers.count != self.viewControllers.count {
|
||||
if controller.isViewLoaded && viewTreeContainsFirstResponder(view: controller.view) {
|
||||
self.ignoreInputHeight = true
|
||||
}
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
self.ignoreInputHeight = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -750,6 +932,53 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
}
|
||||
|
||||
public func presentOverlay(controller: ViewController, inGlobal: Bool = false) {
|
||||
let container = NavigationOverlayContainer(controller: controller, controllerRemoved: { [weak self] controller in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
for i in 0 ..< strongSelf.overlayContainers.count {
|
||||
let overlayContainer = strongSelf.overlayContainers[i]
|
||||
if overlayContainer.controller === controller {
|
||||
overlayContainer.removeFromSupernode()
|
||||
strongSelf.overlayContainers.remove(at: i)
|
||||
break
|
||||
}
|
||||
}
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.updateContainers(layout: layout, transition: .immediate)
|
||||
}
|
||||
}, statusBarUpdated: { [weak self] transition in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.updateContainers(layout: layout, transition: transition)
|
||||
}
|
||||
})
|
||||
self.overlayContainers.append(container)
|
||||
container.isReadyUpdated = { [weak self, weak container] in
|
||||
guard let strongSelf = self, let container = container else {
|
||||
return
|
||||
}
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.updateContainers(layout: layout, transition: .immediate)
|
||||
}
|
||||
}
|
||||
if let layout = self.validLayout {
|
||||
self.updateContainers(layout: layout, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func updateExternalStatusBarHidden(_ value: Bool, transition: ContainedViewLayoutTransition) {
|
||||
if self.currentStatusBarExternalHidden != value {
|
||||
self.currentStatusBarExternalHidden = value
|
||||
if let layout = self.validLayout {
|
||||
self.updateContainers(layout: layout, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
|
@ -816,4 +1045,49 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
self.containerLayoutUpdated(validLayout, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public func setForceInCallStatusBar(_ forceInCallStatusBarText: String?, transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)) {
|
||||
if let forceInCallStatusBarText = forceInCallStatusBarText {
|
||||
let inCallStatusBar: StatusBar
|
||||
if let current = self.inCallStatusBar {
|
||||
inCallStatusBar = current
|
||||
} else {
|
||||
inCallStatusBar = StatusBar()
|
||||
inCallStatusBar.inCallNavigate = { [weak self] in
|
||||
self?.scrollToTop(.master)
|
||||
}
|
||||
inCallStatusBar.alpha = 0.0
|
||||
self.inCallStatusBar = inCallStatusBar
|
||||
|
||||
var bottomOverlayContainer: NavigationOverlayContainer?
|
||||
for overlayContainer in self.overlayContainers {
|
||||
if overlayContainer.supernode != nil {
|
||||
bottomOverlayContainer = overlayContainer
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let bottomOverlayContainer = bottomOverlayContainer {
|
||||
self.displayNode.insertSubnode(inCallStatusBar, belowSubnode: bottomOverlayContainer)
|
||||
} else if let globalScrollToTopNode = self.globalScrollToTopNode {
|
||||
self.displayNode.insertSubnode(inCallStatusBar, belowSubnode: globalScrollToTopNode)
|
||||
} else {
|
||||
self.displayNode.addSubnode(inCallStatusBar)
|
||||
}
|
||||
transition.updateAlpha(node: inCallStatusBar, alpha: 1.0)
|
||||
}
|
||||
if let layout = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, transition: transition)
|
||||
inCallStatusBar.updateState(statusBar: nil, withSafeInsets: !layout.safeInsets.top.isZero, inCallText: forceInCallStatusBarText, animated: false)
|
||||
}
|
||||
} else if let inCallStatusBar = self.inCallStatusBar {
|
||||
self.inCallStatusBar = nil
|
||||
transition.updateAlpha(node: inCallStatusBar, alpha: 0.0, completion: { [weak inCallStatusBar] _ in
|
||||
inCallStatusBar?.removeFromSupernode()
|
||||
})
|
||||
if let layout = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
|
|||
private(set) var dismissProgress: CGFloat = 0.0
|
||||
var isReadyUpdated: (() -> Void)?
|
||||
var updateDismissProgress: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
var interactivelyDismissed: (() -> Void)?
|
||||
var interactivelyDismissed: ((Bool) -> Void)?
|
||||
|
||||
private var isUpdatingState = false
|
||||
private var ignoreScrolling = false
|
||||
private var isDismissed = false
|
||||
private var isInteractiveDimissEnabled = true
|
||||
|
|
@ -33,6 +34,12 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
|
|||
}
|
||||
}
|
||||
|
||||
var canHaveKeyboardFocus: Bool = false {
|
||||
didSet {
|
||||
self.container.canHaveKeyboardFocus = self.canHaveKeyboardFocus
|
||||
}
|
||||
}
|
||||
|
||||
init(theme: NavigationControllerTheme, controllerRemoved: @escaping (ViewController) -> Void) {
|
||||
self.theme = theme
|
||||
|
||||
|
|
@ -57,7 +64,9 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
|
|||
}
|
||||
if !strongSelf.isReady {
|
||||
strongSelf.isReady = true
|
||||
strongSelf.isReadyUpdated?()
|
||||
if !strongSelf.isUpdatingState {
|
||||
strongSelf.isReadyUpdated?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,8 +155,9 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
|
|||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let hadInputFocus = viewTreeContainsFirstResponder(view: strongSelf.view)
|
||||
strongSelf.keyboardViewManager?.dismissEditingWithoutAnimation(view: strongSelf.view)
|
||||
strongSelf.interactivelyDismissed?()
|
||||
strongSelf.interactivelyDismissed?(hadInputFocus)
|
||||
})
|
||||
} else {
|
||||
self.horizontalDismissOffset = nil
|
||||
|
|
@ -188,13 +198,15 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
|
|||
return
|
||||
}
|
||||
if targetOffset == 0.0 {
|
||||
strongSelf.interactivelyDismissed?()
|
||||
strongSelf.interactivelyDismissed?(false)
|
||||
}
|
||||
})
|
||||
self.ignoreScrolling = false
|
||||
self.dismissProgress = dismissProgress
|
||||
|
||||
self.applyDismissProgress(transition: transition, completion: {})
|
||||
|
||||
self.view.endEditing(true)
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
|
|
@ -247,7 +259,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
|
|||
return
|
||||
}
|
||||
if targetOffset == 0.0 {
|
||||
strongSelf.interactivelyDismissed?()
|
||||
strongSelf.interactivelyDismissed?(false)
|
||||
}
|
||||
})
|
||||
self.ignoreScrolling = false
|
||||
|
|
@ -273,6 +285,8 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
|
|||
return
|
||||
}
|
||||
|
||||
self.isUpdatingState = true
|
||||
|
||||
self.validLayout = layout
|
||||
|
||||
transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
|
@ -338,6 +352,8 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
|
|||
transition.updateFrameAsPositionAndBounds(node: self.container, frame: containerFrame.offsetBy(dx: 0.0, dy: layout.size.height))
|
||||
transition.updateTransformScale(node: self.container, scale: containerScale)
|
||||
self.container.update(layout: containerLayout, canBeClosed: true, controllers: controllers, transition: transition)
|
||||
|
||||
self.isUpdatingState = false
|
||||
}
|
||||
|
||||
func animateIn(transition: ContainedViewLayoutTransition) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
final class NavigationOverlayContainer: ASDisplayNode {
|
||||
let controller: ViewController
|
||||
|
||||
private(set) var isReady: Bool = false
|
||||
var isReadyUpdated: (() -> Void)?
|
||||
private var isReadyDisposable: Disposable?
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private var isUpdatingState: Bool = false
|
||||
|
||||
var keyboardViewManager: KeyboardViewManager? {
|
||||
didSet {
|
||||
if self.keyboardViewManager !== oldValue {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(controller: ViewController, controllerRemoved: @escaping (ViewController) -> Void, statusBarUpdated: @escaping (ContainedViewLayoutTransition) -> Void) {
|
||||
self.controller = controller
|
||||
|
||||
super.init()
|
||||
|
||||
self.controller.navigation_setDismiss({ [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
controllerRemoved(strongSelf.controller)
|
||||
}, rootController: nil)
|
||||
|
||||
self.controller.statusBar.alphaUpdated = { transition in
|
||||
statusBarUpdated(transition)
|
||||
}
|
||||
|
||||
self.isReadyDisposable = (self.controller.ready.get()
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if !strongSelf.isReady {
|
||||
strongSelf.isReady = true
|
||||
if !strongSelf.isUpdatingState {
|
||||
strongSelf.isReadyUpdated?()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.isReadyDisposable?.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.isUpdatingState = true
|
||||
|
||||
let updateLayout = self.validLayout != layout
|
||||
|
||||
self.validLayout = layout
|
||||
|
||||
if updateLayout {
|
||||
transition.updateFrame(node: self.controller.displayNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
self.controller.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
|
||||
self.isUpdatingState = false
|
||||
}
|
||||
|
||||
func transitionIn() {
|
||||
self.controller.viewWillAppear(false)
|
||||
self.controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
self.addSubnode(self.controller.displayNode)
|
||||
self.controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
self.controller.viewDidAppear(false)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,19 +3,42 @@ import UIKit
|
|||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
enum NavigationSplitContainerScrollToTop {
|
||||
case master
|
||||
case detail
|
||||
}
|
||||
|
||||
final class NavigationSplitContainer: ASDisplayNode {
|
||||
private var theme: NavigationControllerTheme
|
||||
|
||||
private let masterScrollToTopView: ScrollToTopView
|
||||
private let detailScrollToTopView: ScrollToTopView
|
||||
private let masterContainer: NavigationContainer
|
||||
private let detailContainer: NavigationContainer
|
||||
private let separator: ASDisplayNode
|
||||
|
||||
private var masterControllers: [ViewController] = []
|
||||
private var detailControllers: [ViewController] = []
|
||||
private(set) var masterControllers: [ViewController] = []
|
||||
private(set) var detailControllers: [ViewController] = []
|
||||
|
||||
init(theme: NavigationControllerTheme, controllerRemoved: @escaping (ViewController) -> Void) {
|
||||
var canHaveKeyboardFocus: Bool = false {
|
||||
didSet {
|
||||
self.masterContainer.canHaveKeyboardFocus = self.canHaveKeyboardFocus
|
||||
self.detailContainer.canHaveKeyboardFocus = self.canHaveKeyboardFocus
|
||||
}
|
||||
}
|
||||
|
||||
init(theme: NavigationControllerTheme, controllerRemoved: @escaping (ViewController) -> Void, scrollToTop: @escaping (NavigationSplitContainerScrollToTop) -> Void) {
|
||||
self.theme = theme
|
||||
|
||||
self.masterScrollToTopView = ScrollToTopView(frame: CGRect())
|
||||
self.masterScrollToTopView.action = {
|
||||
scrollToTop(.master)
|
||||
}
|
||||
self.detailScrollToTopView = ScrollToTopView(frame: CGRect())
|
||||
self.detailScrollToTopView.action = {
|
||||
scrollToTop(.detail)
|
||||
}
|
||||
|
||||
self.masterContainer = NavigationContainer(controllerRemoved: controllerRemoved)
|
||||
self.masterContainer.clipsToBounds = true
|
||||
|
||||
|
|
@ -30,6 +53,8 @@ final class NavigationSplitContainer: ASDisplayNode {
|
|||
self.addSubnode(self.masterContainer)
|
||||
self.addSubnode(self.detailContainer)
|
||||
self.addSubnode(self.separator)
|
||||
self.view.addSubview(self.masterScrollToTopView)
|
||||
self.view.addSubview(self.detailScrollToTopView)
|
||||
}
|
||||
|
||||
func updateTheme(theme: NavigationControllerTheme) {
|
||||
|
|
@ -40,6 +65,9 @@ final class NavigationSplitContainer: ASDisplayNode {
|
|||
let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
|
||||
let detailWidth = layout.size.width - masterWidth
|
||||
|
||||
self.masterScrollToTopView.frame = CGRect(origin: CGPoint(x: 0.0, y: -1.0), size: CGSize(width: masterWidth, height: 1.0))
|
||||
self.detailScrollToTopView.frame = CGRect(origin: CGPoint(x: masterWidth, y: -1.0), size: CGSize(width: detailWidth, height: 1.0))
|
||||
|
||||
transition.updateFrame(node: self.masterContainer, frame: CGRect(origin: CGPoint(), size: CGSize(width: masterWidth, height: layout.size.height)))
|
||||
transition.updateFrame(node: self.detailContainer, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height)))
|
||||
transition.updateFrame(node: self.separator, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height)))
|
||||
|
|
|
|||
|
|
@ -35,3 +35,15 @@ class ScrollToTopView: UIScrollView, UIScrollViewDelegate {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class ScrollToTopNode: ASDisplayNode {
|
||||
init(action: @escaping () -> Void) {
|
||||
super.init()
|
||||
|
||||
self.setViewBlock({
|
||||
let view = ScrollToTopView(frame: CGRect())
|
||||
view.action = action
|
||||
return view
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ public final class StatusBar: ASDisplayNode {
|
|||
didSet {
|
||||
if self.statusBarStyle != oldValue {
|
||||
self.layer.invalidateUpTheTree()
|
||||
self.alphaUpdated?(.immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -94,6 +95,13 @@ public final class StatusBar: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
var alphaUpdated: ((ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
public func updateAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.alpha = alpha
|
||||
self.alphaUpdated?(transition)
|
||||
}
|
||||
|
||||
public override init() {
|
||||
self.inCallLabel = StatusBarLabelNode()
|
||||
self.inCallLabel.isUserInteractionEnabled = false
|
||||
|
|
|
|||
|
|
@ -12,5 +12,8 @@ public protocol StatusBarHost {
|
|||
|
||||
var handleVolumeControl: Signal<Bool, NoError> { get }
|
||||
|
||||
var isApplicationInForeground: Bool { get }
|
||||
|
||||
func setStatusBarStyle(_ style: UIStatusBarStyle, animated: Bool)
|
||||
func setStatusBarHidden(_ value: Bool, animated: Bool)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,15 @@ open class TabBarController: ViewController {
|
|||
self.theme = theme
|
||||
|
||||
super.init(navigationBarPresentationData: navigationBarPresentationData)
|
||||
|
||||
self.scrollToTop = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let controller = strongSelf.currentController {
|
||||
controller.scrollToTop?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ public enum ViewControllerNavigationPresentation {
|
|||
}
|
||||
|
||||
private func updateScrollToTopView() {
|
||||
if self.scrollToTop != nil {
|
||||
/*if self.scrollToTop != nil {
|
||||
if let displayNode = self._displayNode , self.scrollToTopView == nil {
|
||||
let scrollToTopView = ScrollToTopView(frame: CGRect(x: 0.0, y: -1.0, width: displayNode.bounds.size.width, height: 1.0))
|
||||
scrollToTopView.action = { [weak self] in
|
||||
|
|
@ -257,7 +257,7 @@ public enum ViewControllerNavigationPresentation {
|
|||
self.scrollToTopView = scrollToTopView
|
||||
self.view.addSubview(scrollToTopView)
|
||||
}
|
||||
} else if let scrollToTopView = self.scrollToTopView {
|
||||
} else*/ if let scrollToTopView = self.scrollToTopView {
|
||||
scrollToTopView.removeFromSuperview()
|
||||
self.scrollToTopView = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ public class Window1 {
|
|||
private var deviceMetrics: DeviceMetrics
|
||||
|
||||
private let statusBarHost: StatusBarHost?
|
||||
private let statusBarManager: StatusBarManager?
|
||||
//private let statusBarManager: StatusBarManager?
|
||||
private let keyboardManager: KeyboardManager?
|
||||
private let keyboardViewManager: KeyboardViewManager?
|
||||
private var statusBarChangeObserver: AnyObject?
|
||||
|
|
@ -312,7 +312,7 @@ public class Window1 {
|
|||
public private(set) var forceInCallStatusBarText: String? = nil
|
||||
public var inCallNavigate: (() -> Void)? {
|
||||
didSet {
|
||||
self.statusBarManager?.inCallNavigate = self.inCallNavigate
|
||||
//self.statusBarManager?.inCallNavigate = self.inCallNavigate
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -323,21 +323,17 @@ public class Window1 {
|
|||
|
||||
private var keyboardTypeChangeTimer: SwiftSignalKit.Timer?
|
||||
|
||||
private let volumeControlStatusBar: VolumeControlStatusBar
|
||||
private let volumeControlStatusBarNode: VolumeControlStatusBarNode
|
||||
//private let volumeControlStatusBar: VolumeControlStatusBar
|
||||
//private let volumeControlStatusBarNode: VolumeControlStatusBarNode
|
||||
|
||||
private var isInteractionBlocked = false
|
||||
|
||||
/*private var accessibilityElements: [Any]? {
|
||||
return self.viewController?.view.accessibilityElements
|
||||
}*/
|
||||
|
||||
public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) {
|
||||
self.hostView = hostView
|
||||
|
||||
self.volumeControlStatusBar = VolumeControlStatusBar(frame: CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: 100.0, height: 20.0)), shouldBeVisible: statusBarHost?.handleVolumeControl ?? .single(false))
|
||||
self.volumeControlStatusBarNode = VolumeControlStatusBarNode()
|
||||
self.volumeControlStatusBarNode.isHidden = true
|
||||
//self.volumeControlStatusBar = VolumeControlStatusBar(frame: CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: 100.0, height: 20.0)), shouldBeVisible: statusBarHost?.handleVolumeControl ?? .single(false))
|
||||
//self.volumeControlStatusBarNode = VolumeControlStatusBarNode()
|
||||
//self.volumeControlStatusBarNode.isHidden = true
|
||||
|
||||
let boundsSize = self.hostView.eventView.bounds.size
|
||||
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHost?.statusBarFrame.height ?? defaultStatusBarHeight, onScreenNavigationHeight: self.hostView.onScreenNavigationHeight)
|
||||
|
|
@ -346,12 +342,12 @@ public class Window1 {
|
|||
let statusBarHeight: CGFloat
|
||||
if let statusBarHost = statusBarHost {
|
||||
statusBarHeight = statusBarHost.statusBarFrame.size.height
|
||||
self.statusBarManager = StatusBarManager(host: statusBarHost, volumeControlStatusBar: self.volumeControlStatusBar, volumeControlStatusBarNode: self.volumeControlStatusBarNode)
|
||||
//self.statusBarManager = StatusBarManager(host: statusBarHost, volumeControlStatusBar: self.volumeControlStatusBar, volumeControlStatusBarNode: self.volumeControlStatusBarNode)
|
||||
self.keyboardManager = KeyboardManager(host: statusBarHost)
|
||||
self.keyboardViewManager = KeyboardViewManager(host: statusBarHost)
|
||||
} else {
|
||||
statusBarHeight = self.deviceMetrics.statusBarHeight
|
||||
self.statusBarManager = nil
|
||||
//self.statusBarManager = nil
|
||||
self.keyboardManager = nil
|
||||
self.keyboardViewManager = nil
|
||||
}
|
||||
|
|
@ -394,7 +390,7 @@ public class Window1 {
|
|||
}
|
||||
|
||||
self.hostView.layoutSubviews = { [weak self] in
|
||||
self?.layoutSubviews()
|
||||
self?.layoutSubviews(force: false)
|
||||
}
|
||||
|
||||
self.hostView.updateToInterfaceOrientation = { [weak self] orientation in
|
||||
|
|
@ -433,7 +429,7 @@ public class Window1 {
|
|||
}*/
|
||||
|
||||
self.presentationContext.view = self.hostView.containerView
|
||||
self.presentationContext.volumeControlStatusBarNodeView = self.volumeControlStatusBarNode.view
|
||||
//self.presentationContext.volumeControlStatusBarNodeView = self.volumeControlStatusBarNode.view
|
||||
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate)
|
||||
self.overlayPresentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate)
|
||||
|
||||
|
|
@ -455,7 +451,7 @@ public class Window1 {
|
|||
keyboardHeight = max(0.0, keyboardHeight - 24.0)
|
||||
}
|
||||
|
||||
print("keyboardHeight: \(keyboardHeight)")
|
||||
//print("rotation keyboardHeight: \(keyboardHeight)")
|
||||
|
||||
var duration: Double = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0.0
|
||||
if duration > Double.ulpOfOne {
|
||||
|
|
@ -483,12 +479,33 @@ public class Window1 {
|
|||
}
|
||||
}
|
||||
|
||||
var popoverDelta: CGFloat = 0.0
|
||||
|
||||
let screenHeight: CGFloat
|
||||
var inPopover = false
|
||||
if keyboardFrame.width.isEqual(to: UIScreen.main.bounds.width) {
|
||||
var portraitScreenSize = UIScreen.main.bounds.size
|
||||
if portraitScreenSize.width > portraitScreenSize.height {
|
||||
portraitScreenSize = CGSize(width: portraitScreenSize.height, height: portraitScreenSize.width)
|
||||
}
|
||||
var portraitLayoutSize = strongSelf.windowLayout.size
|
||||
if portraitLayoutSize.width > portraitLayoutSize.height {
|
||||
portraitLayoutSize = CGSize(width: portraitLayoutSize.height, height: portraitLayoutSize.width)
|
||||
}
|
||||
if abs(strongSelf.windowLayout.size.height - UIScreen.main.bounds.height) > 41.0 {
|
||||
if abs(portraitLayoutSize.height - portraitScreenSize.height) > 41.0 || abs(portraitLayoutSize.width - portraitScreenSize.width) > 41.0 {
|
||||
popoverDelta = 48.0
|
||||
inPopover = true
|
||||
screenHeight = strongSelf.windowLayout.size.height
|
||||
} else {
|
||||
screenHeight = UIScreen.main.bounds.height
|
||||
}
|
||||
} else if abs(strongSelf.windowLayout.size.height - UIScreen.main.bounds.height) > 39.0 {
|
||||
screenHeight = UIScreen.main.bounds.height
|
||||
inPopover = true
|
||||
if abs(portraitLayoutSize.height - portraitScreenSize.height) > 39.0 || abs(portraitLayoutSize.width - portraitScreenSize.width) > 39.0 {
|
||||
popoverDelta = 40.0
|
||||
inPopover = true
|
||||
}
|
||||
} else {
|
||||
screenHeight = strongSelf.windowLayout.size.height
|
||||
}
|
||||
|
|
@ -503,14 +520,16 @@ public class Window1 {
|
|||
keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY)
|
||||
if inPopover {
|
||||
if strongSelf.windowLayout.onScreenNavigationHeight != nil {
|
||||
keyboardHeight = max(0.0, keyboardHeight - 24.0)
|
||||
if !keyboardHeight.isZero {
|
||||
keyboardHeight = max(0.0, keyboardHeight + popoverDelta / 2.0)
|
||||
}
|
||||
} else {
|
||||
keyboardHeight = max(0.0, keyboardHeight - 48.0)
|
||||
keyboardHeight = max(0.0, keyboardHeight - popoverDelta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("keyboardHeight: \(keyboardHeight)")
|
||||
print("keyboardHeight: \(keyboardHeight) (raw: \(keyboardFrame))")
|
||||
|
||||
var duration: Double = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0.0
|
||||
if duration > Double.ulpOfOne {
|
||||
|
|
@ -586,8 +605,8 @@ public class Window1 {
|
|||
self.windowPanRecognizer = recognizer
|
||||
self.hostView.containerView.addGestureRecognizer(recognizer)
|
||||
|
||||
self.hostView.containerView.addSubview(self.volumeControlStatusBar)
|
||||
self.hostView.containerView.addSubview(self.volumeControlStatusBarNode.view)
|
||||
//self.hostView.containerView.addSubview(self.volumeControlStatusBar)
|
||||
//self.hostView.containerView.addSubview(self.volumeControlStatusBarNode.view)
|
||||
}
|
||||
|
||||
public required init(coder aDecoder: NSCoder) {
|
||||
|
|
@ -613,7 +632,7 @@ public class Window1 {
|
|||
}
|
||||
|
||||
public func setupVolumeControlStatusBarGraphics(_ graphics: (UIImage, UIImage, UIImage)) {
|
||||
self.volumeControlStatusBarNode.graphics = graphics
|
||||
//self.volumeControlStatusBarNode.graphics = graphics
|
||||
}
|
||||
|
||||
public func setForceInCallStatusBar(_ forceInCallStatusBarText: String?, transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)) {
|
||||
|
|
@ -701,6 +720,9 @@ public class Window1 {
|
|||
transition = .immediate
|
||||
}
|
||||
self.updateLayout { $0.update(size: value, metrics: layoutMetricsForScreenSize(value), safeInsets: self.deviceMetrics.safeInsets(inLandscape: value.width > value.height), forceInCallStatusBarText: self.forceInCallStatusBarText, transition: transition, overrideTransition: true) }
|
||||
if let statusBarHost = self.statusBarHost, !statusBarHost.isApplicationInForeground {
|
||||
self.layoutSubviews(force: true)
|
||||
}
|
||||
}
|
||||
|
||||
private var _rootController: ContainableController?
|
||||
|
|
@ -718,6 +740,9 @@ public class Window1 {
|
|||
if let rootController = rootController as? NavigationController {
|
||||
rootController.statusBarHost = self.statusBarHost
|
||||
rootController.keyboardViewManager = self.keyboardViewManager
|
||||
rootController.inCallNavigate = { [weak self] in
|
||||
self?.inCallNavigate?()
|
||||
}
|
||||
}
|
||||
if !self.windowLayout.size.width.isZero && !self.windowLayout.size.height.isZero {
|
||||
rootController.displayNode.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size)
|
||||
|
|
@ -738,6 +763,9 @@ public class Window1 {
|
|||
}
|
||||
set(value) {
|
||||
for controller in self._topLevelOverlayControllers {
|
||||
if let controller = controller as? ViewController {
|
||||
controller.statusBar.alphaUpdated = nil
|
||||
}
|
||||
controller.view.removeFromSuperview()
|
||||
}
|
||||
self._topLevelOverlayControllers = value
|
||||
|
|
@ -750,7 +778,25 @@ public class Window1 {
|
|||
if let coveringView = self.coveringView {
|
||||
self.hostView.containerView.insertSubview(controller.view, belowSubview: coveringView)
|
||||
} else {
|
||||
self.hostView.containerView.insertSubview(controller.view, belowSubview: self.volumeControlStatusBarNode.view)
|
||||
self.hostView.containerView.addSubview(controller.view)
|
||||
}
|
||||
|
||||
if let controller = controller as? ViewController {
|
||||
controller.statusBar.alphaUpdated = { [weak self] transition in
|
||||
guard let strongSelf = self, let navigationController = strongSelf._rootController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
var isStatusBarHidden: Bool = false
|
||||
for controller in strongSelf._topLevelOverlayControllers {
|
||||
if let controller = controller as? ViewController {
|
||||
if case .Hide = controller.statusBar.statusBarStyle {
|
||||
isStatusBarHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
navigationController.updateExternalStatusBarHidden(isStatusBarHidden, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -771,7 +817,7 @@ public class Window1 {
|
|||
coveringView.layer.removeAnimation(forKey: "opacity")
|
||||
coveringView.layer.allowsGroupOpacity = false
|
||||
coveringView.alpha = 1.0
|
||||
self.hostView.containerView.insertSubview(coveringView, belowSubview: self.volumeControlStatusBarNode.view)
|
||||
self.hostView.containerView.addSubview(coveringView)
|
||||
if !self.windowLayout.size.width.isZero {
|
||||
coveringView.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size)
|
||||
coveringView.updateLayout(self.windowLayout.size)
|
||||
|
|
@ -781,7 +827,7 @@ public class Window1 {
|
|||
}
|
||||
}
|
||||
|
||||
private func layoutSubviews() {
|
||||
private func layoutSubviews(force: Bool) {
|
||||
var hasPreview = false
|
||||
var updatedHasPreview = false
|
||||
for subview in self.hostView.eventView.subviews {
|
||||
|
|
@ -796,7 +842,7 @@ public class Window1 {
|
|||
updatedHasPreview = true
|
||||
}
|
||||
|
||||
if self.tracingStatusBarsInvalidated || updatedHasPreview, let statusBarManager = statusBarManager, let keyboardManager = keyboardManager {
|
||||
if self.tracingStatusBarsInvalidated || updatedHasPreview, let keyboardManager = keyboardManager {
|
||||
self.tracingStatusBarsInvalidated = false
|
||||
|
||||
/*if self.statusBarHidden {
|
||||
|
|
@ -902,7 +948,9 @@ public class Window1 {
|
|||
}
|
||||
}
|
||||
|
||||
if !UIWindow.isDeviceRotating() {
|
||||
if force {
|
||||
self.commitUpdatingLayout()
|
||||
} else if !UIWindow.isDeviceRotating() {
|
||||
if !self.hostView.isUpdatingOrientationLayout {
|
||||
self.commitUpdatingLayout()
|
||||
} else {
|
||||
|
|
@ -967,6 +1015,9 @@ public class Window1 {
|
|||
let boundsSize = updatingLayout.layout.size
|
||||
let isLandscape = boundsSize.width > boundsSize.height
|
||||
var statusBarHeight: CGFloat? = self.deviceMetrics.statusBarHeight(for: boundsSize)
|
||||
if let statusBarHeightValue = statusBarHeight, let statusBarHost = self.statusBarHost {
|
||||
statusBarHeight = max(statusBarHeightValue, statusBarHost.statusBarFrame.size.height)
|
||||
}
|
||||
|
||||
if self.deviceMetrics.type == .tablet, let onScreenNavigationHeight = self.hostView.onScreenNavigationHeight, onScreenNavigationHeight != self.deviceMetrics.onScreenNavigationHeight(inLandscape: false) {
|
||||
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHeight ?? defaultStatusBarHeight, onScreenNavigationHeight: onScreenNavigationHeight)
|
||||
|
|
@ -1023,8 +1074,8 @@ public class Window1 {
|
|||
})
|
||||
}
|
||||
|
||||
self.volumeControlStatusBarNode.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size)
|
||||
self.volumeControlStatusBarNode.updateLayout(layout: childLayout, transition: updatingLayout.transition)
|
||||
//self.volumeControlStatusBarNode.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size)
|
||||
//self.volumeControlStatusBarNode.updateLayout(layout: childLayout, transition: updatingLayout.transition)
|
||||
|
||||
if let coveringView = self.coveringView {
|
||||
coveringView.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size)
|
||||
|
|
@ -1035,7 +1086,11 @@ public class Window1 {
|
|||
}
|
||||
|
||||
public func present(_ controller: ContainableController, on level: PresentationSurfaceLevel, blockInteraction: Bool = false, completion: @escaping () -> Void = {}) {
|
||||
self.presentationContext.present(controller, on: level, blockInteraction: blockInteraction, completion: completion)
|
||||
if level.rawValue <= 3, let navigationController = self._rootController as? NavigationController, let controller = controller as? ViewController {
|
||||
navigationController.presentOverlay(controller: controller, inGlobal: false)
|
||||
} else {
|
||||
self.presentationContext.present(controller, on: level, blockInteraction: blockInteraction, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
public func presentInGlobalOverlay(_ controller: ContainableController) {
|
||||
|
|
|
|||
|
|
@ -275,14 +275,14 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
|
|||
UIView.animate(withDuration: 0.3, animations: {
|
||||
let alpha: CGFloat = self.areControlsHidden ? 0.0 : 1.0
|
||||
self.navigationBar?.alpha = alpha
|
||||
self.statusBar?.alpha = alpha
|
||||
self.statusBar?.updateAlpha(alpha, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
self.footerNode.alpha = alpha
|
||||
self.updateThumbnailContainerNodeAlpha(.immediate)
|
||||
})
|
||||
} else {
|
||||
let alpha: CGFloat = self.areControlsHidden ? 0.0 : 1.0
|
||||
self.navigationBar?.alpha = alpha
|
||||
self.statusBar?.alpha = alpha
|
||||
self.statusBar?.updateAlpha(alpha, transition: .immediate)
|
||||
self.footerNode.alpha = alpha
|
||||
self.updateThumbnailContainerNodeAlpha(.immediate)
|
||||
}
|
||||
|
|
@ -374,7 +374,11 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
|
|||
self.updateThumbnailContainerNodeAlpha(.immediate)
|
||||
|
||||
if !self.areControlsHidden {
|
||||
self.statusBar?.alpha = transition
|
||||
if transition < 0.5 {
|
||||
self.statusBar?.statusBarStyle = .Ignore
|
||||
} else {
|
||||
self.statusBar?.statusBarStyle = .White
|
||||
}
|
||||
self.navigationBar?.alpha = transition
|
||||
self.footerNode.alpha = transition
|
||||
|
||||
|
|
|
|||
|
|
@ -52,8 +52,6 @@ final class InstantPageReferenceController: ViewController {
|
|||
|
||||
override public func loadView() {
|
||||
super.loadView()
|
||||
|
||||
self.statusBar.removeFromSupernode()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ public final class JoinLinkPreviewController: ViewController {
|
|||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
|
@ -76,8 +78,6 @@ public final class JoinLinkPreviewController: ViewController {
|
|||
|
||||
override public func loadView() {
|
||||
super.loadView()
|
||||
|
||||
self.statusBar.removeFromSupernode()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ public final class LanguageLinkPreviewController: ViewController {
|
|||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
|
@ -85,8 +87,6 @@ public final class LanguageLinkPreviewController: ViewController {
|
|||
|
||||
override public func loadView() {
|
||||
super.loadView()
|
||||
|
||||
self.statusBar.removeFromSupernode()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
|
|
|
|||
|
|
@ -739,7 +739,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
|||
|
||||
[UIView animateWithDuration:0.3f animations:^
|
||||
{
|
||||
[_context setApplicationStatusBarAlpha:1.0f];
|
||||
//[_context setApplicationStatusBarAlpha:1.0f];
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
@ -1816,7 +1816,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
|||
|
||||
[UIView animateWithDuration:0.3f animations:^
|
||||
{
|
||||
[_context setApplicationStatusBarAlpha:1.0f];
|
||||
//[_context setApplicationStatusBarAlpha:1.0f];
|
||||
}];
|
||||
|
||||
[self setInterfaceHidden:true animated:true];
|
||||
|
|
@ -1892,7 +1892,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
|||
{
|
||||
_finishedWithResult = true;
|
||||
|
||||
[_context setApplicationStatusBarAlpha:1.0f];
|
||||
//[_context setApplicationStatusBarAlpha:1.0f];
|
||||
|
||||
self.view.hidden = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -93,13 +93,15 @@
|
|||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
if (!TGIsPad() && iosMajorVersion() >= 11 && UIInterfaceOrientationIsLandscape([[LegacyComponentsGlobals provider] applicationStatusBarOrientation]))
|
||||
return true;
|
||||
|
||||
/*if (!TGIsPad() && iosMajorVersion() >= 11 && UIInterfaceOrientationIsLandscape([[LegacyComponentsGlobals provider] applicationStatusBarOrientation]))
|
||||
return true;
|
||||
|
||||
if (self.childViewControllers.count > 0)
|
||||
return [self.childViewControllers.lastObject prefersStatusBarHidden];
|
||||
|
||||
return [super prefersStatusBarHidden];
|
||||
return [super prefersStatusBarHidden];*/
|
||||
}
|
||||
|
||||
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
|
||||
|
|
@ -262,7 +264,7 @@
|
|||
{
|
||||
[UIView animateWithDuration:0.2 animations:^
|
||||
{
|
||||
[strongSelf->_context setApplicationStatusBarAlpha:1.0f];
|
||||
//[strongSelf->_context setApplicationStatusBarAlpha:1.0f];
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
@ -533,7 +535,7 @@
|
|||
{
|
||||
[UIView animateWithDuration:0.2 animations:^
|
||||
{
|
||||
[strongSelf->_context setApplicationStatusBarAlpha:1.0f];
|
||||
//[strongSelf->_context setApplicationStatusBarAlpha:1.0f];
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
@ -1155,7 +1157,7 @@ static CGFloat transformRotation(CGAffineTransform transform)
|
|||
{
|
||||
[UIView animateWithDuration:0.2 animations:^
|
||||
{
|
||||
[_context setApplicationStatusBarAlpha:1.0f];
|
||||
//[_context setApplicationStatusBarAlpha:1.0f];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
|
@ -1228,7 +1230,7 @@ static CGFloat transformRotation(CGAffineTransform transform)
|
|||
|
||||
if (self.adjustsStatusBarVisibility)
|
||||
{
|
||||
[_context setApplicationStatusBarAlpha:1.0f];
|
||||
//[_context setApplicationStatusBarAlpha:1.0f];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -475,12 +475,12 @@
|
|||
{
|
||||
[UIView animateWithDuration:0.3 animations:^
|
||||
{
|
||||
[_context setApplicationStatusBarAlpha:1.0f];
|
||||
//[_context setApplicationStatusBarAlpha:1.0f];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_context setApplicationStatusBarAlpha:1.0f];
|
||||
//[_context setApplicationStatusBarAlpha:1.0f];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ public final class LegacyControllerContext: NSObject, LegacyComponentsContext {
|
|||
|
||||
public func setApplicationStatusBarAlpha(_ alpha: CGFloat) {
|
||||
if let controller = self.controller {
|
||||
controller.statusBar.alpha = alpha
|
||||
controller.statusBar.updateAlpha(alpha, transition: .immediate)
|
||||
self.updateDeferScreenEdgeGestures()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ public extension Peer {
|
|||
}
|
||||
} else if let lastName = user.lastName {
|
||||
return lastName
|
||||
} else if let phone = user.phone {
|
||||
return "+\(phone)"
|
||||
} else {
|
||||
return strings.User_DeletedAccount
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,19 +71,23 @@ static NSData *base64_decode(NSString *str) {
|
|||
|
||||
+ (MTSignal *)fetchBackupIpsResolveGoogle:(bool)isTesting phoneNumber:(NSString *)phoneNumber currentContext:(MTContext *)currentContext addressOverride:(NSString *)addressOverride {
|
||||
NSArray *hosts = @[
|
||||
@"google.com",
|
||||
@"www.google.com",
|
||||
@"google.ru"
|
||||
@[@"dns.google.com", @""],
|
||||
@[@"www.google.com", @"dns.google.com"],
|
||||
];
|
||||
NSDictionary *headers = @{@"Host": @"dns.google.com"};
|
||||
|
||||
NSMutableArray *signals = [[NSMutableArray alloc] init];
|
||||
for (NSString *host in hosts) {
|
||||
for (NSArray *hostAndHostname in hosts) {
|
||||
NSString *host = hostAndHostname[0];
|
||||
NSString *hostName = hostAndHostname[1];
|
||||
NSMutableDictionary *headers = [[NSMutableDictionary alloc] init];
|
||||
if ([hostName length] != 0) {
|
||||
headers[@"Host"] = hostName;
|
||||
}
|
||||
NSString *apvHost = @"apv3.stel.com";
|
||||
if (addressOverride != nil) {
|
||||
apvHost = addressOverride;
|
||||
}
|
||||
MTSignal *signal = [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://%@/resolve?name=%@&type=16", host, isTesting ? @"tapv3.stel.com" : apvHost]] headers:headers] mapToSignal:^MTSignal *(NSData *data) {
|
||||
MTSignal *signal = [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://%@/resolve?name=%@&type=16&random_padding=%@", host, isTesting ? @"tapv3.stel.com" : apvHost, makeRandomPadding()]] headers:headers] mapToSignal:^MTSignal *(NSData *data) {
|
||||
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
if ([dict respondsToSelector:@selector(objectForKey:)]) {
|
||||
NSArray *answer = dict[@"Answer"];
|
||||
|
|
@ -132,6 +136,90 @@ static NSData *base64_decode(NSString *str) {
|
|||
return [[MTSignal mergeSignals:signals] take:1];
|
||||
}
|
||||
|
||||
static NSString *makeRandomPadding() {
|
||||
char validCharacters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
int maxIndex = sizeof(validCharacters) - 1;
|
||||
|
||||
int minPadding = 13;
|
||||
int maxPadding = 128;
|
||||
int padding = minPadding + arc4random_uniform(maxPadding - minPadding);
|
||||
NSMutableData *result = [[NSMutableData alloc] initWithLength:padding];
|
||||
for (NSUInteger i = 0; i < result.length; i++) {
|
||||
int index = arc4random_uniform(maxIndex);
|
||||
assert(index >= 0 && index < maxIndex);
|
||||
((uint8_t *)(result.mutableBytes))[i] = validCharacters[index];
|
||||
}
|
||||
NSString *string = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
|
||||
return string;
|
||||
}
|
||||
|
||||
+ (MTSignal *)fetchBackupIpsResolveCloudflare:(bool)isTesting phoneNumber:(NSString *)phoneNumber currentContext:(MTContext *)currentContext addressOverride:(NSString *)addressOverride {
|
||||
NSArray *hosts = @[
|
||||
@[@"mozilla.cloudflare-dns.com", @""],
|
||||
];
|
||||
|
||||
NSMutableArray *signals = [[NSMutableArray alloc] init];
|
||||
for (NSArray *hostAndHostname in hosts) {
|
||||
NSString *host = hostAndHostname[0];
|
||||
NSString *hostName = hostAndHostname[1];
|
||||
NSMutableDictionary *headers = [[NSMutableDictionary alloc] init];
|
||||
headers[@"accept"] = @"application/dns-json";
|
||||
if ([hostName length] != 0) {
|
||||
headers[@"Host"] = hostName;
|
||||
}
|
||||
NSString *apvHost = @"apv3.stel.com";
|
||||
if (addressOverride != nil) {
|
||||
apvHost = addressOverride;
|
||||
}
|
||||
MTSignal *signal = [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://%@/dns-query?name=%@&type=16&random_padding=%@", host, isTesting ? @"tapv3.stel.com" : apvHost, makeRandomPadding()]] headers:headers] mapToSignal:^MTSignal *(NSData *data) {
|
||||
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
if ([dict respondsToSelector:@selector(objectForKey:)]) {
|
||||
NSArray *answer = dict[@"Answer"];
|
||||
NSMutableArray *strings = [[NSMutableArray alloc] init];
|
||||
if ([answer respondsToSelector:@selector(objectAtIndex:)]) {
|
||||
for (NSDictionary *value in answer) {
|
||||
if ([value respondsToSelector:@selector(objectForKey:)]) {
|
||||
NSString *part = value[@"data"];
|
||||
if ([part respondsToSelector:@selector(characterAtIndex:)]) {
|
||||
[strings addObject:part];
|
||||
}
|
||||
}
|
||||
}
|
||||
[strings sortUsingComparator:^NSComparisonResult(NSString *lhs, NSString *rhs) {
|
||||
if (lhs.length > rhs.length) {
|
||||
return NSOrderedAscending;
|
||||
} else {
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
}];
|
||||
|
||||
NSString *finalString = @"";
|
||||
for (NSString *string in strings) {
|
||||
finalString = [finalString stringByAppendingString:[string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]]];
|
||||
}
|
||||
|
||||
NSData *result = base64_decode(finalString);
|
||||
NSMutableData *finalData = [[NSMutableData alloc] initWithData:result];
|
||||
[finalData setLength:256];
|
||||
MTBackupDatacenterData *datacenterData = MTIPDataDecode(finalData, phoneNumber);
|
||||
if (datacenterData != nil && [self checkIpData:datacenterData timestamp:(int32_t)[currentContext globalTime] source:@"resolveCloudflare"]) {
|
||||
return [MTSignal single:datacenterData];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [MTSignal complete];
|
||||
}] catch:^MTSignal *(__unused id error) {
|
||||
return [MTSignal complete];
|
||||
}];
|
||||
if (signals.count != 0) {
|
||||
signal = [signal delay:signals.count onQueue:[[MTQueue alloc] init]];
|
||||
}
|
||||
[signals addObject:signal];
|
||||
}
|
||||
|
||||
return [[MTSignal mergeSignals:signals] take:1];
|
||||
}
|
||||
|
||||
+ (MTSignal *)fetchConfigFromAddress:(MTBackupDatacenterAddress *)address currentContext:(MTContext *)currentContext {
|
||||
MTApiEnvironment *apiEnvironment = [currentContext.apiEnvironment copy];
|
||||
|
||||
|
|
@ -210,11 +298,18 @@ static NSData *base64_decode(NSString *str) {
|
|||
+ (MTSignal * _Nonnull)fetchBackupIps:(bool)isTestingEnvironment currentContext:(MTContext * _Nonnull)currentContext additionalSource:(MTSignal * _Nullable)additionalSource phoneNumber:(NSString * _Nullable)phoneNumber {
|
||||
NSMutableArray *signals = [[NSMutableArray alloc] init];
|
||||
[signals addObject:[self fetchBackupIpsResolveGoogle:isTestingEnvironment phoneNumber:phoneNumber currentContext:currentContext addressOverride:currentContext.apiEnvironment.accessHostOverride]];
|
||||
[signals addObject:[self fetchBackupIpsResolveCloudflare:isTestingEnvironment phoneNumber:phoneNumber currentContext:currentContext addressOverride:currentContext.apiEnvironment.accessHostOverride]];
|
||||
if (additionalSource != nil) {
|
||||
/*#if DEBUG
|
||||
[signals removeAllObjects];
|
||||
#endif*/
|
||||
[signals addObject:additionalSource];
|
||||
[signals addObject:[additionalSource mapToSignal:^MTSignal *(MTBackupDatacenterData *datacenterData) {
|
||||
if (![datacenterData isKindOfClass:[MTBackupDatacenterData class]]) {
|
||||
return [MTSignal complete];
|
||||
}
|
||||
if (datacenterData != nil && [self checkIpData:datacenterData timestamp:(int32_t)[currentContext globalTime] source:@"resolveExternal"]) {
|
||||
return [MTSignal single:datacenterData];
|
||||
} else {
|
||||
return [MTSignal complete];
|
||||
}
|
||||
}]];
|
||||
}
|
||||
|
||||
return [[[MTSignal mergeSignals:signals] take:1] mapToSignal:^MTSignal *(MTBackupDatacenterData *data) {
|
||||
|
|
|
|||
|
|
@ -964,6 +964,9 @@ static NSData *decrypt_TL_data(unsigned char buffer[256]) {
|
|||
@end
|
||||
|
||||
MTBackupDatacenterData *MTIPDataDecode(NSData *data, NSString *phoneNumber) {
|
||||
if (data.length < 256) {
|
||||
return nil;
|
||||
}
|
||||
unsigned char buffer[256];
|
||||
memcpy(buffer, data.bytes, 256);
|
||||
NSData *result = decrypt_TL_data(buffer);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
[subscriber putCompletion];
|
||||
} failure:^(__unused NSOperation *operation, __unused NSError *error)
|
||||
{
|
||||
[subscriber putError:nil];
|
||||
[subscriber putError:error];
|
||||
}];
|
||||
|
||||
[operation start];
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (MTSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword;
|
||||
- (MTSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray<NSString *> *)wordList;
|
||||
- (MTSignal *)deleteKey:(TONKey *)key;
|
||||
- (MTSignal *)deleteAllKeys;
|
||||
- (MTSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -325,10 +325,12 @@ typedef enum {
|
|||
make_object<tonlib_api::config>(
|
||||
configString.UTF8String,
|
||||
blockchainName.UTF8String,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
),
|
||||
keystoreDirectory.UTF8String
|
||||
make_object<tonlib_api::keyStoreTypeDirectory>(
|
||||
keystoreDirectory.UTF8String
|
||||
)
|
||||
));
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
|
|
@ -636,6 +638,28 @@ typedef enum {
|
|||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)deleteAllKeys {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
||||
_requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr<tonlib_api::Object> &object) {
|
||||
if (object->get_id() == tonlib_api::error::ID) {
|
||||
auto error = tonlib_api::move_object_as<tonlib_api::error>(object);
|
||||
[subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]];
|
||||
} else {
|
||||
[subscriber putCompletion];
|
||||
}
|
||||
}];
|
||||
|
||||
auto query = make_object<tonlib_api::deleteAllKeys>();
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
NSData *addressData = [address dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
|
|
|||
|
|
@ -135,6 +135,55 @@ public func retry<T, E>(_ delayIncrement: Double, maxDelay: Double, onQueue queu
|
|||
}
|
||||
}
|
||||
|
||||
public func retry<T, E>(retryOnError: @escaping (E) -> Bool, delayIncrement: Double, maxDelay: Double, maxRetries: Int, onQueue queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||
return { signal in
|
||||
return Signal { subscriber in
|
||||
let shouldRetry = Atomic(value: true)
|
||||
let currentDelay = Atomic<(Double, Int)>(value: (0.0, 0))
|
||||
let currentDisposable = MetaDisposable()
|
||||
|
||||
let start = recursiveFunction { recurse in
|
||||
let currentShouldRetry = shouldRetry.with { value in
|
||||
return value
|
||||
}
|
||||
if currentShouldRetry {
|
||||
let disposable = signal.start(next: { next in
|
||||
subscriber.putNext(next)
|
||||
}, error: { error in
|
||||
if !retryOnError(error) {
|
||||
subscriber.putError(error)
|
||||
} else {
|
||||
let (delay, count) = currentDelay.modify { value, count in
|
||||
return (min(maxDelay, value + delayIncrement), count + 1)
|
||||
}
|
||||
|
||||
if count >= maxRetries {
|
||||
subscriber.putError(error)
|
||||
} else {
|
||||
let time: DispatchTime = DispatchTime.now() + Double(delay)
|
||||
queue.queue.asyncAfter(deadline: time, execute: {
|
||||
recurse()
|
||||
})
|
||||
}
|
||||
}
|
||||
}, completed: {
|
||||
let _ = shouldRetry.swap(false)
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
currentDisposable.set(disposable)
|
||||
}
|
||||
}
|
||||
|
||||
start()
|
||||
|
||||
return ActionDisposable {
|
||||
currentDisposable.dispose()
|
||||
let _ = shouldRetry.swap(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func restartIfError<T, E>(_ signal: Signal<T, E>) -> Signal<T, NoError> {
|
||||
return Signal<T, NoError> { subscriber in
|
||||
let shouldRetry = Atomic(value: true)
|
||||
|
|
|
|||
|
|
@ -447,7 +447,7 @@ func editSettingsController(context: AccountContext, currentName: ItemListAvatar
|
|||
(controller?.navigationController as? NavigationController)?.pushViewController(value)
|
||||
}
|
||||
presentControllerImpl = { [weak controller] value, arguments in
|
||||
controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
controller?.present(value, in: .window(.root), with: arguments)
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
let _ = (controller?.navigationController as? NavigationController)?.popViewController(animated: true)
|
||||
|
|
|
|||
|
|
@ -1102,9 +1102,13 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
|||
|> mapToSignal { context in
|
||||
return context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
|> map { view -> Bool in
|
||||
let appConfiguration = view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
|
||||
let configuration = WalletConfiguration.with(appConfiguration: appConfiguration)
|
||||
return configuration.config != nil
|
||||
if #available(iOSApplicationExtension 10.3, iOS 10.3, *) {
|
||||
let appConfiguration = view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
|
||||
let configuration = WalletConfiguration.with(appConfiguration: appConfiguration)
|
||||
return configuration.config != nil
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1375,7 +1379,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
|||
(controller?.navigationController as? NavigationController)?.replaceAllButRootController(value, animated: true, animationOptions: [.removeOnMasterDetails])
|
||||
}
|
||||
presentControllerImpl = { [weak controller] value, arguments in
|
||||
controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet), blockInteraction: true)
|
||||
controller?.present(value, in: .window(.root), with: arguments, blockInteraction: true)
|
||||
}
|
||||
presentInGlobalOverlayImpl = { [weak controller] value, arguments in
|
||||
controller?.presentInGlobalOverlay(value, with: arguments)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public final class TabBarAccountSwitchController: ViewController {
|
|||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Hide
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
self.statusBar.ignoreInCall = true
|
||||
|
||||
self.lockOrientation = true
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public extension TermsOfServiceControllerTheme {
|
|||
}
|
||||
}
|
||||
|
||||
public class TermsOfServiceController: ViewController {
|
||||
public class TermsOfServiceController: ViewController, StandalonePresentableController {
|
||||
private var controllerNode: TermsOfServiceControllerNode {
|
||||
return self.displayNode as! TermsOfServiceControllerNode
|
||||
}
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
self.scrollNode.view.showsHorizontalScrollIndicator = false
|
||||
self.scrollNode.view.isPagingEnabled = true
|
||||
self.scrollNode.view.delegate = self
|
||||
|
|
|
|||
|
|
@ -228,6 +228,11 @@ public final class ShareController: ViewController {
|
|||
return self.displayNode as! ShareControllerNode
|
||||
}
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
override public var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
private var animatedIn = false
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
|
|
@ -267,6 +272,8 @@ public final class ShareController: ViewController {
|
|||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
switch subject {
|
||||
case let .url(text):
|
||||
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in
|
||||
|
|
@ -598,7 +605,7 @@ public final class ShareController: ViewController {
|
|||
return .preparing
|
||||
case let .done(items):
|
||||
if let strongSelf = self, !items.isEmpty {
|
||||
strongSelf.ready.set(.single(true))
|
||||
strongSelf._ready.set(.single(true))
|
||||
var activityItems: [Any] = []
|
||||
for item in items {
|
||||
switch item {
|
||||
|
|
@ -663,13 +670,11 @@ public final class ShareController: ViewController {
|
|||
strongSelf.controllerNode.updatePeers(account: strongSelf.currentAccount, switchableAccounts: strongSelf.switchableAccounts, peers: next.0, accountPeer: next.1, defaultAction: strongSelf.defaultAction)
|
||||
}
|
||||
}))
|
||||
self.ready.set(self.controllerNode.ready.get())
|
||||
self._ready.set(self.controllerNode.ready.get())
|
||||
}
|
||||
|
||||
override public func loadView() {
|
||||
super.loadView()
|
||||
|
||||
self.statusBar.removeFromSupernode()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
|
|
|
|||
|
|
@ -100,13 +100,7 @@ public final class ActiveSessionsContext {
|
|||
return .complete()
|
||||
}
|
||||
|
||||
var mergedSessions = strongSelf._state.sessions
|
||||
for i in (0 ..< mergedSessions.count).reversed() {
|
||||
if mergedSessions[i].hash != 0 {
|
||||
mergedSessions.remove(at: i)
|
||||
break
|
||||
}
|
||||
}
|
||||
let mergedSessions = strongSelf._state.sessions.filter({ $0.hash == 0 })
|
||||
|
||||
strongSelf._state = ActiveSessionsContextState(isLoadingMore: strongSelf._state.isLoadingMore, sessions: mergedSessions)
|
||||
return .complete()
|
||||
|
|
|
|||
|
|
@ -417,6 +417,8 @@ public struct NetworkInitializationArguments {
|
|||
}
|
||||
}
|
||||
|
||||
private let cloudDataContext = makeCloudDataContext()
|
||||
|
||||
func initializedNetwork(arguments: NetworkInitializationArguments, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool, languageCode: String?, proxySettings: ProxySettings?, networkSettings: NetworkSettings?, phoneNumber: String?) -> Signal<Network, NoError> {
|
||||
return Signal { subscriber in
|
||||
let queue = Queue()
|
||||
|
|
@ -484,17 +486,18 @@ func initializedNetwork(arguments: NetworkInitializationArguments, supplementary
|
|||
var wrappedAdditionalSource: MTSignal?
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
let additionalSource = cloudDataAdditionalAddressSource(phoneNumber: .single(phoneNumber ?? ""))
|
||||
wrappedAdditionalSource = MTSignal(generator: { subscriber in
|
||||
let disposable = additionalSource.start(next: { value in
|
||||
subscriber?.putNext(value)
|
||||
}, completed: {
|
||||
subscriber?.putCompletion()
|
||||
if let cloudDataContext = cloudDataContext {
|
||||
wrappedAdditionalSource = MTSignal(generator: { subscriber in
|
||||
let disposable = cloudDataContext.get(phoneNumber: .single(phoneNumber)).start(next: { value in
|
||||
subscriber?.putNext(value)
|
||||
}, completed: {
|
||||
subscriber?.putCompletion()
|
||||
})
|
||||
return MTBlockDisposable(block: {
|
||||
disposable.dispose()
|
||||
})
|
||||
})
|
||||
return MTBlockDisposable(block: {
|
||||
disposable.dispose()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
context.setDiscoverBackupAddressListSignal(MTBackupAddressSignals.fetchBackupIps(testingEnvironment, currentContext: context, additionalSource: wrappedAdditionalSource, phoneNumber: phoneNumber))
|
||||
|
|
@ -1010,7 +1013,8 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
|||
}
|
||||
|
||||
public func retryRequest<T>(signal: Signal<T, MTRpcError>) -> Signal<T, NoError> {
|
||||
return signal |> retry(0.2, maxDelay: 5.0, onQueue: Queue.concurrentDefaultQueue())
|
||||
return signal
|
||||
|> retry(0.2, maxDelay: 5.0, onQueue: Queue.concurrentDefaultQueue())
|
||||
}
|
||||
|
||||
class Keychain: NSObject, MTKeychain {
|
||||
|
|
@ -1045,7 +1049,7 @@ class Keychain: NSObject, MTKeychain {
|
|||
}
|
||||
}
|
||||
|
||||
public func makeCloudDataContext() -> Any? {
|
||||
func makeCloudDataContext() -> CloudDataContext? {
|
||||
if #available(iOS 10.0, *) {
|
||||
return CloudDataContextImpl()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ public extension Peer {
|
|||
var isDeleted: Bool {
|
||||
switch self {
|
||||
case let user as TelegramUser:
|
||||
return user.firstName == nil && user.lastName == nil
|
||||
return user.firstName == nil && user.lastName == nil && user.phone == nil
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public enum TonKeychainEncryptDataError {
|
|||
public enum TonKeychainDecryptDataError {
|
||||
case generic
|
||||
case publicKeyMismatch
|
||||
case cancelled
|
||||
}
|
||||
|
||||
public struct TonKeychain {
|
||||
|
|
@ -65,7 +66,10 @@ private final class TonInstanceImpl {
|
|||
} else {
|
||||
let network = self.network
|
||||
instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
|
||||
let _ = (network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: request.data)))).start(next: { result in
|
||||
let _ = (
|
||||
network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: request.data)))
|
||||
|> timeout(10.0, queue: .concurrentDefaultQueue(), alternate: .fail(MTRpcError(errorCode: 500, errorDescription: "NETWORK_ERROR")))
|
||||
).start(next: { result in
|
||||
switch result {
|
||||
case let .liteResponse(response):
|
||||
request.onResult(response.makeData(), nil)
|
||||
|
|
@ -92,13 +96,13 @@ public final class TonInstance {
|
|||
})
|
||||
}
|
||||
|
||||
fileprivate func exportKey(key: TONKey, serverSalt: Data) -> Signal<[String], NoError> {
|
||||
fileprivate func exportKey(key: TONKey, localPassword: Data) -> Signal<[String], NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.export(key, localPassword: serverSalt).start(next: { wordList in
|
||||
let cancel = ton.export(key, localPassword: localPassword).start(next: { wordList in
|
||||
guard let wordList = wordList as? [String] else {
|
||||
assertionFailure()
|
||||
return
|
||||
|
|
@ -116,18 +120,18 @@ public final class TonInstance {
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate func createWallet(keychain: TonKeychain, serverSalt: Data) -> Signal<(WalletInfo, [String]), CreateWalletError> {
|
||||
fileprivate func createWallet(keychain: TonKeychain, localPassword: Data) -> Signal<(WalletInfo, [String]), CreateWalletError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.createKey(withLocalPassword: serverSalt, mnemonicPassword: Data()).start(next: { key in
|
||||
let cancel = ton.createKey(withLocalPassword: localPassword, mnemonicPassword: Data()).start(next: { key in
|
||||
guard let key = key as? TONKey else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
let cancel = keychain.encrypt(key.secret).start(next: { encryptedSecretData in
|
||||
let _ = self.exportKey(key: key, serverSalt: serverSalt).start(next: { wordList in
|
||||
let _ = self.exportKey(key: key, localPassword: localPassword).start(next: { wordList in
|
||||
subscriber.putNext((WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: encryptedSecretData), wordList))
|
||||
subscriber.putCompletion()
|
||||
}, error: { error in
|
||||
|
|
@ -150,13 +154,13 @@ public final class TonInstance {
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate func importWallet(keychain: TonKeychain, wordList: [String], serverSalt: Data) -> Signal<WalletInfo, ImportWalletInternalError> {
|
||||
fileprivate func importWallet(keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal<WalletInfo, ImportWalletInternalError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.importKey(withLocalPassword: serverSalt, mnemonicPassword: Data(), wordList: wordList).start(next: { key in
|
||||
let cancel = ton.importKey(withLocalPassword: localPassword, mnemonicPassword: Data(), wordList: wordList).start(next: { key in
|
||||
guard let key = key as? TONKey else {
|
||||
subscriber.putError(.generic)
|
||||
return
|
||||
|
|
@ -218,8 +222,16 @@ public final class TonInstance {
|
|||
return
|
||||
}
|
||||
subscriber.putNext(state)
|
||||
}, error: { _ in
|
||||
subscriber.putError(.generic)
|
||||
}, error: { error in
|
||||
if let error = error as? TONError {
|
||||
if error.text.hasPrefix("LITE_SERVER_") {
|
||||
subscriber.putError(.network)
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
|
@ -240,7 +252,7 @@ public final class TonInstance {
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate func walletLastTransactionId(address: String) -> Signal<WalletTransactionId?, NoError> {
|
||||
fileprivate func walletLastTransactionId(address: String) -> Signal<WalletTransactionId?, WalletLastTransactionIdError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
|
|
@ -252,7 +264,16 @@ public final class TonInstance {
|
|||
return
|
||||
}
|
||||
subscriber.putNext(state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:)))
|
||||
}, error: { _ in
|
||||
}, error: { error in
|
||||
if let error = error as? TONError {
|
||||
if error.text.hasPrefix("ДITE_SERVER_") {
|
||||
subscriber.putError(.network)
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
|
@ -278,39 +299,10 @@ public final class TonInstance {
|
|||
return
|
||||
}
|
||||
subscriber.putNext(transactions.map(WalletTransaction.init(tonTransaction:)))
|
||||
}, error: { _ in
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
disposable.set(ActionDisposable {
|
||||
cancel?.dispose()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func sendGramsFromWallet(decryptedSecret: Data, serverSalt: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: String, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<Never, SendGramsFromWalletError> {
|
||||
let key = TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret)
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.sendGrams(from: key, localPassword: serverSalt, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId).start(next: { result in
|
||||
guard let result = result as? TONSendGramsResult else {
|
||||
subscriber.putError(.generic)
|
||||
return
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
}, error: { error in
|
||||
if let error = error as? TONError {
|
||||
if error.text == "Failed to parse account address" {
|
||||
subscriber.putError(.invalidAddress)
|
||||
} else if error.text.hasPrefix("DANGEROUS_TRANSACTION") {
|
||||
subscriber.putError(.destinationIsNotInitialized)
|
||||
if error.text.hasPrefix("LITE_SERVER_") {
|
||||
subscriber.putError(.network)
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
|
|
@ -330,65 +322,97 @@ public final class TonInstance {
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate func walletRestoreWords(walletInfo: WalletInfo, keychain: TonKeychain, serverSalt: Data) -> Signal<[String], WalletRestoreWordsError> {
|
||||
return keychain.decrypt(walletInfo.encryptedSecret)
|
||||
|> mapError { _ -> WalletRestoreWordsError in
|
||||
return .secretDecryptionFailed
|
||||
}
|
||||
|> mapToSignal { decryptedSecret -> Signal<[String], WalletRestoreWordsError> in
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.export(TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret), localPassword: serverSalt).start(next: { wordList in
|
||||
guard let wordList = wordList as? [String] else {
|
||||
subscriber.putError(.generic)
|
||||
return
|
||||
}
|
||||
subscriber.putNext(wordList)
|
||||
}, error: { _ in
|
||||
fileprivate func sendGramsFromWallet(decryptedSecret: Data, localPassword: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: String, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<Never, SendGramsFromWalletError> {
|
||||
let key = TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret)
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.sendGrams(from: key, localPassword: localPassword, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId).start(next: { result in
|
||||
guard let result = result as? TONSendGramsResult else {
|
||||
subscriber.putError(.generic)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
disposable.set(ActionDisposable {
|
||||
cancel?.dispose()
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
}, error: { error in
|
||||
if let error = error as? TONError {
|
||||
if error.text.hasPrefix("INVALID_ACCOUNT_ADDRESS") {
|
||||
subscriber.putError(.invalidAddress)
|
||||
} else if error.text.hasPrefix("DANGEROUS_TRANSACTION") {
|
||||
subscriber.putError(.destinationIsNotInitialized)
|
||||
} else if error.text.hasPrefix("MESSAGE_TOO_LONG") {
|
||||
subscriber.putError(.messageTooLong)
|
||||
} else if error.text.hasPrefix("NOT_ENOUGH_FUNDS") {
|
||||
subscriber.putError(.notEnoughFunds)
|
||||
} else if error.text.hasPrefix("LITE_SERVER_") {
|
||||
subscriber.putError(.network)
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
disposable.set(ActionDisposable {
|
||||
cancel?.dispose()
|
||||
})
|
||||
}
|
||||
|
||||
return disposable
|
||||
}
|
||||
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func deleteLocalWalletData(walletInfo: WalletInfo, keychain: TonKeychain, serverSalt: Data) -> Signal<Never, DeleteLocalWalletDataError> {
|
||||
return keychain.decrypt(walletInfo.encryptedSecret)
|
||||
|> mapError { _ -> DeleteLocalWalletDataError in
|
||||
return .secretDecryptionFailed
|
||||
}
|
||||
|> mapToSignal { decryptedSecret -> Signal<Never, DeleteLocalWalletDataError> in
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.delete(TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret)).start(next: { _ in
|
||||
assertionFailure()
|
||||
}, error: { _ in
|
||||
fileprivate func walletRestoreWords(publicKey: WalletPublicKey, decryptedSecret: Data, localPassword: Data) -> Signal<[String], WalletRestoreWordsError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.export(TONKey(publicKey: publicKey.rawValue, secret: decryptedSecret), localPassword: localPassword).start(next: { wordList in
|
||||
guard let wordList = wordList as? [String] else {
|
||||
subscriber.putError(.generic)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
disposable.set(ActionDisposable {
|
||||
cancel?.dispose()
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
subscriber.putNext(wordList)
|
||||
}, error: { _ in
|
||||
subscriber.putError(.generic)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
disposable.set(ActionDisposable {
|
||||
cancel?.dispose()
|
||||
})
|
||||
}
|
||||
|
||||
return disposable
|
||||
}
|
||||
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func deleteAllLocalWalletsData() -> Signal<Never, DeleteAllLocalWalletsDataError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.deleteAllKeys().start(next: { _ in
|
||||
assertionFailure()
|
||||
}, error: { _ in
|
||||
subscriber.putError(.generic)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
disposable.set(ActionDisposable {
|
||||
cancel?.dispose()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -508,24 +532,18 @@ public enum CreateWalletError {
|
|||
case generic
|
||||
}
|
||||
|
||||
public func createWallet(postbox: Postbox, network: Network, tonInstance: TonInstance, keychain: TonKeychain) -> Signal<(WalletInfo, [String]), CreateWalletError> {
|
||||
return getServerWalletSalt(network: network)
|
||||
|> mapError { _ -> CreateWalletError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { serverSalt -> Signal<(WalletInfo, [String]), CreateWalletError> in
|
||||
return tonInstance.createWallet(keychain: keychain, serverSalt: serverSalt)
|
||||
|> mapToSignal { walletInfo, wordList -> Signal<(WalletInfo, [String]), CreateWalletError> in
|
||||
return postbox.transaction { transaction -> (WalletInfo, [String]) in
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
||||
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
|
||||
walletCollection.wallets = [WalletStateRecord(info: walletInfo, exportCompleted: false, state: nil)]
|
||||
return walletCollection
|
||||
})
|
||||
return (walletInfo, wordList)
|
||||
}
|
||||
|> castError(CreateWalletError.self)
|
||||
public func createWallet(postbox: Postbox, tonInstance: TonInstance, keychain: TonKeychain, localPassword: Data) -> Signal<(WalletInfo, [String]), CreateWalletError> {
|
||||
return tonInstance.createWallet(keychain: keychain, localPassword: localPassword)
|
||||
|> mapToSignal { walletInfo, wordList -> Signal<(WalletInfo, [String]), CreateWalletError> in
|
||||
return postbox.transaction { transaction -> (WalletInfo, [String]) in
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
||||
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
|
||||
walletCollection.wallets = [WalletStateRecord(info: walletInfo, exportCompleted: false, state: nil)]
|
||||
return walletCollection
|
||||
})
|
||||
return (walletInfo, wordList)
|
||||
}
|
||||
|> castError(CreateWalletError.self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -552,77 +570,51 @@ public enum ImportWalletError {
|
|||
case generic
|
||||
}
|
||||
|
||||
public func importWallet(postbox: Postbox, network: Network, tonInstance: TonInstance, keychain: TonKeychain, wordList: [String]) -> Signal<WalletInfo, ImportWalletError> {
|
||||
return getServerWalletSalt(network: network)
|
||||
|> mapError { _ -> ImportWalletError in
|
||||
return .generic
|
||||
public func importWallet(postbox: Postbox, tonInstance: TonInstance, keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal<WalletInfo, ImportWalletError> {
|
||||
return tonInstance.importWallet(keychain: keychain, wordList: wordList, localPassword: localPassword)
|
||||
|> `catch` { error -> Signal<WalletInfo, ImportWalletError> in
|
||||
switch error {
|
||||
case .generic:
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { serverSalt in
|
||||
return tonInstance.importWallet(keychain: keychain, wordList: wordList, serverSalt: serverSalt)
|
||||
|> `catch` { error -> Signal<WalletInfo, ImportWalletError> in
|
||||
switch error {
|
||||
case .generic:
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { walletInfo -> Signal<WalletInfo, ImportWalletError> in
|
||||
return postbox.transaction { transaction -> WalletInfo in
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
||||
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
|
||||
walletCollection.wallets = [WalletStateRecord(info: walletInfo, exportCompleted: true, state: nil)]
|
||||
return walletCollection
|
||||
})
|
||||
return walletInfo
|
||||
}
|
||||
|> castError(ImportWalletError.self)
|
||||
|> mapToSignal { walletInfo -> Signal<WalletInfo, ImportWalletError> in
|
||||
return postbox.transaction { transaction -> WalletInfo in
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
||||
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
|
||||
walletCollection.wallets = [WalletStateRecord(info: walletInfo, exportCompleted: true, state: nil)]
|
||||
return walletCollection
|
||||
})
|
||||
return walletInfo
|
||||
}
|
||||
|> castError(ImportWalletError.self)
|
||||
}
|
||||
}
|
||||
|
||||
public enum DeleteLocalWalletDataError {
|
||||
public enum DeleteAllLocalWalletsDataError {
|
||||
case generic
|
||||
case secretDecryptionFailed
|
||||
}
|
||||
|
||||
public func deleteLocalWalletData(postbox: Postbox, network: Network, tonInstance: TonInstance, keychain: TonKeychain, walletInfo: WalletInfo) -> Signal<Never, DeleteLocalWalletDataError> {
|
||||
return getServerWalletSalt(network: network)
|
||||
|> mapError { _ -> DeleteLocalWalletDataError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { serverSalt -> Signal<Never, DeleteLocalWalletDataError> in
|
||||
return tonInstance.deleteLocalWalletData(walletInfo: walletInfo, keychain: keychain, serverSalt: serverSalt)
|
||||
|> then(
|
||||
postbox.transaction { transaction -> Void in
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
||||
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
|
||||
for i in 0 ..< walletCollection.wallets.count {
|
||||
if walletCollection.wallets[i].info.publicKey == walletInfo.publicKey {
|
||||
walletCollection.wallets.remove(at: i)
|
||||
break
|
||||
}
|
||||
}
|
||||
return walletCollection
|
||||
})
|
||||
}
|
||||
|> castError(DeleteLocalWalletDataError.self)
|
||||
|> ignoreValues
|
||||
)
|
||||
}
|
||||
public func deleteAllLocalWalletsData(postbox: Postbox, network: Network, tonInstance: TonInstance) -> Signal<Never, DeleteAllLocalWalletsDataError> {
|
||||
return tonInstance.deleteAllLocalWalletsData()
|
||||
|> then(
|
||||
postbox.transaction { transaction -> Void in
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
||||
let walletCollection = WalletCollection(wallets: [])
|
||||
return walletCollection
|
||||
})
|
||||
}
|
||||
|> castError(DeleteAllLocalWalletsDataError.self)
|
||||
|> ignoreValues
|
||||
)
|
||||
}
|
||||
|
||||
public enum WalletRestoreWordsError {
|
||||
case generic
|
||||
case secretDecryptionFailed
|
||||
}
|
||||
|
||||
public func walletRestoreWords(network: Network, walletInfo: WalletInfo, tonInstance: TonInstance, keychain: TonKeychain) -> Signal<[String], WalletRestoreWordsError> {
|
||||
return getServerWalletSalt(network: network)
|
||||
|> mapError { _ -> WalletRestoreWordsError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { serverSalt in
|
||||
return tonInstance.walletRestoreWords(walletInfo: walletInfo, keychain: keychain, serverSalt: serverSalt)
|
||||
}
|
||||
public func walletRestoreWords(tonInstance: TonInstance, publicKey: WalletPublicKey, decryptedSecret: Data, localPassword: Data) -> Signal<[String], WalletRestoreWordsError> {
|
||||
return tonInstance.walletRestoreWords(publicKey: publicKey, decryptedSecret: decryptedSecret, localPassword: localPassword)
|
||||
}
|
||||
|
||||
public struct WalletState: Codable, Equatable {
|
||||
|
|
@ -641,6 +633,7 @@ public func walletAddress(publicKey: WalletPublicKey, tonInstance: TonInstance)
|
|||
|
||||
private enum GetWalletStateError {
|
||||
case generic
|
||||
case network
|
||||
}
|
||||
|
||||
private func getWalletState(address: String, tonInstance: TonInstance) -> Signal<(WalletState, Int64), GetWalletStateError> {
|
||||
|
|
@ -649,6 +642,7 @@ private func getWalletState(address: String, tonInstance: TonInstance) -> Signal
|
|||
|
||||
public enum GetCombinedWalletStateError {
|
||||
case generic
|
||||
case network
|
||||
}
|
||||
|
||||
public enum CombinedWalletStateResult {
|
||||
|
|
@ -681,8 +675,19 @@ public func getCombinedWalletState(postbox: Postbox, subject: CombinedWalletStat
|
|||
|> castError(GetCombinedWalletStateError.self)
|
||||
|> mapToSignal { address -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
|
||||
return getWalletState(address: address, tonInstance: tonInstance)
|
||||
|> mapError { _ -> GetCombinedWalletStateError in
|
||||
return .generic
|
||||
|> retryTonRequest(isNetworkError: { error in
|
||||
if case .network = error {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|> mapError { error -> GetCombinedWalletStateError in
|
||||
if case .network = error {
|
||||
return .network
|
||||
} else {
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
|> mapToSignal { walletState, syncUtime -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
|
||||
let topTransactions: Signal<[WalletTransaction], GetCombinedWalletStateError>
|
||||
|
|
@ -690,8 +695,12 @@ public func getCombinedWalletState(postbox: Postbox, subject: CombinedWalletStat
|
|||
topTransactions = .single(cachedState?.topTransactions ?? [])
|
||||
} else {
|
||||
topTransactions = getWalletTransactions(address: address, previousId: nil, tonInstance: tonInstance)
|
||||
|> mapError { _ -> GetCombinedWalletStateError in
|
||||
return .generic
|
||||
|> mapError { error -> GetCombinedWalletStateError in
|
||||
if case .network = error {
|
||||
return .network
|
||||
} else {
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
}
|
||||
return topTransactions
|
||||
|
|
@ -743,13 +752,16 @@ public enum SendGramsFromWalletError {
|
|||
case secretDecryptionFailed
|
||||
case invalidAddress
|
||||
case destinationIsNotInitialized
|
||||
case messageTooLong
|
||||
case notEnoughFunds
|
||||
case network
|
||||
}
|
||||
|
||||
public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, walletInfo: WalletInfo, decryptedSecret: Data, serverSalt: Data, toAddress: String, amount: Int64, textMessage: String, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<Never, SendGramsFromWalletError> {
|
||||
public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, walletInfo: WalletInfo, decryptedSecret: Data, localPassword: Data, toAddress: String, amount: Int64, textMessage: String, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<Never, SendGramsFromWalletError> {
|
||||
return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance)
|
||||
|> castError(SendGramsFromWalletError.self)
|
||||
|> mapToSignal { fromAddress in
|
||||
return tonInstance.sendGramsFromWallet(decryptedSecret: decryptedSecret, serverSalt: serverSalt, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId)
|
||||
return tonInstance.sendGramsFromWallet(decryptedSecret: decryptedSecret, localPassword: localPassword, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -861,6 +873,7 @@ private extension WalletTransaction {
|
|||
|
||||
public enum GetWalletTransactionsError {
|
||||
case generic
|
||||
case network
|
||||
}
|
||||
|
||||
public func getWalletTransactions(address: String, previousId: WalletTransactionId?, tonInstance: TonInstance) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
|
||||
|
|
@ -884,18 +897,50 @@ public func getWalletTransactions(address: String, previousId: WalletTransaction
|
|||
}
|
||||
}
|
||||
|
||||
private func retryTonRequest<T, E>(isNetworkError: @escaping (E) -> Bool) -> (Signal<T, E>) -> Signal<T, E> {
|
||||
return { signal in
|
||||
return signal
|
||||
|> retry(retryOnError: isNetworkError, delayIncrement: 0.2, maxDelay: 5.0, maxRetries: 3, onQueue: Queue.concurrentDefaultQueue())
|
||||
}
|
||||
}
|
||||
|
||||
private enum WalletLastTransactionIdError {
|
||||
case generic
|
||||
case network
|
||||
}
|
||||
|
||||
private func getWalletTransactionsOnce(address: String, previousId: WalletTransactionId?, tonInstance: TonInstance) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
|
||||
let previousIdValue: Signal<WalletTransactionId?, GetWalletTransactionsError>
|
||||
if let previousId = previousId {
|
||||
previousIdValue = .single(previousId)
|
||||
} else {
|
||||
previousIdValue = tonInstance.walletLastTransactionId(address: address)
|
||||
|> castError(GetWalletTransactionsError.self)
|
||||
|> retryTonRequest(isNetworkError: { error in
|
||||
if case .network = error {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|> mapError { error -> GetWalletTransactionsError in
|
||||
if case .network = error {
|
||||
return .network
|
||||
} else {
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
}
|
||||
return previousIdValue
|
||||
|> mapToSignal { previousId in
|
||||
if let previousId = previousId {
|
||||
return tonInstance.getWalletTransactions(address: address, previousId: previousId)
|
||||
|> retryTonRequest(isNetworkError: { error in
|
||||
if case .network = error {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return .single([])
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@
|
|||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : " 2.pdf"
|
||||
"filename" : "ic_walletupdate.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
|
|||
BIN
submodules/TelegramUI/Images.xcassets/Wallet/RefreshIcon.imageset/ic_walletupdate.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Wallet/RefreshIcon.imageset/ic_walletupdate.pdf
vendored
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -56,6 +56,15 @@ private let keyboardWindowClass: AnyClass? = {
|
|||
private class ApplicationStatusBarHost: StatusBarHost {
|
||||
private let application = UIApplication.shared
|
||||
|
||||
var isApplicationInForeground: Bool {
|
||||
switch self.application.applicationState {
|
||||
case .background:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
var statusBarFrame: CGRect {
|
||||
return self.application.statusBarFrame
|
||||
}
|
||||
|
|
@ -71,6 +80,10 @@ private class ApplicationStatusBarHost: StatusBarHost {
|
|||
self.application.setStatusBarStyle(style, animated: animated)
|
||||
}
|
||||
|
||||
func setStatusBarHidden(_ value: Bool, animated: Bool) {
|
||||
self.application.setStatusBarHidden(value, with: animated ? .fade : .none)
|
||||
}
|
||||
|
||||
var statusBarWindow: UIView? {
|
||||
return self.application.value(forKey: "statusBarWindow") as? UIView
|
||||
}
|
||||
|
|
@ -231,8 +244,6 @@ final class SharedApplicationContext {
|
|||
|
||||
private let deviceToken = Promise<Data?>(nil)
|
||||
|
||||
private var cloudDataContext: Any?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
||||
precondition(!testIsLaunched)
|
||||
testIsLaunched = true
|
||||
|
|
@ -249,8 +260,6 @@ final class SharedApplicationContext {
|
|||
self.window = window
|
||||
self.nativeWindow = window
|
||||
|
||||
self.cloudDataContext = makeCloudDataContext()
|
||||
|
||||
let clearNotificationsManager = ClearNotificationsManager(getNotificationIds: { completion in
|
||||
if #available(iOS 10.0, *) {
|
||||
UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler: { notifications in
|
||||
|
|
@ -664,6 +673,7 @@ final class SharedApplicationContext {
|
|||
#if targetEnvironment(simulator)
|
||||
tonKeychain = TonKeychain(encryptionPublicKey: {
|
||||
//return .single(nil)
|
||||
//return .single("1".data(using: .utf8)!)
|
||||
return .single(Data())
|
||||
}, encrypt: { data in
|
||||
return Signal { subscriber in
|
||||
|
|
@ -701,11 +711,17 @@ final class SharedApplicationContext {
|
|||
}
|
||||
}, decrypt: { encryptedData in
|
||||
return Signal { subscriber in
|
||||
BuildConfig.decryptApplicationSecret(encryptedData.data, publicKey: encryptedData.publicKey, baseAppBundleId: baseAppBundleId, completion: { result in
|
||||
BuildConfig.decryptApplicationSecret(encryptedData.data, publicKey: encryptedData.publicKey, baseAppBundleId: baseAppBundleId, completion: { result, cancelled in
|
||||
if let result = result {
|
||||
subscriber.putNext(result)
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
let error: TonKeychainDecryptDataError
|
||||
if cancelled {
|
||||
error = .cancelled
|
||||
} else {
|
||||
error = .generic
|
||||
}
|
||||
subscriber.putError(error)
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
|
@ -1259,9 +1275,12 @@ final class SharedApplicationContext {
|
|||
#endif
|
||||
}
|
||||
|
||||
if UIApplication.shared.isStatusBarHidden {
|
||||
UIApplication.shared.setStatusBarHidden(false, with: .none)
|
||||
}
|
||||
NotificationCenter.default.addObserver(forName: UIWindow.didBecomeHiddenNotification, object: nil, queue: nil, using: { notification in
|
||||
if UIApplication.shared.isStatusBarHidden {
|
||||
UIApplication.shared.setStatusBarHidden(false, with: .none)
|
||||
//UIApplication.shared.setStatusBarHidden(false, with: .none)
|
||||
}
|
||||
})
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -456,6 +456,7 @@ final class AuthorizedApplicationContext {
|
|||
}, expandAction: { expandData in
|
||||
if let strongSelf = self {
|
||||
let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(firstMessage.id.peerId), mode: .overlay)
|
||||
//chatController.navigation_setNavigationController(strongSelf.rootController)
|
||||
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(chatController, in: .window(.root), with: ChatControllerOverlayPresentationData(expandData: expandData()))
|
||||
}
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ final class AuthorizationSequenceCodeEntryController: ViewController {
|
|||
self.displayNode = AuthorizationSequenceCodeEntryControllerNode(strings: self.strings, theme: self.theme)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.controllerNode.view.disableAutomaticKeyboardHandling = [.forward, .backward]
|
||||
|
||||
self.controllerNode.loginWithCode = { [weak self] code in
|
||||
self?.continueWithCode(code)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ final class AuthorizationSequencePasswordEntryController: ViewController {
|
|||
self.displayNode = AuthorizationSequencePasswordEntryControllerNode(strings: self.strings, theme: self.theme)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.controllerNode.view.disableAutomaticKeyboardHandling = [.forward, .backward]
|
||||
|
||||
self.controllerNode.loginWithCode = { [weak self] _ in
|
||||
self?.nextPressed()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,9 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
|||
self.controllerNode.codeAndNumber = (code, name, number)
|
||||
}
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.controllerNode.view.disableAutomaticKeyboardHandling = [.forward, .backward]
|
||||
|
||||
self.controllerNode.selectCountryCode = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.strings, theme: strongSelf.theme)
|
||||
|
|
@ -118,8 +121,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
|||
controller.dismissed = {
|
||||
self?.controllerNode.activateInput()
|
||||
}
|
||||
strongSelf.controllerNode.view.endEditing(true)
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
strongSelf.push(controller)
|
||||
}
|
||||
}
|
||||
self.controllerNode.checkPhone = { [weak self] in
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
|||
})
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.controllerNode.view.disableAutomaticKeyboardHandling = [.forward, .backward]
|
||||
|
||||
self.controllerNode.signUpWithName = { [weak self] _, _ in
|
||||
self?.nextPressed()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2313,7 +2313,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
|
||||
private func themeAndStringsUpdated() {
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
switch self.presentationInterfaceState.mode {
|
||||
case .standard:
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
self.deferScreenEdgeGestures = []
|
||||
case .overlay:
|
||||
self.statusBar.statusBarStyle = .Hide
|
||||
self.deferScreenEdgeGestures = [.top]
|
||||
case .inline:
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
self.chatTitleView?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in
|
||||
|
|
@ -2590,22 +2599,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
self.chatDisplayNode.historyNode.contentPositionChanged = { [weak self] offset in
|
||||
if let strongSelf = self {
|
||||
let offsetAlpha: CGFloat
|
||||
if case let .standard(previewing) = strongSelf.presentationInterfaceState.mode, previewing {
|
||||
offsetAlpha = 0.0
|
||||
} else {
|
||||
switch offset {
|
||||
case let .known(offset):
|
||||
if offset < 40.0 {
|
||||
offsetAlpha = 0.0
|
||||
} else {
|
||||
offsetAlpha = 1.0
|
||||
}
|
||||
case .unknown:
|
||||
offsetAlpha = 1.0
|
||||
case .none:
|
||||
switch offset {
|
||||
case let .known(offset):
|
||||
if offset < 40.0 {
|
||||
offsetAlpha = 0.0
|
||||
}
|
||||
} else {
|
||||
offsetAlpha = 1.0
|
||||
}
|
||||
case .unknown:
|
||||
offsetAlpha = 1.0
|
||||
case .none:
|
||||
offsetAlpha = 0.0
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.navigateButtons.displayDownButton = !offsetAlpha.isZero
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1903,6 +1903,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
|
||||
switch self.chatPresentationInterfaceState.mode {
|
||||
case .standard(previewing: true):
|
||||
if let result = self.navigateButtons.hitTest(self.view.convert(point, to: self.navigateButtons.view), with: event) {
|
||||
return result
|
||||
}
|
||||
if self.bounds.contains(point) {
|
||||
return self.historyNode.view
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ final class ChatScheduleTimeController: ViewController {
|
|||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
self.blocksBackgroundWhenInOverlay = true
|
||||
|
||||
self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||
|
|
@ -74,8 +76,6 @@ final class ChatScheduleTimeController: ViewController {
|
|||
|
||||
override public func loadView() {
|
||||
super.loadView()
|
||||
|
||||
self.statusBar.removeFromSupernode()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ public final class NotificationContainerController: ViewController {
|
|||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
public init(context: AccountContext) {
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
|
|
@ -61,7 +63,15 @@ public final class NotificationContainerController: ViewController {
|
|||
|
||||
self.controllerNode.displayingItemsUpdated = { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
strongSelf.statusBar.statusBarStyle = value ? .Hide : .Ignore
|
||||
var statusBarHidden = false
|
||||
if value, let layout = strongSelf.validLayout {
|
||||
if let statusBarHeight = layout.statusBarHeight, statusBarHeight > 20.0 {
|
||||
statusBarHidden = false
|
||||
} else {
|
||||
statusBarHidden = true
|
||||
}
|
||||
}
|
||||
strongSelf.statusBar.statusBarStyle = statusBarHidden ? .Hide : .Ignore
|
||||
if value {
|
||||
strongSelf.deferScreenEdgeGestures = [.top]
|
||||
} else {
|
||||
|
|
@ -72,6 +82,8 @@ public final class NotificationContainerController: ViewController {
|
|||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
|
||||
|
|
|
|||
|
|
@ -68,29 +68,19 @@ final class NotificationContainerControllerNode: ASDisplayNode {
|
|||
if let (item, topItemNode) = self.topItemAndNode {
|
||||
if item.groupingKey == key {
|
||||
self.topItemAndNode = nil
|
||||
self.displayingItemsUpdated?(false)
|
||||
topItemNode.animateOut(completion: { [weak self, weak topItemNode] in
|
||||
topItemNode?.removeFromSupernode()
|
||||
|
||||
if let strongSelf = self, strongSelf.topItemAndNode == nil {
|
||||
strongSelf.displayingItemsUpdated?(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enqueue(_ item: NotificationItem) {
|
||||
var updatedDisplayingItems = false
|
||||
if let (_, topItemNode) = self.topItemAndNode {
|
||||
topItemNode.animateOut(completion: { [weak self, weak topItemNode] in
|
||||
topItemNode?.removeFromSupernode()
|
||||
|
||||
if let strongSelf = self, strongSelf.topItemAndNode == nil {
|
||||
strongSelf.displayingItemsUpdated?(false)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
updatedDisplayingItems = true
|
||||
}
|
||||
|
||||
var useCompactLayout = false
|
||||
|
|
@ -138,9 +128,7 @@ final class NotificationContainerControllerNode: ASDisplayNode {
|
|||
containerNode.animateIn()
|
||||
}
|
||||
|
||||
if updatedDisplayingItems {
|
||||
self.displayingItemsUpdated?(true)
|
||||
}
|
||||
self.displayingItemsUpdated?(true)
|
||||
|
||||
self.resetTimeoutTimer()
|
||||
}
|
||||
|
|
@ -172,12 +160,9 @@ final class NotificationContainerControllerNode: ASDisplayNode {
|
|||
if let strongSelf = self {
|
||||
if let (_, topItemNode) = strongSelf.topItemAndNode {
|
||||
strongSelf.topItemAndNode = nil
|
||||
strongSelf.displayingItemsUpdated?(false)
|
||||
topItemNode.animateOut(completion: { [weak topItemNode] in
|
||||
topItemNode?.removeFromSupernode()
|
||||
|
||||
if let strongSelf = self, strongSelf.topItemAndNode == nil {
|
||||
strongSelf.displayingItemsUpdated?(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,6 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer
|
|||
|
||||
override public func loadView() {
|
||||
super.loadView()
|
||||
|
||||
self.statusBar.removeFromSupernode()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -843,10 +843,13 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
} else {
|
||||
text = presentationData.strings.Call_StatusBar("").0
|
||||
}
|
||||
|
||||
self.mainWindow?.setForceInCallStatusBar(text)
|
||||
if let navigationController = self.mainWindow?.viewController as? NavigationController {
|
||||
navigationController.setForceInCallStatusBar(text)
|
||||
}
|
||||
} else {
|
||||
self.mainWindow?.setForceInCallStatusBar(nil)
|
||||
if let navigationController = self.mainWindow?.viewController as? NavigationController {
|
||||
navigationController.setForceInCallStatusBar(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -692,6 +692,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||
return
|
||||
}
|
||||
let combinedState: CombinedWalletState?
|
||||
var isUpdated = false
|
||||
switch value {
|
||||
case let .cached(state):
|
||||
if strongSelf.combinedState != nil {
|
||||
|
|
@ -705,6 +706,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||
}
|
||||
combinedState = state
|
||||
case let .updated(state):
|
||||
isUpdated = true
|
||||
strongSelf.loadingIndicator.stopAnimating()
|
||||
strongSelf.loadingIndicator.isHidden = true
|
||||
combinedState = state
|
||||
|
|
@ -729,7 +731,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||
|
||||
strongSelf.transactionsLoaded(isReload: true, transactions: combinedState.topTransactions)
|
||||
|
||||
strongSelf.headerNode.isRefreshing = false
|
||||
if isUpdated {
|
||||
strongSelf.headerNode.isRefreshing = false
|
||||
}
|
||||
|
||||
if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .animated(duration: 0.2, curve: .easeInOut), isScrolling: false)
|
||||
|
|
@ -751,7 +755,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||
strongSelf.didSetContentReady = true
|
||||
strongSelf.contentReady.set(.single(true))
|
||||
}
|
||||
}, error: { [weak self] _ in
|
||||
}, error: { [weak self] error in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -780,7 +784,14 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||
strongSelf.contentReady.set(.single(true))
|
||||
}
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Info_RefreshErrorTitle, text: strongSelf.presentationData.strings.Wallet_Info_RefreshErrorText, actions: [
|
||||
let text: String
|
||||
switch error {
|
||||
case .generic:
|
||||
text = strongSelf.presentationData.strings.Wallet_Info_RefreshErrorText
|
||||
case .network:
|
||||
text = strongSelf.presentationData.strings.Wallet_Info_RefreshErrorNetworkText
|
||||
}
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Info_RefreshErrorTitle, text: text, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), nil)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ final class WalletRefreshNode: ASDisplayNode {
|
|||
self.iconNode = ASImageNode()
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
self.iconNode.displayWithoutProcessing = true
|
||||
self.iconNode.image = UIImage(bundleImageName: "Wallet/RefreshIcon")
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Wallet/RefreshIcon"), color: UIColor(white: 0.6, alpha: 1.0))
|
||||
if let image = self.iconNode.image {
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(), size: image.size)
|
||||
}
|
||||
|
|
@ -199,7 +199,7 @@ final class WalletRefreshNode: ASDisplayNode {
|
|||
|
||||
let iconSize = self.iconNode.image?.size ?? CGSize(width: 20.0, height: 20.0)
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: 200.0, height: 100.0))
|
||||
let iconSpacing: CGFloat = 5.0
|
||||
let iconSpacing: CGFloat = 1.0
|
||||
|
||||
let contentWidth = iconSize.width + titleSize.width + iconSpacing
|
||||
let contentOrigin = floor(-contentWidth / 2.0)
|
||||
|
|
|
|||
|
|
@ -104,10 +104,20 @@ public func walletSettingsController(context: AccountContext, tonContext: TonCon
|
|||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
|
||||
presentControllerImpl?(controller, nil)
|
||||
let _ = (walletRestoreWords(network: context.account.network, walletInfo: walletInfo, tonInstance: tonContext.instance, keychain: tonContext.keychain)
|
||||
|> deliverOnMainQueue).start(next: { [weak controller] wordList in
|
||||
controller?.dismiss()
|
||||
pushControllerImpl?(WalletWordDisplayScreen(context: context, tonContext: tonContext, walletInfo: walletInfo, wordList: wordList, mode: .export, walletCreatedPreloadState: nil))
|
||||
let _ = (tonContext.keychain.decrypt(walletInfo.encryptedSecret)
|
||||
|> deliverOnMainQueue).start(next: { [weak controller] decryptedSecret in
|
||||
let _ = (getServerWalletSalt(network: context.account.network)
|
||||
|> deliverOnMainQueue).start(next: { serverSalt in
|
||||
let _ = (walletRestoreWords(tonInstance: tonContext.instance, publicKey: walletInfo.publicKey, decryptedSecret: decryptedSecret, localPassword: serverSalt)
|
||||
|> deliverOnMainQueue).start(next: { [weak controller] wordList in
|
||||
controller?.dismiss()
|
||||
pushControllerImpl?(WalletWordDisplayScreen(context: context, tonContext: tonContext, walletInfo: walletInfo, wordList: wordList, mode: .export, walletCreatedPreloadState: nil))
|
||||
}, error: { [weak controller] _ in
|
||||
controller?.dismiss()
|
||||
})
|
||||
}, error: { [weak controller] _ in
|
||||
controller?.dismiss()
|
||||
})
|
||||
}, error: { [weak controller] _ in
|
||||
controller?.dismiss()
|
||||
})
|
||||
|
|
@ -120,7 +130,7 @@ public func walletSettingsController(context: AccountContext, tonContext: TonCon
|
|||
actionSheet?.dismissAnimated()
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
|
||||
presentControllerImpl?(controller, nil)
|
||||
let _ = (deleteLocalWalletData(postbox: context.account.postbox, network: context.account.network, tonInstance: tonContext.instance, keychain: tonContext.keychain, walletInfo: walletInfo)
|
||||
let _ = (deleteAllLocalWalletsData(postbox: context.account.postbox, network: context.account.network, tonInstance: tonContext.instance)
|
||||
|> deliverOnMainQueue).start(error: { [weak controller] _ in
|
||||
controller?.dismiss()
|
||||
}, completed: { [weak controller] in
|
||||
|
|
|
|||
|
|
@ -93,17 +93,21 @@ public final class WalletSplashScreen: ViewController {
|
|||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.sendGrams(walletInfo: walletInfo, decryptedSecret: decryptedSecret, address: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: false, randomId: randomId, serverSalt: serverSalt)
|
||||
}, error: { [weak self] _ in
|
||||
strongSelf.sendGrams(walletInfo: walletInfo, decryptedSecret: decryptedSecret, address: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: true, randomId: randomId, serverSalt: serverSalt)
|
||||
}, error: { [weak self] error in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let text = strongSelf.presentationData.strings.Wallet_Send_ErrorDecryptionFailed
|
||||
let controller = textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
self?.dismiss()
|
||||
})])
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
strongSelf.dismiss()
|
||||
if case .cancelled = error {
|
||||
strongSelf.dismiss()
|
||||
} else {
|
||||
let text = strongSelf.presentationData.strings.Wallet_Send_ErrorDecryptionFailed
|
||||
let controller = textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
self?.dismiss()
|
||||
})])
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
})
|
||||
case .sent:
|
||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(customDisplayNode: ASDisplayNode())!, animated: false)
|
||||
|
|
@ -129,7 +133,7 @@ public final class WalletSplashScreen: ViewController {
|
|||
}
|
||||
|
||||
private func sendGrams(walletInfo: WalletInfo, decryptedSecret: Data, address: String, amount: Int64, textMessage: String, forceIfDestinationNotInitialized: Bool, randomId: Int64, serverSalt: Data) {
|
||||
let _ = (sendGramsFromWallet(network: self.context.account.network, tonInstance: self.tonContext.instance, walletInfo: walletInfo, decryptedSecret: decryptedSecret, serverSalt: serverSalt, toAddress: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: 0, randomId: randomId)
|
||||
let _ = (sendGramsFromWallet(network: self.context.account.network, tonInstance: self.tonContext.instance, walletInfo: walletInfo, decryptedSecret: decryptedSecret, localPassword: serverSalt, toAddress: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: 0, randomId: randomId)
|
||||
|> deliverOnMainQueue).start(error: { [weak self] error in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
@ -138,14 +142,20 @@ public final class WalletSplashScreen: ViewController {
|
|||
switch error {
|
||||
case .generic:
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
case .network:
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_NetworkError
|
||||
case .notEnoughFunds:
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_ErrorNotEnoughFunds
|
||||
case .messageTooLong:
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
case .invalidAddress:
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_ErrorInvalidAddress
|
||||
case .secretDecryptionFailed:
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_ErrorDecryptionFailed
|
||||
case .destinationIsNotInitialized:
|
||||
if !forceIfDestinationNotInitialized {
|
||||
text = "This address belongs to an empty wallet. Are you sure you want to transfer grams to it?"
|
||||
let controller = textAlertController(context: strongSelf.context, title: "Warning", text: text, actions: [
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_UninitializedText
|
||||
let controller = textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Wallet_Send_UninitializedTitle, text: text, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
navigationController.popViewController(animated: true)
|
||||
|
|
@ -203,15 +213,7 @@ public final class WalletSplashScreen: ViewController {
|
|||
switch strongSelf.mode {
|
||||
case .intro:
|
||||
let controller = OverlayStatusController(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, type: .loading(cancelled: nil))
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
let _ = (createWallet(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, tonInstance: strongSelf.tonContext.instance, keychain: strongSelf.tonContext.keychain)
|
||||
|> deliverOnMainQueue).start(next: { walletInfo, wordList in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
controller.dismiss()
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .created(walletInfo, wordList), walletCreatedPreloadState: nil), animated: true)
|
||||
}, error: { _ in
|
||||
let displayError: () -> Void = {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -220,6 +222,22 @@ public final class WalletSplashScreen: ViewController {
|
|||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
}
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
let _ = (getServerWalletSalt(network: strongSelf.context.account.network)
|
||||
|> deliverOnMainQueue).start(next: { serverSalt in
|
||||
let _ = (createWallet(postbox: strongSelf.context.account.postbox, tonInstance: strongSelf.tonContext.instance, keychain: strongSelf.tonContext.keychain, localPassword: serverSalt)
|
||||
|> deliverOnMainQueue).start(next: { walletInfo, wordList in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
controller.dismiss()
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .created(walletInfo, wordList), walletCreatedPreloadState: nil), animated: true)
|
||||
}, error: { _ in
|
||||
displayError()
|
||||
})
|
||||
}, error: { _ in
|
||||
displayError()
|
||||
})
|
||||
case let .created(walletInfo, wordList):
|
||||
if let wordList = wordList {
|
||||
|
|
@ -227,23 +245,56 @@ public final class WalletSplashScreen: ViewController {
|
|||
} else {
|
||||
let controller = OverlayStatusController(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, type: .loading(cancelled: nil))
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
let _ = (walletRestoreWords(network: strongSelf.context.account.network, walletInfo: walletInfo, tonInstance: strongSelf.tonContext.instance, keychain: strongSelf.tonContext.keychain)
|
||||
|> deliverOnMainQueue).start(next: { wordList in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.mode = .created(walletInfo, wordList)
|
||||
controller.dismiss()
|
||||
strongSelf.push(WalletWordDisplayScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: walletInfo, wordList: wordList, mode: .check, walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
|
||||
}, error: { _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
controller.dismiss()
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Created_ExportErrorTitle, text: strongSelf.presentationData.strings.Wallet_Created_ExportErrorText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
|
||||
let context = strongSelf.context
|
||||
let tonContext = strongSelf.tonContext
|
||||
let _ = (strongSelf.tonContext.keychain.decrypt(walletInfo.encryptedSecret)
|
||||
|> deliverOnMainQueue).start(next: { [weak controller] decryptedSecret in
|
||||
let _ = (getServerWalletSalt(network: context.account.network)
|
||||
|> deliverOnMainQueue).start(next: { [weak controller] serverSalt in
|
||||
let _ = (walletRestoreWords(tonInstance: tonContext.instance, publicKey: walletInfo.publicKey, decryptedSecret: decryptedSecret, localPassword: serverSalt)
|
||||
|> deliverOnMainQueue).start(next: { wordList in
|
||||
controller?.dismiss()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.mode = .created(walletInfo, wordList)
|
||||
strongSelf.push(WalletWordDisplayScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: walletInfo, wordList: wordList, mode: .check, walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
|
||||
}, error: { _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
controller?.dismiss()
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Created_ExportErrorTitle, text: strongSelf.presentationData.strings.Wallet_Created_ExportErrorText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
})
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
}, error: { [weak controller] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
controller?.dismiss()
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Created_ExportErrorTitle, text: strongSelf.presentationData.strings.Wallet_Created_ExportErrorText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
})
|
||||
}, error: { [weak controller] error in
|
||||
controller?.dismiss()
|
||||
if case .cancelled = error {
|
||||
} else {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Created_ExportErrorTitle, text: strongSelf.presentationData.strings.Wallet_Created_ExportErrorText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
}
|
||||
})
|
||||
}
|
||||
case let .success(walletInfo):
|
||||
|
|
@ -720,16 +771,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
|||
let secondaryActionSize = self.secondaryActionTitleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||
|
||||
let contentHeight = iconSize.height + iconSpacing + titleSize.height + titleSpacing + textSize.height
|
||||
let contentVerticalOrigin = floor((layout.size.height - contentHeight - iconSize.height / 2.0) / 2.0)
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: contentVerticalOrigin), size: iconSize).offsetBy(dx: iconOffset.x, dy: iconOffset.y)
|
||||
transition.updateFrameAdditive(node: self.iconNode, frame: iconFrame)
|
||||
self.animationNode.updateLayout(size: iconFrame.size)
|
||||
transition.updateFrameAdditive(node: self.animationNode, frame: iconFrame)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: iconFrame.maxY + iconSpacing), size: titleSize)
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
||||
let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
var contentVerticalOrigin = floor((layout.size.height - contentHeight - iconSize.height / 2.0) / 2.0)
|
||||
|
||||
let minimalBottomInset: CGFloat = 60.0
|
||||
let bottomInset = layout.intrinsicInsets.bottom + max(minimalBottomInset, termsSize.height + termsSpacing * 2.0)
|
||||
|
|
@ -740,12 +782,27 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
|||
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
|
||||
self.buttonNode.updateLayout(width: buttonFrame.width, transition: transition)
|
||||
|
||||
var maxContentVerticalOrigin = buttonFrame.minY - 12.0 - contentHeight
|
||||
|
||||
if !secondaryActionSize.width.isZero {
|
||||
let secondaryActionFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - secondaryActionSize.width) / 2.0), y: buttonFrame.minY - 20.0 - secondaryActionSize.height), size: secondaryActionSize)
|
||||
transition.updateFrameAdditive(node: self.secondaryActionTitleNode, frame: secondaryActionFrame)
|
||||
transition.updateFrame(node: self.secondaryActionButtonNode, frame: secondaryActionFrame.insetBy(dx: -10.0, dy: -10.0))
|
||||
|
||||
maxContentVerticalOrigin = secondaryActionFrame.minY - 12.0 - contentHeight
|
||||
}
|
||||
|
||||
contentVerticalOrigin = min(contentVerticalOrigin, maxContentVerticalOrigin)
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: contentVerticalOrigin), size: iconSize).offsetBy(dx: iconOffset.x, dy: iconOffset.y)
|
||||
transition.updateFrameAdditive(node: self.iconNode, frame: iconFrame)
|
||||
self.animationNode.updateLayout(size: iconFrame.size)
|
||||
transition.updateFrameAdditive(node: self.animationNode, frame: iconFrame)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: iconFrame.maxY + iconSpacing), size: titleSize)
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
||||
let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
let termsFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - termsSize.width) / 2.0), y: buttonFrame.maxY + floor((layout.size.height - layout.intrinsicInsets.bottom - buttonFrame.maxY - termsSize.height) / 2.0)), size: termsSize)
|
||||
transition.updateFrameAdditive(node: self.termsNode, frame: termsFrame)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import TelegramStringFormatting
|
|||
import UrlEscaping
|
||||
|
||||
let walletAddressLength: Int = 48
|
||||
let walletTextLimit: Int = 124
|
||||
let walletTextLimit: Int = 1024
|
||||
|
||||
func formatAddress(_ address: String) -> String {
|
||||
var address = address
|
||||
|
|
|
|||
|
|
@ -2178,30 +2178,8 @@ public final class WalletWordCheckScreen: ViewController {
|
|||
], actionLayout: .vertical), in: .window(.root))
|
||||
return
|
||||
}
|
||||
let _ = (importWallet(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, tonInstance: strongSelf.tonContext.instance, keychain: strongSelf.tonContext.keychain, wordList: enteredWords)
|
||||
|> deliverOnMainQueue).start(next: { walletInfo in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { controller in
|
||||
if controller is WalletSplashScreen {
|
||||
return false
|
||||
}
|
||||
if controller is WalletWordDisplayScreen {
|
||||
return false
|
||||
}
|
||||
if controller is WalletWordCheckScreen {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .success(walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
}, error: { error in
|
||||
|
||||
let displayError: () -> Void = {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -2210,6 +2188,38 @@ public final class WalletWordCheckScreen: ViewController {
|
|||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
}
|
||||
|
||||
let _ = (getServerWalletSalt(network: strongSelf.context.account.network)
|
||||
|> deliverOnMainQueue).start(next: { serverSalt in
|
||||
let _ = (importWallet(postbox: strongSelf.context.account.postbox, tonInstance: strongSelf.tonContext.instance, keychain: strongSelf.tonContext.keychain, wordList: enteredWords, localPassword: serverSalt)
|
||||
|> deliverOnMainQueue).start(next: { walletInfo in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { controller in
|
||||
if controller is WalletSplashScreen {
|
||||
return false
|
||||
}
|
||||
if controller is WalletWordDisplayScreen {
|
||||
return false
|
||||
}
|
||||
if controller is WalletWordCheckScreen {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .success(walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
}, error: { error in
|
||||
displayError()
|
||||
})
|
||||
}, error: { _ in
|
||||
displayError()
|
||||
})
|
||||
}
|
||||
}, secondaryAction: { [weak self] in
|
||||
|
|
|
|||
|
|
@ -315,6 +315,8 @@ if (NOT CMAKE_CROSSCOMPILING)
|
|||
GenFif(DEST smartcont/config-code.fif SOURCE smartcont/config-code.fc)
|
||||
GenFif(DEST smartcont/wallet-code.fif SOURCE smartcont/wallet-code.fc)
|
||||
GenFif(DEST smartcont/simple-wallet-code.fif SOURCE smartcont/simple-wallet-code.fc)
|
||||
GenFif(DEST smartcont/highload-wallet-code.fif SOURCE smartcont/highload-wallet-code.fc)
|
||||
GenFif(DEST smartcont/highload-wallet-v2-code.fif SOURCE smartcont/highload-wallet-v2-code.fc)
|
||||
GenFif(DEST smartcont/elector-code.fif SOURCE smartcont/elector-code.fc)
|
||||
endif()
|
||||
|
||||
|
|
@ -333,3 +335,6 @@ target_link_libraries(dump-block PUBLIC ton_crypto fift-lib ton_block)
|
|||
if (WINGETOPT_FOUND)
|
||||
target_link_libraries_system(dump-block wingetopt)
|
||||
endif()
|
||||
|
||||
install(TARGETS fift func RUNTIME DESTINATION bin)
|
||||
install(DIRECTORY fift/lib/ DESTINATION lib/fift)
|
||||
|
|
|
|||
|
|
@ -8891,6 +8891,7 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
|
|||
&& cs.advance(2);
|
||||
case trans_split_prepare:
|
||||
return cs.advance(528)
|
||||
&& t_Maybe_TrStoragePhase.skip(cs)
|
||||
&& t_TrComputePhase.skip(cs)
|
||||
&& t_Maybe_Ref_TrActionPhase.skip(cs)
|
||||
&& cs.advance(2);
|
||||
|
|
@ -8902,6 +8903,7 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
|
|||
&& cs.advance(1);
|
||||
case trans_merge_install:
|
||||
return cs.advance_ext(0x10210)
|
||||
&& t_Maybe_TrStoragePhase.skip(cs)
|
||||
&& t_Maybe_TrCreditPhase.skip(cs)
|
||||
&& t_TrComputePhase.skip(cs)
|
||||
&& t_Maybe_Ref_TrActionPhase.skip(cs)
|
||||
|
|
@ -8932,6 +8934,7 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
|
|||
&& cs.advance(2);
|
||||
case trans_split_prepare:
|
||||
return cs.advance(528)
|
||||
&& t_Maybe_TrStoragePhase.validate_skip(cs, weak)
|
||||
&& t_TrComputePhase.validate_skip(cs, weak)
|
||||
&& t_Maybe_Ref_TrActionPhase.validate_skip(cs, weak)
|
||||
&& cs.advance(2);
|
||||
|
|
@ -8947,6 +8950,7 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
|
|||
return cs.fetch_ulong(4) == 7
|
||||
&& cs.advance(524)
|
||||
&& t_Transaction.validate_skip_ref(cs, weak)
|
||||
&& t_Maybe_TrStoragePhase.validate_skip(cs, weak)
|
||||
&& t_Maybe_TrCreditPhase.validate_skip(cs, weak)
|
||||
&& t_TrComputePhase.validate_skip(cs, weak)
|
||||
&& t_Maybe_Ref_TrActionPhase.validate_skip(cs, weak)
|
||||
|
|
@ -8998,7 +9002,7 @@ bool TransactionDescr::cell_unpack_trans_storage(Ref<vm::Cell> cell_ref, Ref<Cel
|
|||
bool TransactionDescr::unpack(vm::CellSlice& cs, TransactionDescr::Record_trans_tick_tock& data) const {
|
||||
return cs.fetch_ulong(3) == 1
|
||||
&& cs.fetch_bool_to(data.is_tock)
|
||||
&& t_TrStoragePhase.fetch_to(cs, data.storage)
|
||||
&& t_TrStoragePhase.fetch_to(cs, data.storage_ph)
|
||||
&& t_TrComputePhase.fetch_to(cs, data.compute_ph)
|
||||
&& t_Maybe_Ref_TrActionPhase.fetch_to(cs, data.action)
|
||||
&& cs.fetch_bool_to(data.aborted)
|
||||
|
|
@ -9014,6 +9018,7 @@ bool TransactionDescr::cell_unpack(Ref<vm::Cell> cell_ref, TransactionDescr::Rec
|
|||
bool TransactionDescr::unpack(vm::CellSlice& cs, TransactionDescr::Record_trans_split_prepare& data) const {
|
||||
return cs.fetch_ulong(4) == 4
|
||||
&& cs.fetch_subslice_to(524, data.split_info)
|
||||
&& t_Maybe_TrStoragePhase.fetch_to(cs, data.storage_ph)
|
||||
&& t_TrComputePhase.fetch_to(cs, data.compute_ph)
|
||||
&& t_Maybe_Ref_TrActionPhase.fetch_to(cs, data.action)
|
||||
&& cs.fetch_bool_to(data.aborted)
|
||||
|
|
@ -9082,6 +9087,7 @@ bool TransactionDescr::unpack(vm::CellSlice& cs, TransactionDescr::Record_trans_
|
|||
return cs.fetch_ulong(4) == 7
|
||||
&& cs.fetch_subslice_to(524, data.split_info)
|
||||
&& cs.fetch_ref_to(data.prepare_transaction)
|
||||
&& t_Maybe_TrStoragePhase.fetch_to(cs, data.storage_ph)
|
||||
&& t_Maybe_TrCreditPhase.fetch_to(cs, data.credit_ph)
|
||||
&& t_TrComputePhase.fetch_to(cs, data.compute_ph)
|
||||
&& t_Maybe_Ref_TrActionPhase.fetch_to(cs, data.action)
|
||||
|
|
@ -9135,7 +9141,7 @@ bool TransactionDescr::cell_pack_trans_storage(Ref<vm::Cell>& cell_ref, Ref<Cell
|
|||
bool TransactionDescr::pack(vm::CellBuilder& cb, const TransactionDescr::Record_trans_tick_tock& data) const {
|
||||
return cb.store_long_bool(1, 3)
|
||||
&& cb.store_ulong_rchk_bool(data.is_tock, 1)
|
||||
&& t_TrStoragePhase.store_from(cb, data.storage)
|
||||
&& t_TrStoragePhase.store_from(cb, data.storage_ph)
|
||||
&& t_TrComputePhase.store_from(cb, data.compute_ph)
|
||||
&& t_Maybe_Ref_TrActionPhase.store_from(cb, data.action)
|
||||
&& cb.store_ulong_rchk_bool(data.aborted, 1)
|
||||
|
|
@ -9150,6 +9156,7 @@ bool TransactionDescr::cell_pack(Ref<vm::Cell>& cell_ref, const TransactionDescr
|
|||
bool TransactionDescr::pack(vm::CellBuilder& cb, const TransactionDescr::Record_trans_split_prepare& data) const {
|
||||
return cb.store_long_bool(4, 4)
|
||||
&& cb.append_cellslice_chk(data.split_info, 524)
|
||||
&& t_Maybe_TrStoragePhase.store_from(cb, data.storage_ph)
|
||||
&& t_TrComputePhase.store_from(cb, data.compute_ph)
|
||||
&& t_Maybe_Ref_TrActionPhase.store_from(cb, data.action)
|
||||
&& cb.store_ulong_rchk_bool(data.aborted, 1)
|
||||
|
|
@ -9213,6 +9220,7 @@ bool TransactionDescr::pack(vm::CellBuilder& cb, const TransactionDescr::Record_
|
|||
return cb.store_long_bool(7, 4)
|
||||
&& cb.append_cellslice_chk(data.split_info, 524)
|
||||
&& cb.store_ref_bool(data.prepare_transaction)
|
||||
&& t_Maybe_TrStoragePhase.store_from(cb, data.storage_ph)
|
||||
&& t_Maybe_TrCreditPhase.store_from(cb, data.credit_ph)
|
||||
&& t_TrComputePhase.store_from(cb, data.compute_ph)
|
||||
&& t_Maybe_Ref_TrActionPhase.store_from(cb, data.action)
|
||||
|
|
@ -9254,7 +9262,7 @@ bool TransactionDescr::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
|
|||
return cs.advance(3)
|
||||
&& pp.open("trans_tick_tock")
|
||||
&& pp.fetch_uint_field(cs, 1, "is_tock")
|
||||
&& pp.field("storage")
|
||||
&& pp.field("storage_ph")
|
||||
&& t_TrStoragePhase.print_skip(pp, cs)
|
||||
&& pp.field("compute_ph")
|
||||
&& t_TrComputePhase.print_skip(pp, cs)
|
||||
|
|
@ -9268,6 +9276,8 @@ bool TransactionDescr::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
|
|||
&& pp.open("trans_split_prepare")
|
||||
&& pp.field("split_info")
|
||||
&& t_SplitMergeInfo.print_skip(pp, cs)
|
||||
&& pp.field("storage_ph")
|
||||
&& t_Maybe_TrStoragePhase.print_skip(pp, cs)
|
||||
&& pp.field("compute_ph")
|
||||
&& t_TrComputePhase.print_skip(pp, cs)
|
||||
&& pp.field("action")
|
||||
|
|
@ -9300,6 +9310,8 @@ bool TransactionDescr::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
|
|||
&& t_SplitMergeInfo.print_skip(pp, cs)
|
||||
&& pp.field("prepare_transaction")
|
||||
&& t_Transaction.print_ref(pp, cs.fetch_ref())
|
||||
&& pp.field("storage_ph")
|
||||
&& t_Maybe_TrStoragePhase.print_skip(pp, cs)
|
||||
&& pp.field("credit_ph")
|
||||
&& t_Maybe_TrCreditPhase.print_skip(pp, cs)
|
||||
&& pp.field("compute_ph")
|
||||
|
|
@ -14089,11 +14101,13 @@ const StoragePrices t_StoragePrices;
|
|||
//
|
||||
// code for type `GasLimitsPrices`
|
||||
//
|
||||
constexpr unsigned char GasLimitsPrices::cons_tag[2];
|
||||
constexpr unsigned char GasLimitsPrices::cons_tag[3];
|
||||
|
||||
int GasLimitsPrices::get_tag(const vm::CellSlice& cs) const {
|
||||
switch (cs.bselect(6, 0x180000000000000ULL)) {
|
||||
switch (cs.bselect(6, 0x1b0000000000000ULL)) {
|
||||
case 0:
|
||||
return gas_flat_pfx;
|
||||
case 2:
|
||||
return cs.bit_at(6) ? gas_prices_ext : gas_prices;
|
||||
default:
|
||||
return -1;
|
||||
|
|
@ -14106,6 +14120,8 @@ int GasLimitsPrices::check_tag(const vm::CellSlice& cs) const {
|
|||
return cs.prefetch_ulong(8) == 0xdd ? gas_prices : -1;
|
||||
case gas_prices_ext:
|
||||
return cs.prefetch_ulong(8) == 0xde ? gas_prices_ext : -1;
|
||||
case gas_flat_pfx:
|
||||
return cs.prefetch_ulong(8) == 0xd1 ? gas_flat_pfx : -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -14116,6 +14132,9 @@ bool GasLimitsPrices::skip(vm::CellSlice& cs) const {
|
|||
return cs.advance(392);
|
||||
case gas_prices_ext:
|
||||
return cs.advance(456);
|
||||
case gas_flat_pfx:
|
||||
return cs.advance(136)
|
||||
&& skip(cs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -14128,6 +14147,10 @@ bool GasLimitsPrices::validate_skip(vm::CellSlice& cs, bool weak) const {
|
|||
case gas_prices_ext:
|
||||
return cs.fetch_ulong(8) == 0xde
|
||||
&& cs.advance(448);
|
||||
case gas_flat_pfx:
|
||||
return cs.fetch_ulong(8) == 0xd1
|
||||
&& cs.advance(128)
|
||||
&& validate_skip(cs, weak);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -14165,6 +14188,32 @@ bool GasLimitsPrices::cell_unpack(Ref<vm::Cell> cell_ref, GasLimitsPrices::Recor
|
|||
return unpack(cs, data) && cs.empty_ext();
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::unpack(vm::CellSlice& cs, GasLimitsPrices::Record_gas_flat_pfx& data) const {
|
||||
return cs.fetch_ulong(8) == 0xd1
|
||||
&& cs.fetch_uint_to(64, data.flat_gas_limit)
|
||||
&& cs.fetch_uint_to(64, data.flat_gas_price)
|
||||
&& fetch_to(cs, data.other);
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::unpack_gas_flat_pfx(vm::CellSlice& cs, unsigned long long& flat_gas_limit, unsigned long long& flat_gas_price, Ref<CellSlice>& other) const {
|
||||
return cs.fetch_ulong(8) == 0xd1
|
||||
&& cs.fetch_uint_to(64, flat_gas_limit)
|
||||
&& cs.fetch_uint_to(64, flat_gas_price)
|
||||
&& fetch_to(cs, other);
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::cell_unpack(Ref<vm::Cell> cell_ref, GasLimitsPrices::Record_gas_flat_pfx& data) const {
|
||||
if (cell_ref.is_null()) { return false; }
|
||||
auto cs = load_cell_slice(std::move(cell_ref));
|
||||
return unpack(cs, data) && cs.empty_ext();
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::cell_unpack_gas_flat_pfx(Ref<vm::Cell> cell_ref, unsigned long long& flat_gas_limit, unsigned long long& flat_gas_price, Ref<CellSlice>& other) const {
|
||||
if (cell_ref.is_null()) { return false; }
|
||||
auto cs = load_cell_slice(std::move(cell_ref));
|
||||
return unpack_gas_flat_pfx(cs, flat_gas_limit, flat_gas_price, other) && cs.empty_ext();
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::pack(vm::CellBuilder& cb, const GasLimitsPrices::Record_gas_prices& data) const {
|
||||
return cb.store_long_bool(0xdd, 8)
|
||||
&& cb.store_ulong_rchk_bool(data.gas_price, 64)
|
||||
|
|
@ -14196,6 +14245,30 @@ bool GasLimitsPrices::cell_pack(Ref<vm::Cell>& cell_ref, const GasLimitsPrices::
|
|||
return pack(cb, data) && std::move(cb).finalize_to(cell_ref);
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::pack(vm::CellBuilder& cb, const GasLimitsPrices::Record_gas_flat_pfx& data) const {
|
||||
return cb.store_long_bool(0xd1, 8)
|
||||
&& cb.store_ulong_rchk_bool(data.flat_gas_limit, 64)
|
||||
&& cb.store_ulong_rchk_bool(data.flat_gas_price, 64)
|
||||
&& store_from(cb, data.other);
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::pack_gas_flat_pfx(vm::CellBuilder& cb, unsigned long long flat_gas_limit, unsigned long long flat_gas_price, Ref<CellSlice> other) const {
|
||||
return cb.store_long_bool(0xd1, 8)
|
||||
&& cb.store_ulong_rchk_bool(flat_gas_limit, 64)
|
||||
&& cb.store_ulong_rchk_bool(flat_gas_price, 64)
|
||||
&& store_from(cb, other);
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::cell_pack(Ref<vm::Cell>& cell_ref, const GasLimitsPrices::Record_gas_flat_pfx& data) const {
|
||||
vm::CellBuilder cb;
|
||||
return pack(cb, data) && std::move(cb).finalize_to(cell_ref);
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::cell_pack_gas_flat_pfx(Ref<vm::Cell>& cell_ref, unsigned long long flat_gas_limit, unsigned long long flat_gas_price, Ref<CellSlice> other) const {
|
||||
vm::CellBuilder cb;
|
||||
return pack_gas_flat_pfx(cb, flat_gas_limit, flat_gas_price, std::move(other)) && std::move(cb).finalize_to(cell_ref);
|
||||
}
|
||||
|
||||
bool GasLimitsPrices::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
|
||||
switch (get_tag(cs)) {
|
||||
case gas_prices:
|
||||
|
|
@ -14219,6 +14292,14 @@ bool GasLimitsPrices::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
|
|||
&& pp.fetch_uint_field(cs, 64, "freeze_due_limit")
|
||||
&& pp.fetch_uint_field(cs, 64, "delete_due_limit")
|
||||
&& pp.close();
|
||||
case gas_flat_pfx:
|
||||
return cs.fetch_ulong(8) == 0xd1
|
||||
&& pp.open("gas_flat_pfx")
|
||||
&& pp.fetch_uint_field(cs, 64, "flat_gas_limit")
|
||||
&& pp.fetch_uint_field(cs, 64, "flat_gas_price")
|
||||
&& pp.field("other")
|
||||
&& print_skip(pp, cs)
|
||||
&& pp.close();
|
||||
}
|
||||
return pp.fail("unknown constructor for GasLimitsPrices");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3831,24 +3831,25 @@ struct TransactionDescr::Record_trans_ord {
|
|||
struct TransactionDescr::Record_trans_tick_tock {
|
||||
typedef TransactionDescr type_class;
|
||||
bool is_tock; // is_tock : Bool
|
||||
Ref<CellSlice> storage; // storage : TrStoragePhase
|
||||
Ref<CellSlice> storage_ph; // storage_ph : TrStoragePhase
|
||||
Ref<CellSlice> compute_ph; // compute_ph : TrComputePhase
|
||||
Ref<CellSlice> action; // action : Maybe ^TrActionPhase
|
||||
bool aborted; // aborted : Bool
|
||||
bool destroyed; // destroyed : Bool
|
||||
Record_trans_tick_tock() = default;
|
||||
Record_trans_tick_tock(bool _is_tock, Ref<CellSlice> _storage, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : is_tock(_is_tock), storage(std::move(_storage)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
|
||||
Record_trans_tick_tock(bool _is_tock, Ref<CellSlice> _storage_ph, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : is_tock(_is_tock), storage_ph(std::move(_storage_ph)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
|
||||
};
|
||||
|
||||
struct TransactionDescr::Record_trans_split_prepare {
|
||||
typedef TransactionDescr type_class;
|
||||
Ref<CellSlice> split_info; // split_info : SplitMergeInfo
|
||||
Ref<CellSlice> storage_ph; // storage_ph : Maybe TrStoragePhase
|
||||
Ref<CellSlice> compute_ph; // compute_ph : TrComputePhase
|
||||
Ref<CellSlice> action; // action : Maybe ^TrActionPhase
|
||||
bool aborted; // aborted : Bool
|
||||
bool destroyed; // destroyed : Bool
|
||||
Record_trans_split_prepare() = default;
|
||||
Record_trans_split_prepare(Ref<CellSlice> _split_info, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : split_info(std::move(_split_info)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
|
||||
Record_trans_split_prepare(Ref<CellSlice> _split_info, Ref<CellSlice> _storage_ph, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : split_info(std::move(_split_info)), storage_ph(std::move(_storage_ph)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
|
||||
};
|
||||
|
||||
struct TransactionDescr::Record_trans_split_install {
|
||||
|
|
@ -3873,13 +3874,14 @@ struct TransactionDescr::Record_trans_merge_install {
|
|||
typedef TransactionDescr type_class;
|
||||
Ref<CellSlice> split_info; // split_info : SplitMergeInfo
|
||||
Ref<Cell> prepare_transaction; // prepare_transaction : ^Transaction
|
||||
Ref<CellSlice> storage_ph; // storage_ph : Maybe TrStoragePhase
|
||||
Ref<CellSlice> credit_ph; // credit_ph : Maybe TrCreditPhase
|
||||
Ref<CellSlice> compute_ph; // compute_ph : TrComputePhase
|
||||
Ref<CellSlice> action; // action : Maybe ^TrActionPhase
|
||||
bool aborted; // aborted : Bool
|
||||
bool destroyed; // destroyed : Bool
|
||||
Record_trans_merge_install() = default;
|
||||
Record_trans_merge_install(Ref<CellSlice> _split_info, Ref<Cell> _prepare_transaction, Ref<CellSlice> _credit_ph, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : split_info(std::move(_split_info)), prepare_transaction(std::move(_prepare_transaction)), credit_ph(std::move(_credit_ph)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
|
||||
Record_trans_merge_install(Ref<CellSlice> _split_info, Ref<Cell> _prepare_transaction, Ref<CellSlice> _storage_ph, Ref<CellSlice> _credit_ph, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : split_info(std::move(_split_info)), prepare_transaction(std::move(_prepare_transaction)), storage_ph(std::move(_storage_ph)), credit_ph(std::move(_credit_ph)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
|
||||
};
|
||||
|
||||
extern const TransactionDescr t_TransactionDescr;
|
||||
|
|
@ -6166,11 +6168,12 @@ extern const StoragePrices t_StoragePrices;
|
|||
//
|
||||
|
||||
struct GasLimitsPrices final : TLB_Complex {
|
||||
enum { gas_prices, gas_prices_ext };
|
||||
enum { gas_flat_pfx, gas_prices, gas_prices_ext };
|
||||
static constexpr int cons_len_exact = 8;
|
||||
static constexpr unsigned char cons_tag[2] = { 0xdd, 0xde };
|
||||
static constexpr unsigned char cons_tag[3] = { 0xd1, 0xdd, 0xde };
|
||||
struct Record_gas_prices;
|
||||
struct Record_gas_prices_ext;
|
||||
struct Record_gas_flat_pfx;
|
||||
bool skip(vm::CellSlice& cs) const override;
|
||||
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
|
||||
bool unpack(vm::CellSlice& cs, Record_gas_prices& data) const;
|
||||
|
|
@ -6181,6 +6184,14 @@ struct GasLimitsPrices final : TLB_Complex {
|
|||
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_gas_prices_ext& data) const;
|
||||
bool pack(vm::CellBuilder& cb, const Record_gas_prices_ext& data) const;
|
||||
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_gas_prices_ext& data) const;
|
||||
bool unpack(vm::CellSlice& cs, Record_gas_flat_pfx& data) const;
|
||||
bool unpack_gas_flat_pfx(vm::CellSlice& cs, unsigned long long& flat_gas_limit, unsigned long long& flat_gas_price, Ref<CellSlice>& other) const;
|
||||
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_gas_flat_pfx& data) const;
|
||||
bool cell_unpack_gas_flat_pfx(Ref<vm::Cell> cell_ref, unsigned long long& flat_gas_limit, unsigned long long& flat_gas_price, Ref<CellSlice>& other) const;
|
||||
bool pack(vm::CellBuilder& cb, const Record_gas_flat_pfx& data) const;
|
||||
bool pack_gas_flat_pfx(vm::CellBuilder& cb, unsigned long long flat_gas_limit, unsigned long long flat_gas_price, Ref<CellSlice> other) const;
|
||||
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_gas_flat_pfx& data) const;
|
||||
bool cell_pack_gas_flat_pfx(Ref<vm::Cell>& cell_ref, unsigned long long flat_gas_limit, unsigned long long flat_gas_price, Ref<CellSlice> other) const;
|
||||
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
||||
std::ostream& print_type(std::ostream& os) const override {
|
||||
return os << "GasLimitsPrices";
|
||||
|
|
@ -6214,6 +6225,15 @@ struct GasLimitsPrices::Record_gas_prices_ext {
|
|||
Record_gas_prices_ext(unsigned long long _gas_price, unsigned long long _gas_limit, unsigned long long _special_gas_limit, unsigned long long _gas_credit, unsigned long long _block_gas_limit, unsigned long long _freeze_due_limit, unsigned long long _delete_due_limit) : gas_price(_gas_price), gas_limit(_gas_limit), special_gas_limit(_special_gas_limit), gas_credit(_gas_credit), block_gas_limit(_block_gas_limit), freeze_due_limit(_freeze_due_limit), delete_due_limit(_delete_due_limit) {}
|
||||
};
|
||||
|
||||
struct GasLimitsPrices::Record_gas_flat_pfx {
|
||||
typedef GasLimitsPrices type_class;
|
||||
unsigned long long flat_gas_limit; // flat_gas_limit : uint64
|
||||
unsigned long long flat_gas_price; // flat_gas_price : uint64
|
||||
Ref<CellSlice> other; // other : GasLimitsPrices
|
||||
Record_gas_flat_pfx() = default;
|
||||
Record_gas_flat_pfx(unsigned long long _flat_gas_limit, unsigned long long _flat_gas_price, Ref<CellSlice> _other) : flat_gas_limit(_flat_gas_limit), flat_gas_price(_flat_gas_price), other(std::move(_other)) {}
|
||||
};
|
||||
|
||||
extern const GasLimitsPrices t_GasLimitsPrices;
|
||||
|
||||
//
|
||||
|
|
@ -6522,7 +6542,7 @@ extern const ValidatorSignedTempKey t_ValidatorSignedTempKey;
|
|||
//
|
||||
|
||||
struct ConfigParam final : TLB_Complex {
|
||||
enum { cons32, cons33, cons34, cons35, cons36, cons37, config_mc_block_limits, config_block_limits, cons14, cons0, cons1, cons2, cons3, cons4, cons6, cons7, cons9, cons12, cons15, cons16, cons17, cons18, cons31, cons39, cons28, cons8, cons29, config_mc_gas_prices, config_gas_prices, config_mc_fwd_prices, config_fwd_prices };
|
||||
enum { cons32, cons33, cons34, cons35, cons36, cons37, config_mc_block_limits, config_block_limits, cons14, cons0, cons1, cons2, cons3, cons4, cons6, cons7, cons9, cons12, cons15, cons16, cons17, cons18, cons31, cons39, cons28, cons8, config_mc_gas_prices, config_gas_prices, cons29, config_mc_fwd_prices, config_fwd_prices };
|
||||
static constexpr int cons_len_exact = 0;
|
||||
int m_;
|
||||
ConfigParam(int m) : m_(m) {}
|
||||
|
|
|
|||
|
|
@ -1143,6 +1143,20 @@ bool TrStoragePhase::validate_skip(vm::CellSlice& cs, bool weak) const {
|
|||
&& t_AccStatusChange.validate_skip(cs, weak); // status_change:AccStatusChange
|
||||
}
|
||||
|
||||
bool TrStoragePhase::get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const {
|
||||
return t_Grams.as_integer_skip_to(cs, storage_fees); // storage_fees_collected:Grams
|
||||
}
|
||||
|
||||
bool TrStoragePhase::maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const {
|
||||
auto z = cs.fetch_ulong(1);
|
||||
if (!z) {
|
||||
storage_fees = td::make_refint(0);
|
||||
return true;
|
||||
} else {
|
||||
return z == 1 && get_storage_fees(cs, storage_fees);
|
||||
}
|
||||
}
|
||||
|
||||
const TrStoragePhase t_TrStoragePhase;
|
||||
|
||||
bool TrCreditPhase::skip(vm::CellSlice& cs) const {
|
||||
|
|
@ -1322,13 +1336,14 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
|
|||
&& t_TrStoragePhase.skip(cs); // storage_ph:TrStoragePhase
|
||||
case trans_tick_tock:
|
||||
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
|
||||
&& t_TrStoragePhase.skip(cs) // storage:TrStoragePhase
|
||||
&& t_TrStoragePhase.skip(cs) // storage_ph:TrStoragePhase
|
||||
&& t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase
|
||||
&& Maybe<RefTo<TrActionPhase>>{}.skip(cs) // action:(Maybe ^TrActionPhase)
|
||||
&& cs.advance(2); // aborted:Bool destroyed:Bool
|
||||
case trans_split_prepare:
|
||||
return cs.advance(4) // trans_split_prepare$0100
|
||||
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
|
||||
&& Maybe<TrStoragePhase>{}.skip(cs) // storage_ph:(Maybe TrStoragePhase)
|
||||
&& t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase
|
||||
&& Maybe<RefTo<TrActionPhase>>{}.skip(cs) // action:(Maybe ^TrActionPhase)
|
||||
&& cs.advance(2); // aborted:Bool destroyed:Bool
|
||||
|
|
@ -1346,6 +1361,7 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
|
|||
return cs.advance(4) // trans_merge_install$0111
|
||||
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
|
||||
&& t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction
|
||||
&& Maybe<TrStoragePhase>{}.skip(cs) // storage_ph:(Maybe TrStoragePhase)
|
||||
&& Maybe<TrCreditPhase>{}.skip(cs) // credit_ph:(Maybe TrCreditPhase)
|
||||
&& Maybe<TrComputePhase>{}.skip(cs) // compute_ph:TrComputePhase
|
||||
&& Maybe<RefTo<TrActionPhase>>{}.skip(cs) // action:(Maybe ^TrActionPhase)
|
||||
|
|
@ -1370,13 +1386,14 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
|
|||
&& t_TrStoragePhase.validate_skip(cs, weak); // storage_ph:TrStoragePhase
|
||||
case trans_tick_tock:
|
||||
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
|
||||
&& t_TrStoragePhase.validate_skip(cs, weak) // storage:TrStoragePhase
|
||||
&& t_TrStoragePhase.validate_skip(cs, weak) // storage_ph:TrStoragePhase
|
||||
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase
|
||||
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
|
||||
&& cs.advance(2); // aborted:Bool destroyed:Bool
|
||||
case trans_split_prepare:
|
||||
return cs.advance(4) // trans_split_prepare$0100
|
||||
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
|
||||
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase)
|
||||
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase
|
||||
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
|
||||
&& cs.advance(2); // aborted:Bool destroyed:Bool
|
||||
|
|
@ -1394,6 +1411,7 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
|
|||
return cs.advance(4) // trans_merge_install$0111
|
||||
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
|
||||
&& t_Ref_Transaction.validate_skip(cs, weak) // prepare_transaction:^Transaction
|
||||
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase)
|
||||
&& Maybe<TrCreditPhase>{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase)
|
||||
&& Maybe<TrComputePhase>{}.validate_skip(cs, weak) // compute_ph:TrComputePhase
|
||||
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
|
||||
|
|
@ -1407,6 +1425,53 @@ int TransactionDescr::get_tag(const vm::CellSlice& cs) const {
|
|||
return (t >= 0 && t <= 7) ? (t == 3 ? 2 : t) : -1;
|
||||
}
|
||||
|
||||
bool TransactionDescr::skip_to_storage_phase(vm::CellSlice& cs, bool& found) const {
|
||||
found = false;
|
||||
switch (get_tag(cs)) {
|
||||
case trans_ord:
|
||||
return cs.advance(4 + 1) // trans_ord$0000 storage_first:Bool
|
||||
&& cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase)
|
||||
case trans_storage:
|
||||
return cs.advance(4) // trans_storage$0001
|
||||
&& (found = true); // storage_ph:TrStoragePhase
|
||||
case trans_tick_tock:
|
||||
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
|
||||
&& (found = true); // storage_ph:TrStoragePhase
|
||||
case trans_split_prepare:
|
||||
return cs.advance(4) // trans_split_prepare$0100
|
||||
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
|
||||
&& cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase)
|
||||
case trans_split_install:
|
||||
return true;
|
||||
case trans_merge_prepare:
|
||||
return cs.advance(4) // trans_merge_prepare$0110
|
||||
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
|
||||
&& (found = true); // storage_ph:TrStoragePhase
|
||||
case trans_merge_install:
|
||||
return cs.advance(4) // trans_merge_install$0111
|
||||
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
|
||||
&& t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction
|
||||
&& cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TransactionDescr::get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const {
|
||||
if (cell.is_null()) {
|
||||
return false;
|
||||
}
|
||||
auto cs = vm::load_cell_slice(std::move(cell));
|
||||
bool found;
|
||||
if (!skip_to_storage_phase(cs, found)) {
|
||||
return false;
|
||||
} else if (found) {
|
||||
return t_TrStoragePhase.get_storage_fees(cs, storage_fees);
|
||||
} else {
|
||||
storage_fees = td::make_refint(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const TransactionDescr t_TransactionDescr;
|
||||
|
||||
bool Transaction_aux::skip(vm::CellSlice& cs) const {
|
||||
|
|
@ -1447,6 +1512,32 @@ bool Transaction::validate_skip(vm::CellSlice& cs, bool weak) const {
|
|||
&& RefTo<TransactionDescr>{}.validate_skip(cs, weak); // description:^TransactionDescr
|
||||
}
|
||||
|
||||
bool Transaction::get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const {
|
||||
Ref<vm::Cell> tdescr;
|
||||
return get_descr(std::move(cell), tdescr) && t_TransactionDescr.get_storage_fees(std::move(tdescr), storage_fees);
|
||||
}
|
||||
|
||||
bool Transaction::get_descr(Ref<vm::Cell> cell, Ref<vm::Cell>& tdescr) const {
|
||||
if (cell.is_null()) {
|
||||
return false;
|
||||
} else {
|
||||
auto cs = vm::load_cell_slice(std::move(cell));
|
||||
return cs.is_valid() && get_descr(cs, tdescr) && cs.empty_ext();
|
||||
}
|
||||
}
|
||||
|
||||
bool Transaction::get_descr(vm::CellSlice& cs, Ref<vm::Cell>& tdescr) const {
|
||||
return cs.advance(
|
||||
4 + 256 + 64 + 256 + 64 + 32 +
|
||||
15) // transaction$0111 account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15
|
||||
&& t_AccountStatus.skip(cs) // orig_status:AccountStatus
|
||||
&& t_AccountStatus.skip(cs) // end_status:AccountStatus
|
||||
&& cs.advance_refs(1) // ^[ in_msg:(Maybe ^Message) out_msgs:(HashmapE 15 ^Message) ]
|
||||
&& t_CurrencyCollection.skip(cs) // total_fees:CurrencyCollection
|
||||
&& cs.advance_refs(1) // state_update:^(MERKLE_UPDATE Account)
|
||||
&& cs.fetch_ref_to(tdescr); // description:^TransactionDescr
|
||||
}
|
||||
|
||||
bool Transaction::get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const {
|
||||
return cs.is_valid() && cs.fetch_ulong(4) == 7 // transaction$0111
|
||||
&&
|
||||
|
|
|
|||
|
|
@ -614,6 +614,8 @@ extern const AccStatusChange t_AccStatusChange;
|
|||
struct TrStoragePhase final : TLB_Complex {
|
||||
bool skip(vm::CellSlice& cs) const override;
|
||||
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
|
||||
bool get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const;
|
||||
bool maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const;
|
||||
};
|
||||
|
||||
extern const TrStoragePhase t_TrStoragePhase;
|
||||
|
|
@ -693,6 +695,8 @@ struct TransactionDescr final : TLB_Complex {
|
|||
bool skip(vm::CellSlice& cs) const override;
|
||||
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
|
||||
int get_tag(const vm::CellSlice& cs) const override;
|
||||
bool skip_to_storage_phase(vm::CellSlice& cs, bool& found) const;
|
||||
bool get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const;
|
||||
};
|
||||
|
||||
extern const TransactionDescr t_TransactionDescr;
|
||||
|
|
@ -708,6 +712,9 @@ struct Transaction final : TLB_Complex {
|
|||
bool skip(vm::CellSlice& cs) const override;
|
||||
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
|
||||
bool get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const;
|
||||
bool get_descr(Ref<vm::Cell> cell, Ref<vm::Cell>& tdescr) const;
|
||||
bool get_descr(vm::CellSlice& cs, Ref<vm::Cell>& tdescr) const;
|
||||
bool get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const;
|
||||
};
|
||||
|
||||
extern const Transaction t_Transaction;
|
||||
|
|
|
|||
|
|
@ -368,14 +368,14 @@ std::unique_ptr<MsgProcessedUptoCollection> MsgProcessedUptoCollection::unpack(t
|
|||
return v && v->valid ? std::move(v) : std::unique_ptr<MsgProcessedUptoCollection>{};
|
||||
}
|
||||
|
||||
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const& {
|
||||
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const & {
|
||||
return ton::shard_is_ancestor(shard, other.shard) && mc_seqno >= other.mc_seqno &&
|
||||
(last_inmsg_lt > other.last_inmsg_lt ||
|
||||
(last_inmsg_lt == other.last_inmsg_lt && !(last_inmsg_hash < other.last_inmsg_hash)));
|
||||
}
|
||||
|
||||
bool MsgProcessedUpto::contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
|
||||
ton::BlockSeqno other_mc_seqno) const& {
|
||||
ton::BlockSeqno other_mc_seqno) const & {
|
||||
return ton::shard_is_ancestor(shard, other_shard) && mc_seqno >= other_mc_seqno &&
|
||||
(last_inmsg_lt > other_lt || (last_inmsg_lt == other_lt && !(last_inmsg_hash < other_hash)));
|
||||
}
|
||||
|
|
@ -552,7 +552,9 @@ bool MsgProcessedUpto::already_processed(const EnqueuedMsgDescr& msg) const {
|
|||
if (msg.lt_ == last_inmsg_lt && last_inmsg_hash < msg.hash_) {
|
||||
return false;
|
||||
}
|
||||
if (ton::shard_contains(shard, msg.cur_prefix_.account_id_prefix)) {
|
||||
if (msg.same_workchain() && ton::shard_contains(shard, msg.cur_prefix_.account_id_prefix)) {
|
||||
// this branch is needed only for messages generated in the same shard
|
||||
// (such messages could have been processed without a reference from the masterchain)
|
||||
// ? enable this branch only if an extra boolean parameter is set ?
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,6 +145,9 @@ struct EnqueuedMsgDescr {
|
|||
return false;
|
||||
}
|
||||
bool unpack(vm::CellSlice& cs);
|
||||
bool same_workchain() const {
|
||||
return cur_prefix_.workchain == next_prefix_.workchain;
|
||||
}
|
||||
};
|
||||
|
||||
using compute_shard_end_lt_func_t = std::function<ton::LogicalTime(ton::AccountIdPrefixFull)>;
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ trans_ord$0000 credit_first:Bool
|
|||
trans_storage$0001 storage_ph:TrStoragePhase
|
||||
= TransactionDescr;
|
||||
|
||||
trans_tick_tock$001 is_tock:Bool storage:TrStoragePhase
|
||||
trans_tick_tock$001 is_tock:Bool storage_ph:TrStoragePhase
|
||||
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
|
||||
aborted:Bool destroyed:Bool = TransactionDescr;
|
||||
//
|
||||
|
|
@ -333,6 +333,7 @@ split_merge_info$_ cur_shard_pfx_len:(## 6)
|
|||
acc_split_depth:(## 6) this_addr:bits256 sibling_addr:bits256
|
||||
= SplitMergeInfo;
|
||||
trans_split_prepare$0100 split_info:SplitMergeInfo
|
||||
storage_ph:(Maybe TrStoragePhase)
|
||||
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
|
||||
aborted:Bool destroyed:Bool
|
||||
= TransactionDescr;
|
||||
|
|
@ -345,6 +346,7 @@ trans_merge_prepare$0110 split_info:SplitMergeInfo
|
|||
= TransactionDescr;
|
||||
trans_merge_install$0111 split_info:SplitMergeInfo
|
||||
prepare_transaction:^Transaction
|
||||
storage_ph:(Maybe TrStoragePhase)
|
||||
credit_ph:(Maybe TrCreditPhase)
|
||||
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
|
||||
aborted:Bool destroyed:Bool
|
||||
|
|
@ -609,6 +611,9 @@ gas_prices_ext#de gas_price:uint64 gas_limit:uint64 special_gas_limit:uint64 gas
|
|||
block_gas_limit:uint64 freeze_due_limit:uint64 delete_due_limit:uint64
|
||||
= GasLimitsPrices;
|
||||
|
||||
gas_flat_pfx#d1 flat_gas_limit:uint64 flat_gas_price:uint64 other:GasLimitsPrices
|
||||
= GasLimitsPrices;
|
||||
|
||||
config_mc_gas_prices#_ GasLimitsPrices = ConfigParam 20;
|
||||
config_gas_prices#_ GasLimitsPrices = ConfigParam 21;
|
||||
|
||||
|
|
|
|||
|
|
@ -672,9 +672,56 @@ bool Transaction::prepare_credit_phase() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ComputePhaseConfig::parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit,
|
||||
td::RefInt256& delete_due_limit) {
|
||||
return cell.not_null() &&
|
||||
parse_GasLimitsPrices(vm::load_cell_slice_ref(std::move(cell)), freeze_due_limit, delete_due_limit);
|
||||
}
|
||||
|
||||
bool ComputePhaseConfig::parse_GasLimitsPrices(Ref<vm::CellSlice> cs, td::RefInt256& freeze_due_limit,
|
||||
td::RefInt256& delete_due_limit) {
|
||||
if (cs.is_null()) {
|
||||
return false;
|
||||
}
|
||||
block::gen::GasLimitsPrices::Record_gas_flat_pfx flat;
|
||||
if (tlb::csr_unpack(cs, flat)) {
|
||||
bool ok = parse_GasLimitsPrices(std::move(flat.other), freeze_due_limit, delete_due_limit);
|
||||
flat_gas_limit = flat.flat_gas_limit;
|
||||
flat_gas_price = flat.flat_gas_price;
|
||||
return ok;
|
||||
}
|
||||
flat_gas_limit = flat_gas_price = 0;
|
||||
auto f = [&](const auto& r, td::uint64 spec_limit) {
|
||||
gas_limit = r.gas_limit;
|
||||
special_gas_limit = spec_limit;
|
||||
gas_credit = r.gas_credit;
|
||||
gas_price = r.gas_price;
|
||||
freeze_due_limit = td::RefInt256{true, r.freeze_due_limit};
|
||||
delete_due_limit = td::RefInt256{true, r.delete_due_limit};
|
||||
};
|
||||
block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
|
||||
if (tlb::csr_unpack(cs, rec)) {
|
||||
f(rec, rec.special_gas_limit);
|
||||
} else {
|
||||
block::gen::GasLimitsPrices::Record_gas_prices rec0;
|
||||
if (tlb::csr_unpack(std::move(cs), rec0)) {
|
||||
f(rec0, rec0.gas_limit);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
compute_threshold();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ComputePhaseConfig::compute_threshold() {
|
||||
gas_price256 = td::RefInt256{true, gas_price};
|
||||
max_gas_threshold = td::rshift(gas_price256 * gas_limit, 16, 1);
|
||||
if (gas_limit > flat_gas_limit) {
|
||||
max_gas_threshold =
|
||||
td::rshift(gas_price256 * (gas_limit - flat_gas_limit), 16, 1) + td::make_refint(flat_gas_price);
|
||||
} else {
|
||||
max_gas_threshold = td::make_refint(flat_gas_price);
|
||||
}
|
||||
}
|
||||
|
||||
td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const {
|
||||
|
|
@ -684,8 +731,11 @@ td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const {
|
|||
if (nanograms >= max_gas_threshold) {
|
||||
return gas_limit;
|
||||
}
|
||||
auto res = td::div(std::move(nanograms) << 16, gas_price256);
|
||||
return res->to_long();
|
||||
if (nanograms < flat_gas_price) {
|
||||
return 0;
|
||||
}
|
||||
auto res = td::div((std::move(nanograms) - flat_gas_price) << 16, gas_price256);
|
||||
return res->to_long() + flat_gas_limit;
|
||||
}
|
||||
|
||||
td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
|
||||
|
|
@ -855,6 +905,16 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
|
|||
cp.skip_reason = ComputePhase::sk_no_gas;
|
||||
return true;
|
||||
}
|
||||
// Compute gas limits
|
||||
if (!compute_gas_limits(cp, cfg)) {
|
||||
compute_phase.reset();
|
||||
return false;
|
||||
}
|
||||
if (!cp.gas_limit && !cp.gas_credit) {
|
||||
// no gas
|
||||
cp.skip_reason = ComputePhase::sk_no_gas;
|
||||
return true;
|
||||
}
|
||||
if (in_msg_state.not_null()) {
|
||||
LOG(DEBUG) << "HASH(in_msg_state) = " << in_msg_state->get_hash().bits().to_hex(256)
|
||||
<< ", account_state_hash = " << account.state_hash.to_hex();
|
||||
|
|
@ -883,11 +943,6 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
|
|||
} else if (in_msg_state.not_null()) {
|
||||
unpack_msg_state(true); // use only libraries
|
||||
}
|
||||
// Compute gas limits
|
||||
if (!compute_gas_limits(cp, cfg)) {
|
||||
compute_phase.reset();
|
||||
return false;
|
||||
}
|
||||
// initialize VM
|
||||
Ref<vm::Stack> stack = prepare_vm_stack(cp);
|
||||
if (stack.is_null()) {
|
||||
|
|
|
|||
|
|
@ -98,6 +98,8 @@ struct ComputePhaseConfig {
|
|||
td::uint64 gas_limit;
|
||||
td::uint64 special_gas_limit;
|
||||
td::uint64 gas_credit;
|
||||
td::uint64 flat_gas_limit = 0;
|
||||
td::uint64 flat_gas_price = 0;
|
||||
static constexpr td::uint64 gas_infty = (1ULL << 63) - 1;
|
||||
td::RefInt256 gas_price256;
|
||||
td::RefInt256 max_gas_threshold;
|
||||
|
|
@ -126,6 +128,8 @@ struct ComputePhaseConfig {
|
|||
Ref<vm::Cell> get_lib_root() const {
|
||||
return libraries ? libraries->get_root_cell() : Ref<vm::Cell>{};
|
||||
}
|
||||
bool parse_GasLimitsPrices(Ref<vm::CellSlice> cs, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit);
|
||||
bool parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit);
|
||||
};
|
||||
|
||||
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class SourceLookup {
|
|||
if (os_time_) {
|
||||
return os_time_->now();
|
||||
}
|
||||
return static_cast<td::uint32>(td::Time::now());
|
||||
return static_cast<td::uint32>(td::Clocks::system());
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
{ bl word 1 { -rot 2 'nop does swap 0 (create) }
|
||||
} :: 2=:
|
||||
{ <b swap s, b> } : s>c
|
||||
{ s>c hash } : shash
|
||||
{ s>c hashB } : shash
|
||||
// to be more efficiently re-implemented in C++ in the future
|
||||
{ dup 0< ' negate if } : abs
|
||||
{ 2dup > ' swap if } : minmax
|
||||
|
|
|
|||
|
|
@ -764,13 +764,17 @@ void interpret_string_to_bytes(vm::Stack& stack) {
|
|||
stack.push_bytes(stack.pop_string());
|
||||
}
|
||||
|
||||
void interpret_bytes_hash(vm::Stack& stack) {
|
||||
void interpret_bytes_hash(vm::Stack& stack, bool as_uint) {
|
||||
std::string str = stack.pop_bytes();
|
||||
unsigned char buffer[32];
|
||||
digest::hash_str<digest::SHA256>(buffer, str.c_str(), str.size());
|
||||
td::RefInt256 x{true};
|
||||
x.write().import_bytes(buffer, 32, false);
|
||||
stack.push_int(std::move(x));
|
||||
if (as_uint) {
|
||||
td::RefInt256 x{true};
|
||||
x.write().import_bytes(buffer, 32, false);
|
||||
stack.push_int(std::move(x));
|
||||
} else {
|
||||
stack.push_bytes(std::string{(char*)buffer, 32});
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_empty(vm::Stack& stack) {
|
||||
|
|
@ -892,11 +896,15 @@ void interpret_builder_remaining_bitrefs(vm::Stack& stack, int mode) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_cell_hash(vm::Stack& stack) {
|
||||
void interpret_cell_hash(vm::Stack& stack, bool as_uint) {
|
||||
auto cell = stack.pop_cell();
|
||||
td::RefInt256 hash{true};
|
||||
hash.write().import_bytes(cell->get_hash().as_slice().ubegin(), 32, false);
|
||||
stack.push_int(std::move(hash));
|
||||
if (as_uint) {
|
||||
td::RefInt256 hash{true};
|
||||
hash.write().import_bytes(cell->get_hash().as_slice().ubegin(), 32, false);
|
||||
stack.push_int(std::move(hash));
|
||||
} else {
|
||||
stack.push_bytes(cell->get_hash().as_slice().str());
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_store_ref(vm::Stack& stack) {
|
||||
|
|
@ -934,7 +942,9 @@ void interpret_fetch(vm::Stack& stack, int mode) {
|
|||
auto n = stack.pop_smallint_range(256 + (mode & 1));
|
||||
auto cs = stack.pop_cellslice();
|
||||
if (!cs->have(n)) {
|
||||
stack.push(std::move(cs));
|
||||
if (mode & 2) {
|
||||
stack.push(std::move(cs));
|
||||
}
|
||||
stack.push_bool(false);
|
||||
if (!(mode & 4)) {
|
||||
throw IntError{"end of data while reading integer from cell"};
|
||||
|
|
@ -959,7 +969,9 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) {
|
|||
unsigned n = stack.pop_smallint_range(127);
|
||||
auto cs = stack.pop_cellslice();
|
||||
if (!cs->have(n * 8)) {
|
||||
stack.push(std::move(cs));
|
||||
if (mode & 2) {
|
||||
stack.push(std::move(cs));
|
||||
}
|
||||
stack.push_bool(false);
|
||||
if (!(mode & 4)) {
|
||||
throw IntError{"end of data while reading byte string from cell"};
|
||||
|
|
@ -970,7 +982,7 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) {
|
|||
if (mode & 2) {
|
||||
cs.write().fetch_bytes(tmp, n);
|
||||
} else {
|
||||
cs.write().prefetch_bytes(tmp, n);
|
||||
cs->prefetch_bytes(tmp, n);
|
||||
}
|
||||
std::string s{tmp, tmp + n};
|
||||
if (mode & 1) {
|
||||
|
|
@ -978,7 +990,9 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) {
|
|||
} else {
|
||||
stack.push_string(std::move(s));
|
||||
}
|
||||
stack.push(std::move(cs));
|
||||
if (mode & 2) {
|
||||
stack.push(std::move(cs));
|
||||
}
|
||||
if (mode & 4) {
|
||||
stack.push_bool(true);
|
||||
}
|
||||
|
|
@ -1009,13 +1023,15 @@ void interpret_cell_remaining(vm::Stack& stack) {
|
|||
void interpret_fetch_ref(vm::Stack& stack, int mode) {
|
||||
auto cs = stack.pop_cellslice();
|
||||
if (!cs->have_refs(1)) {
|
||||
stack.push(std::move(cs));
|
||||
if (mode & 2) {
|
||||
stack.push(std::move(cs));
|
||||
}
|
||||
stack.push_bool(false);
|
||||
if (!(mode & 4)) {
|
||||
throw IntError{"end of data while reading reference from cell"};
|
||||
}
|
||||
} else {
|
||||
auto cell = (mode & 2) ? cs.write().fetch_ref() : cs.write().prefetch_ref();
|
||||
auto cell = (mode & 2) ? cs.write().fetch_ref() : cs->prefetch_ref();
|
||||
if (mode & 2) {
|
||||
stack.push(std::move(cs));
|
||||
}
|
||||
|
|
@ -2474,7 +2490,9 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("B>Lu@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x12));
|
||||
d.def_stack_word("B>Li@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x13));
|
||||
d.def_stack_word("$>B ", interpret_string_to_bytes);
|
||||
d.def_stack_word("Bhash ", interpret_bytes_hash);
|
||||
d.def_stack_word("Bhash ", std::bind(interpret_bytes_hash, _1, true));
|
||||
d.def_stack_word("Bhashu ", std::bind(interpret_bytes_hash, _1, true));
|
||||
d.def_stack_word("BhashB ", std::bind(interpret_bytes_hash, _1, false));
|
||||
// cell manipulation (create, write and modify cells)
|
||||
d.def_stack_word("<b ", interpret_empty);
|
||||
d.def_stack_word("i, ", std::bind(interpret_store, _1, true));
|
||||
|
|
@ -2496,7 +2514,9 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("brembits ", std::bind(interpret_builder_remaining_bitrefs, _1, 1));
|
||||
d.def_stack_word("bremrefs ", std::bind(interpret_builder_remaining_bitrefs, _1, 2));
|
||||
d.def_stack_word("brembitrefs ", std::bind(interpret_builder_remaining_bitrefs, _1, 3));
|
||||
d.def_stack_word("hash ", interpret_cell_hash);
|
||||
d.def_stack_word("hash ", std::bind(interpret_cell_hash, _1, true));
|
||||
d.def_stack_word("hashu ", std::bind(interpret_cell_hash, _1, true));
|
||||
d.def_stack_word("hashB ", std::bind(interpret_cell_hash, _1, false));
|
||||
// cellslice manipulation (read from cells)
|
||||
d.def_stack_word("<s ", interpret_from_cell);
|
||||
d.def_stack_word("i@ ", std::bind(interpret_fetch, _1, 1));
|
||||
|
|
|
|||
|
|
@ -628,7 +628,7 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
stack.opt_show();
|
||||
StackLayout layout1 = stack.vars();
|
||||
bool next_empty = next->is_empty();
|
||||
stack.o << (next_empty ? "WHILEEND:<{" : "WHILE:<{");
|
||||
stack.o << "WHILE:<{";
|
||||
stack.o.indent();
|
||||
stack.forget_const();
|
||||
block0->generate_code_all(stack);
|
||||
|
|
@ -638,7 +638,7 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
stack.modified();
|
||||
stack.o.undent();
|
||||
Stack stack_copy{stack};
|
||||
stack.o << (next_empty ? "}>" : "}>DO<{");
|
||||
stack.o << (next_empty ? "}>DO:" : "}>DO<{");
|
||||
if (!next_empty) {
|
||||
stack.o.indent();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,6 +169,11 @@ bool SourceReader::load_line() {
|
|||
error("line too long");
|
||||
return false;
|
||||
}
|
||||
if (len && cur_line.back() == '\r') {
|
||||
// CP/M line breaks support
|
||||
cur_line.pop_back();
|
||||
--len;
|
||||
}
|
||||
loc.text = cur_line;
|
||||
cur_line_len = (int)len;
|
||||
loc.line_pos = 0;
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ cr ."initial basechain state is:" cr dup <s csr. cr
|
|||
dup dup 31 boc+>B dup Bx. cr
|
||||
dup "basestate0" +suffix +".boc" tuck B>file
|
||||
."(Initial basechain state saved to file " type .")" cr
|
||||
Bhash dup =: basestate0_fhash
|
||||
Bhashu dup =: basestate0_fhash
|
||||
."file hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"basestate0" +suffix +".fhash" B>file
|
||||
hash dup =: basestate0_rhash
|
||||
hashu dup =: basestate0_rhash
|
||||
."root hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"basestate0" +suffix +".rhash" B>file
|
||||
|
||||
|
|
@ -227,10 +227,10 @@ cr cr ."new state is:" cr dup <s csr. cr
|
|||
dup 31 boc+>B dup Bx. cr
|
||||
dup "zerostate" +suffix +".boc" tuck B>file
|
||||
."(Initial masterchain state saved to file " type .")" cr
|
||||
Bhash dup =: zerostate_fhash
|
||||
Bhashu dup =: zerostate_fhash
|
||||
."file hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"zerostate" +suffix +".fhash" B>file
|
||||
hash dup =: zerostate_rhash ."root hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
hashu dup =: zerostate_rhash ."root hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"zerostate" +suffix +".rhash" B>file
|
||||
basestate0_rhash ."Basestate0 root hash=" dup x. space 256 u>B B>base64url type cr
|
||||
basestate0_fhash ."Basestate0 file hash=" dup x. space 256 u>B B>base64url type cr
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ cr ."initial basechain state is:" cr dup <s csr. cr
|
|||
dup dup 31 boc+>B dup Bx. cr
|
||||
dup "basestate0" +suffix +".boc" tuck B>file
|
||||
."(Initial basechain state saved to file " type .")" cr
|
||||
Bhash dup =: basestate0_fhash
|
||||
Bhashu dup =: basestate0_fhash
|
||||
."file hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"basestate0" +suffix +".fhash" B>file
|
||||
hash dup =: basestate0_rhash
|
||||
hashu dup =: basestate0_rhash
|
||||
."root hash=" dup x. space 256 u>B dup B>base64url type cr
|
||||
"basestate0" +suffix +".rhash" B>file
|
||||
|
||||
|
|
@ -231,10 +231,10 @@ cr cr ."new state is:" cr dup <s csr. cr
|
|||
dup 31 boc+>B dup Bx. cr
|
||||
dup "zerostate" +suffix +".boc" tuck B>file
|
||||
."(Initial masterchain state saved to file " type .")" cr
|
||||
Bhash dup =: zerostate_fhash
|
||||
Bhashu dup =: zerostate_fhash
|
||||
."file hash= " dup X. space 256 u>B dup B>base64url type cr
|
||||
"zerostate" +suffix +".fhash" B>file
|
||||
hash dup =: zerostate_rhash ."root hash= " dup X. space 256 u>B dup B>base64url type cr
|
||||
hashu dup =: zerostate_rhash ."root hash= " dup X. space 256 u>B dup B>base64url type cr
|
||||
"zerostate" +suffix +".rhash" B>file
|
||||
basestate0_rhash ."Basestate0 root hash= " dup X. space 256 u>B B>base64url type cr
|
||||
basestate0_fhash ."Basestate0 file hash= " dup X. space 256 u>B B>base64url type cr
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges)
|
||||
;; accepts orders for up to 254 internal messages (transfers) in one external message
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
var signature = in_msg~load_bits(512);
|
||||
var cs = in_msg;
|
||||
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
|
||||
throw_if(35, valid_until <= now());
|
||||
var ds = get_data().begin_parse();
|
||||
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
|
||||
ds.end_parse();
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
throw_unless(34, subwallet_id == stored_subwallet);
|
||||
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
var dict = cs~load_dict();
|
||||
cs.end_parse();
|
||||
accept_message();
|
||||
int i = -1;
|
||||
do {
|
||||
(i, var cs, var f) = dict.idict_get_next?(16, i);
|
||||
if (f) {
|
||||
var mode = cs~load_uint(8);
|
||||
send_raw_message(cs~load_ref(), mode);
|
||||
}
|
||||
} until (~ f);
|
||||
set_data(begin_cell()
|
||||
.store_uint(stored_seqno + 1, 32)
|
||||
.store_uint(stored_subwallet, 32)
|
||||
.store_uint(public_key, 256)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
|
||||
int seqno() method_id {
|
||||
return get_data().begin_parse().preload_uint(32);
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// automatically generated from `smartcont/stdlib.fc` `smartcont/highload-wallet-code.fc`
|
||||
PROGRAM{
|
||||
DECLPROC recv_internal
|
||||
DECLPROC recv_external
|
||||
85143 DECLMETHOD seqno
|
||||
recv_internal PROC:<{
|
||||
// in_msg
|
||||
DROP //
|
||||
}>
|
||||
recv_external PROC:<{
|
||||
// in_msg
|
||||
9 PUSHPOW2 // in_msg _3=512
|
||||
LDSLICEX // signature in_msg
|
||||
DUP // signature in_msg cs
|
||||
32 LDU // signature in_msg _9 cs
|
||||
32 LDU // signature in_msg _9 _12 cs
|
||||
32 LDU // signature in_msg subwallet_id valid_until msg_seqno cs
|
||||
NOW // signature in_msg subwallet_id valid_until msg_seqno cs _19
|
||||
s1 s3 XCHG // signature in_msg subwallet_id cs msg_seqno valid_until _19
|
||||
LEQ // signature in_msg subwallet_id cs msg_seqno _20
|
||||
35 THROWIF
|
||||
c4 PUSH // signature in_msg subwallet_id cs msg_seqno _23
|
||||
CTOS // signature in_msg subwallet_id cs msg_seqno ds
|
||||
32 LDU // signature in_msg subwallet_id cs msg_seqno _28 ds
|
||||
32 LDU // signature in_msg subwallet_id cs msg_seqno _28 _31 ds
|
||||
256 LDU // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key ds
|
||||
ENDS
|
||||
s3 s2 XCPU // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet msg_seqno stored_seqno
|
||||
EQUAL // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet _39
|
||||
33 THROWIFNOT
|
||||
s4 s4 XCPU // signature in_msg stored_subwallet cs public_key stored_seqno subwallet_id stored_subwallet
|
||||
EQUAL // signature in_msg stored_subwallet cs public_key stored_seqno _42
|
||||
34 THROWIFNOT
|
||||
s0 s4 XCHG // signature stored_seqno stored_subwallet cs public_key in_msg
|
||||
HASHSU // signature stored_seqno stored_subwallet cs public_key _45
|
||||
s0 s5 s5 XC2PU // public_key stored_seqno stored_subwallet cs _45 signature public_key
|
||||
CHKSIGNU // public_key stored_seqno stored_subwallet cs _46
|
||||
35 THROWIFNOT
|
||||
LDDICT // public_key stored_seqno stored_subwallet dict cs
|
||||
ENDS
|
||||
ACCEPT
|
||||
-1 PUSHINT // public_key stored_seqno stored_subwallet dict i=-1
|
||||
UNTIL:<{
|
||||
OVER
|
||||
16 PUSHINT // public_key stored_seqno stored_subwallet dict i dict _57=16
|
||||
DICTIGETNEXT
|
||||
NULLSWAPIFNOT
|
||||
NULLSWAPIFNOT // public_key stored_seqno stored_subwallet dict cs i f
|
||||
DUP // public_key stored_seqno stored_subwallet dict cs i f f
|
||||
IF:<{ // public_key stored_seqno stored_subwallet dict cs i f
|
||||
s0 s2 XCHG // public_key stored_seqno stored_subwallet dict f i cs
|
||||
8 LDU // public_key stored_seqno stored_subwallet dict f i mode cs
|
||||
LDREF // public_key stored_seqno stored_subwallet dict f i mode _100 _99
|
||||
DROP // public_key stored_seqno stored_subwallet dict f i mode _63
|
||||
SWAP // public_key stored_seqno stored_subwallet dict f i _63 mode
|
||||
SENDRAWMSG
|
||||
}>ELSE<{
|
||||
s2 POP // public_key stored_seqno stored_subwallet dict f i
|
||||
}>
|
||||
SWAP // public_key stored_seqno stored_subwallet dict i f
|
||||
NOT // public_key stored_seqno stored_subwallet dict i _66
|
||||
}> // public_key stored_seqno stored_subwallet dict i
|
||||
2DROP // public_key stored_seqno stored_subwallet
|
||||
SWAP // public_key stored_subwallet stored_seqno
|
||||
INC // public_key stored_subwallet _68
|
||||
NEWC // public_key stored_subwallet _68 _69
|
||||
32 STU // public_key stored_subwallet _71
|
||||
32 STU // public_key _73
|
||||
256 STU // _75
|
||||
ENDC // _76
|
||||
c4 POP
|
||||
}>
|
||||
seqno PROC:<{
|
||||
//
|
||||
c4 PUSH // _0
|
||||
CTOS // _1
|
||||
32 PLDU // _3
|
||||
}>
|
||||
}END>c
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges)
|
||||
;; accepts orders for up to 254 internal messages (transfers) in one external message
|
||||
;; this version does not use seqno for replay protection; instead, it remembers all recent query_ids
|
||||
;; in this way several external messages with different query_id can be sent in parallel
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
var signature = in_msg~load_bits(512);
|
||||
var cs = in_msg;
|
||||
var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64));
|
||||
var bound = (now() << 32);
|
||||
throw_if(35, query_id < bound);
|
||||
var ds = get_data().begin_parse();
|
||||
var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict());
|
||||
ds.end_parse();
|
||||
(_, var found?) = old_queries.udict_get?(64, query_id);
|
||||
throw_if(32, found?);
|
||||
throw_unless(34, subwallet_id == stored_subwallet);
|
||||
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
var dict = cs~load_dict();
|
||||
cs.end_parse();
|
||||
accept_message();
|
||||
int i = -1;
|
||||
do {
|
||||
(i, var cs, var f) = dict.idict_get_next?(16, i);
|
||||
if (f) {
|
||||
var mode = cs~load_uint(8);
|
||||
send_raw_message(cs~load_ref(), mode);
|
||||
}
|
||||
} until (~ f);
|
||||
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
|
||||
old_queries~udict_set_builder(64, query_id, begin_cell());
|
||||
var queries = old_queries;
|
||||
do {
|
||||
var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64);
|
||||
f~touch();
|
||||
if (f) {
|
||||
f = (i < bound);
|
||||
}
|
||||
if (f) {
|
||||
old_queries = old_queries';
|
||||
last_cleaned = i;
|
||||
}
|
||||
} until (~ f);
|
||||
set_data(begin_cell()
|
||||
.store_uint(stored_subwallet, 32)
|
||||
.store_uint(last_cleaned, 64)
|
||||
.store_uint(public_key, 256)
|
||||
.store_dict(old_queries)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
|
||||
;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten)
|
||||
int processed?(int query_id) method_id {
|
||||
var ds = get_data().begin_parse();
|
||||
var (_, last_cleaned, _, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict());
|
||||
ds.end_parse();
|
||||
(_, var found) = old_queries.udict_get?(64, query_id);
|
||||
return found ? true : - (query_id <= last_cleaned);
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
// automatically generated from `smartcont/stdlib.fc` `smartcont/highload-wallet-v2-code.fc`
|
||||
PROGRAM{
|
||||
DECLPROC recv_internal
|
||||
DECLPROC recv_external
|
||||
117746 DECLMETHOD processed?
|
||||
recv_internal PROC:<{
|
||||
// in_msg
|
||||
DROP //
|
||||
}>
|
||||
recv_external PROC:<{
|
||||
// in_msg
|
||||
9 PUSHPOW2 // in_msg _3=512
|
||||
LDSLICEX // signature in_msg
|
||||
DUP // signature in_msg cs
|
||||
32 LDU // signature in_msg _8 cs
|
||||
64 LDU // signature in_msg subwallet_id query_id cs
|
||||
NOW // signature in_msg subwallet_id query_id cs _15
|
||||
32 LSHIFT# // signature in_msg subwallet_id query_id cs bound
|
||||
s2 s0 PUSH2 // signature in_msg subwallet_id query_id cs bound query_id bound
|
||||
LESS // signature in_msg subwallet_id query_id cs bound _19
|
||||
35 THROWIF
|
||||
c4 PUSH // signature in_msg subwallet_id query_id cs bound _22
|
||||
CTOS // signature in_msg subwallet_id query_id cs bound ds
|
||||
32 LDU // signature in_msg subwallet_id query_id cs bound _28 ds
|
||||
64 LDU // signature in_msg subwallet_id query_id cs bound _28 _31 ds
|
||||
256 LDU // signature in_msg subwallet_id query_id cs bound _28 _31 _34 ds
|
||||
LDDICT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries ds
|
||||
ENDS
|
||||
s6 s0 PUSH2
|
||||
64 PUSHINT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries query_id old_queries _42=64
|
||||
DICTUGET
|
||||
NULLSWAPIFNOT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries _115 _116
|
||||
NIP // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries found?
|
||||
32 THROWIF
|
||||
s7 s3 XCPU // signature in_msg old_queries query_id cs bound stored_subwallet last_cleaned public_key subwallet_id stored_subwallet
|
||||
EQUAL // signature in_msg old_queries query_id cs bound stored_subwallet last_cleaned public_key _47
|
||||
34 THROWIFNOT
|
||||
s0 s7 XCHG // signature public_key old_queries query_id cs bound stored_subwallet last_cleaned in_msg
|
||||
HASHSU // signature public_key old_queries query_id cs bound stored_subwallet last_cleaned _50
|
||||
s0 s8 s7 XC2PU // last_cleaned public_key old_queries query_id cs bound stored_subwallet _50 signature public_key
|
||||
CHKSIGNU // last_cleaned public_key old_queries query_id cs bound stored_subwallet _51
|
||||
35 THROWIFNOT
|
||||
s0 s2 XCHG // last_cleaned public_key old_queries query_id stored_subwallet bound cs
|
||||
LDDICT // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs
|
||||
ENDS
|
||||
ACCEPT
|
||||
-1 PUSHINT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i=-1
|
||||
UNTIL:<{
|
||||
OVER
|
||||
16 PUSHINT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i dict _62=16
|
||||
DICTIGETNEXT
|
||||
NULLSWAPIFNOT
|
||||
NULLSWAPIFNOT // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f
|
||||
DUP // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f f
|
||||
IF:<{ // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f
|
||||
s0 s2 XCHG // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i cs
|
||||
8 LDU // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode cs
|
||||
LDREF // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode _125 _124
|
||||
DROP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode _68
|
||||
SWAP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i _68 mode
|
||||
SENDRAWMSG
|
||||
}>ELSE<{
|
||||
s2 POP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i
|
||||
}>
|
||||
SWAP // last_cleaned public_key old_queries query_id stored_subwallet bound dict i f
|
||||
NOT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i _71
|
||||
}> // last_cleaned public_key old_queries query_id stored_subwallet bound dict i
|
||||
2DROP // last_cleaned public_key old_queries query_id stored_subwallet bound
|
||||
38 PUSHPOW2 // last_cleaned public_key old_queries query_id stored_subwallet bound _74
|
||||
SUB // last_cleaned public_key old_queries query_id stored_subwallet bound
|
||||
NEWC // last_cleaned public_key old_queries query_id stored_subwallet bound _77
|
||||
s0 s3 s4 XCHG3
|
||||
64 PUSHINT // last_cleaned public_key stored_subwallet bound _77 query_id old_queries _78=64
|
||||
DICTUSETB // last_cleaned public_key stored_subwallet bound old_queries
|
||||
UNTIL:<{
|
||||
DUP
|
||||
64 PUSHINT // last_cleaned public_key stored_subwallet bound old_queries old_queries _85=64
|
||||
DICTUREMMIN
|
||||
NULLSWAPIFNOT
|
||||
NULLSWAPIFNOT // last_cleaned public_key stored_subwallet bound old_queries _126 _128 _127 _129
|
||||
s2 POP // last_cleaned public_key stored_subwallet bound old_queries old_queries' f i
|
||||
s1 s0 XCPU // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f f
|
||||
IF:<{ // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f
|
||||
DROP // last_cleaned public_key stored_subwallet bound old_queries old_queries' i
|
||||
s0 s3 PUSH2 // last_cleaned public_key stored_subwallet bound old_queries old_queries' i i bound
|
||||
LESS // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f
|
||||
}> // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f
|
||||
DUP // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f f
|
||||
IF:<{ // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f
|
||||
s3 POP
|
||||
s6 POP // last_cleaned public_key stored_subwallet bound f old_queries
|
||||
}>ELSE<{
|
||||
s3 s1 s3 XCHG3
|
||||
2DROP // last_cleaned public_key stored_subwallet bound f old_queries
|
||||
}>
|
||||
SWAP // last_cleaned public_key stored_subwallet bound old_queries f
|
||||
NOT // last_cleaned public_key stored_subwallet bound old_queries _90
|
||||
}> // last_cleaned public_key stored_subwallet bound old_queries
|
||||
NIP // last_cleaned public_key stored_subwallet old_queries
|
||||
NEWC // last_cleaned public_key stored_subwallet old_queries _91
|
||||
s1 s2 XCHG // last_cleaned public_key old_queries stored_subwallet _91
|
||||
32 STU // last_cleaned public_key old_queries _93
|
||||
s1 s3 XCHG // old_queries public_key last_cleaned _93
|
||||
64 STU // old_queries public_key _95
|
||||
256 STU // old_queries _97
|
||||
STDICT // _98
|
||||
ENDC // _99
|
||||
c4 POP
|
||||
}>
|
||||
processed? PROC:<{
|
||||
// query_id
|
||||
c4 PUSH // query_id _2
|
||||
CTOS // query_id ds
|
||||
32 LDU // query_id _29 _28
|
||||
NIP // query_id ds
|
||||
64 LDU // query_id _11 ds
|
||||
256 LDU // query_id _11 _33 _32
|
||||
NIP // query_id _11 ds
|
||||
LDDICT // query_id last_cleaned old_queries ds
|
||||
ENDS
|
||||
s2 s(-1) PUXC
|
||||
64 PUSHINT // query_id last_cleaned query_id old_queries _22=64
|
||||
DICTUGET
|
||||
NULLSWAPIFNOT // query_id last_cleaned _36 _37
|
||||
NIP // query_id last_cleaned found
|
||||
IF:<{ // query_id last_cleaned
|
||||
2DROP //
|
||||
TRUE // _24
|
||||
}>ELSE<{ // query_id last_cleaned
|
||||
LEQ // _26
|
||||
NEGATE // _24
|
||||
}>
|
||||
}>
|
||||
}END>c
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <subwallet-id> <order-file> [<savefile>]" cr
|
||||
."Creates a request with up to 254 orders loaded from <order-file> to high-load v2 (sub)wallet created by new-highload-v2-wallet.fif, with private key loaded from file <filename-base>.pk "
|
||||
."and address from <filename-base><subwallet-id>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr
|
||||
."<order-file> is a text file with lines `SEND <dest-addr> <amount>`" cr 1 halt
|
||||
} : usage
|
||||
$# dup 3 < swap 4 > or ' usage if
|
||||
|
||||
$1 =: file-base
|
||||
$2 parse-int dup 32 fits ' usage ifnot =: subwallet-id // parse subwallet-id
|
||||
{ subwallet-id (.) $+ } : +subwallet
|
||||
$3 =: order-file
|
||||
def? $4 { @' $4 } { "wallet-query" } cond constant savefile
|
||||
3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
|
||||
60 constant timeout // external message expires in 60 seconds
|
||||
|
||||
file-base +subwallet +".addr" load-address
|
||||
2dup 2constant wallet_addr
|
||||
."Source wallet address = " 2dup .addr cr 6 .Addr cr
|
||||
file-base +".pk" load-keypair nip constant wallet_pk
|
||||
|
||||
variable orders dictnew orders !
|
||||
variable order# order# 0!
|
||||
// c --
|
||||
{ <s order# @ dup 254 >= abort"more than 254 orders"
|
||||
orders @ 16 udict!+ not abort"cannot add order to dictionary"
|
||||
orders ! order# 1+!
|
||||
} : add-order
|
||||
// b body -- b'
|
||||
{ tuck <s 2dup s-fits? not rot over 1 i, -rot
|
||||
{ drop swap ref, } { s, nip } cond
|
||||
} : append-msg-body
|
||||
// ng wc addr bounce body -- c
|
||||
{ <b b{01} s, rot 1 i, b{000100} s, 2swap addr, rot Gram,
|
||||
0 9 64 32 + + 1+ u, swap append-msg-body b>
|
||||
} : create-int-msg
|
||||
// ng wc addr bnc --
|
||||
{ ."Transferring " 3 roll .GR ."to account "
|
||||
-rot 2dup 4 pick 7 + .Addr ." = " .addr ." bounce=" . cr
|
||||
} : .transfer
|
||||
// addr$ ng -- c
|
||||
{ swap parse-smc-addr // ng wc addr bnc
|
||||
2over 2over .transfer
|
||||
<b 0 32 u, b> create-int-msg
|
||||
} : create-simple-transfer
|
||||
// c m -- c'
|
||||
{ <b swap 8 u, swap ref, b> } : create-order
|
||||
|
||||
// addr$ ng --
|
||||
{ create-simple-transfer send-mode create-order add-order } : send
|
||||
{ bl word bl word $>GR send } : SEND
|
||||
|
||||
// parse order file
|
||||
order-file include
|
||||
|
||||
// create external message
|
||||
now timeout + 32 << <b orders @ dict, b> hashu 32 1<<1- and + =: query_id
|
||||
<b subwallet-id 32 i, query_id 64 u, orders @ dict, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hashu wallet_pk ed25519_sign_uint
|
||||
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
|
||||
swap B, swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
."Query_id is " query_id dup . ."= 0x" X. cr
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <subwallet-id> <seqno> <order-file> [<savefile>]" cr
|
||||
."Creates a request with up to 254 orders loaded from <order-file> to high-load (sub)wallet created by new-highload-wallet.fif, with private key loaded from file <filename-base>.pk "
|
||||
."and address from <filename-base><subwallet-id>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr
|
||||
."<order-file> is a text file with lines `SEND <dest-addr> <amount>`" cr 1 halt
|
||||
} : usage
|
||||
$# dup 4 < swap 5 > or ' usage if
|
||||
|
||||
$1 =: file-base
|
||||
$2 parse-int dup 32 fits ' usage ifnot =: subwallet-id // parse subwallet-id
|
||||
{ subwallet-id (.) $+ } : +subwallet
|
||||
$3 parse-int =: seqno
|
||||
$4 =: order-file
|
||||
def? $5 { @' $5 } { "wallet-query" } cond constant savefile
|
||||
3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
|
||||
60 constant timeout // external message expires in 60 seconds
|
||||
|
||||
file-base +subwallet +".addr" load-address
|
||||
2dup 2constant wallet_addr
|
||||
."Source wallet address = " 2dup .addr cr 6 .Addr cr
|
||||
file-base +".pk" load-keypair nip constant wallet_pk
|
||||
|
||||
variable orders dictnew orders !
|
||||
variable order# order# 0!
|
||||
// c --
|
||||
{ <s order# @ dup 254 >= abort"more than 254 orders"
|
||||
orders @ 16 udict!+ not abort"cannot add order to dictionary"
|
||||
orders ! order# 1+!
|
||||
} : add-order
|
||||
// b body -- b'
|
||||
{ tuck <s 2dup s-fits? not rot over 1 i, -rot
|
||||
{ drop swap ref, } { s, nip } cond
|
||||
} : append-msg-body
|
||||
// ng wc addr bounce body -- c
|
||||
{ <b b{01} s, rot 1 i, b{000100} s, 2swap addr, rot Gram,
|
||||
0 9 64 32 + + 1+ u, swap append-msg-body b>
|
||||
} : create-int-msg
|
||||
// ng wc addr bnc --
|
||||
{ ."Transferring " 3 roll .GR ."to account "
|
||||
-rot 2dup 4 pick 7 + .Addr ." = " .addr ." bounce=" . cr
|
||||
} : .transfer
|
||||
// addr$ ng -- c
|
||||
{ swap parse-smc-addr // ng wc addr bnc
|
||||
2over 2over .transfer
|
||||
<b 0 32 u, b> create-int-msg
|
||||
} : create-simple-transfer
|
||||
// c m -- c'
|
||||
{ <b swap 8 u, swap ref, b> } : create-order
|
||||
|
||||
// addr$ ng --
|
||||
{ create-simple-transfer send-mode create-order add-order } : send
|
||||
{ bl word bl word $>GR send } : SEND
|
||||
|
||||
// parse order file
|
||||
order-file include
|
||||
|
||||
// create external message
|
||||
<b subwallet-id 32 i, now timeout + 32 u, seqno 32 u, orders @ dict, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hashu wallet_pk ed25519_sign_uint
|
||||
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
|
||||
swap B, swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <workchain-id> <subwallet-id> [<filename-base>]" cr
|
||||
."Creates a new v2 high-load wallet in the specified workchain, with the controlling private key saved to or loaded from <filename-base>.pk "
|
||||
."('new-wallet.pk' by default)" cr
|
||||
."<subwallet-id> is the 32-bit identifier of this subwallet among all controlled by the same private key" cr 1 halt
|
||||
} : usage
|
||||
$# 2- -2 and ' usage if
|
||||
|
||||
$1 parse-workchain-id =: wc // set workchain id from command line argument
|
||||
$2 parse-int dup =: subwallet-id // parse subwallet-id
|
||||
32 fits ' usage ifnot
|
||||
{ subwallet-id (.) $+ } : +subwallet
|
||||
def? $3 { @' $3 } { "new-wallet" } cond constant file-base
|
||||
65536 constant timeout // init query times out in 65536 seconds
|
||||
|
||||
."Creating new v2 high-load wallet in workchain " wc .
|
||||
."with subwallet id " subwallet-id . cr
|
||||
|
||||
// Create new high-load wallet; source code included from `highload-wallet-v2-code.fif`
|
||||
"highload-wallet-v2-code.fif" include
|
||||
// code
|
||||
<b subwallet-id 32 i, 0 64 u,
|
||||
file-base +".pk" load-generate-keypair
|
||||
constant wallet_pk
|
||||
B, false 1 i,
|
||||
b> // data
|
||||
null // no libraries
|
||||
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
|
||||
dup ."StateInit: " <s csr. cr
|
||||
dup hashu wc swap 2dup 2constant wallet_addr
|
||||
."new wallet address = " 2dup .addr cr
|
||||
2dup file-base +subwallet +".addr" save-address-verbose
|
||||
."Non-bounceable address (for init): " 2dup 7 .Addr cr
|
||||
."Bounceable address (for later access): " 6 .Addr cr
|
||||
now timeout + 32 << 1- dup =: query_id
|
||||
."Init query_id is " dup . ."(0x" X._ .")" cr
|
||||
<b subwallet-id 32 i, query_id 64 u, false 1 i, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hashu wallet_pk ed25519_sign_uint rot
|
||||
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
|
||||
dup ."External message for initialization is " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
file-base +subwallet +"-query.boc" tuck B>file
|
||||
."(Saved wallet creating query to file " type .")" cr
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue