From c86bfc02c7b7368849907b5cf9399743ee5c69c2 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Fri, 6 Apr 2018 17:00:11 +0300
Subject: [PATCH 01/34] enabling push
---
Adamant.xcodeproj/project.pbxproj | 113 +++---------------------------
Adamant/Adamant.entitlements | 8 +++
2 files changed, 17 insertions(+), 104 deletions(-)
create mode 100644 Adamant/Adamant.entitlements
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index 18cc7a4e8..6e0776bd5 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -180,6 +180,7 @@
E905D39C204C13B900DDB504 /* SecuredStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecuredStore.swift; sourceTree = ""; };
E905D39E204C281400DDB504 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; };
E9061B96207501E40011F104 /* AdamantUserInfoKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantUserInfoKey.swift; sourceTree = ""; };
+ E9061B982077AF8E0011F104 /* Adamant.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Adamant.entitlements; sourceTree = ""; };
E90A4942204C5ED6009F6A65 /* EurekaPassphraseRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EurekaPassphraseRow.swift; sourceTree = ""; };
E90A4944204C5F60009F6A65 /* PassphraseCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PassphraseCell.xib; sourceTree = ""; };
E90A494A204D9EB8009F6A65 /* AdamantAuthentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantAuthentication.swift; sourceTree = ""; };
@@ -409,6 +410,7 @@
E913C8F11FFFA51D001A83F7 /* AppDelegate.swift */,
E9E7CD9020026FA100DFC4DB /* SwinjectDependencies.swift */,
E913C8FD1FFFA51E001A83F7 /* Info.plist */,
+ E9061B982077AF8E0011F104 /* Adamant.entitlements */,
);
path = Adamant;
sourceTree = "";
@@ -793,6 +795,9 @@
com.apple.BackgroundModes = {
enabled = 1;
};
+ com.apple.Push = {
+ enabled = 1;
+ };
};
};
E9EC344320066D4A00C0E546 = {
@@ -1240,6 +1245,7 @@
baseConfigurationReference = 871009461457F6B7B47D99C5 /* Pods-Adamant.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_ENTITLEMENTS = Adamant/Adamant.entitlements;
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = J2L77FMN46;
DISPLAY_NAME = ADM.Dev;
@@ -1248,7 +1254,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger-dev";
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "37a2cc10-f98c-431d-b00f-e929c1e336fc";
+ PROVISIONING_PROFILE = "e4233bbf-3705-44fe-95b0-e77475672c60";
PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Debug Dev";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
@@ -1260,6 +1266,7 @@
baseConfigurationReference = D283570A539D864115735AAA /* Pods-Adamant.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_ENTITLEMENTS = Adamant/Adamant.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
@@ -1270,112 +1277,13 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger";
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "65b9c88e-381e-45f4-83fb-10cb9d8b343d";
+ PROVISIONING_PROFILE = "bedd1b75-2f23-4a85-a0b2-14c424fcff42";
PROVISIONING_PROFILE_SPECIFIER = "ADAMANT Messenger Dev";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
- E9722062201E654D004F2AAD /* Testing */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_COMMA = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INFINITE_RECURSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
- CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
- CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
- CLANG_WARN_STRICT_PROTOTYPES = YES;
- CLANG_WARN_SUSPICIOUS_MOVE = YES;
- CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- CODE_SIGN_IDENTITY = "iPhone Developer";
- COPY_PHASE_STRIP = NO;
- DEBUG_INFORMATION_FORMAT = dwarf;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- ENABLE_TESTABILITY = YES;
- GCC_C_LANGUAGE_STANDARD = gnu11;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_NO_COMMON_BLOCKS = YES;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- );
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 10.0;
- MTL_ENABLE_DEBUG_INFO = YES;
- ONLY_ACTIVE_ARCH = YES;
- PRODUCT_NAME = ADAMANT;
- SDKROOT = iphoneos;
- SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
- SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- };
- name = Testing;
- };
- E9722063201E654D004F2AAD /* Testing */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = 903AC078A8A55175A05E42D4 /* Pods-Adamant.testing.xcconfig */;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CODE_SIGN_STYLE = Manual;
- DEVELOPMENT_TEAM = J2L77FMN46;
- DISPLAY_NAME = Adamant;
- INFOPLIST_FILE = Adamant/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 10.0;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = "im.adamant.adamant-messenger-qa";
- PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "79394ceb-6016-46b9-8b60-1927d7bd227a";
- PROVISIONING_PROFILE_SPECIFIER = "ADAMANT QA Dev";
- SWIFT_VERSION = 4.0;
- TARGETED_DEVICE_FAMILY = 1;
- };
- name = Testing;
- };
- E9722064201E654D004F2AAD /* Testing */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- BUNDLE_LOADER = "$(TEST_HOST)";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CODE_SIGN_STYLE = Automatic;
- DEVELOPMENT_TEAM = J2L77FMN46;
- INFOPLIST_FILE = AdamantTests/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = im.adamant.AdamantTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "";
- SWIFT_VERSION = 4.0;
- TARGETED_DEVICE_FAMILY = "1,2";
- TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Adamant.app/Adamant";
- };
- name = Testing;
- };
E9EC344C20066D4A00C0E546 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1419,7 +1327,6 @@
isa = XCConfigurationList;
buildConfigurations = (
E913C8FE1FFFA51E001A83F7 /* Debug */,
- E9722062201E654D004F2AAD /* Testing */,
E913C8FF1FFFA51E001A83F7 /* Release */,
);
defaultConfigurationIsVisible = 0;
@@ -1429,7 +1336,6 @@
isa = XCConfigurationList;
buildConfigurations = (
E913C9011FFFA51E001A83F7 /* Debug */,
- E9722063201E654D004F2AAD /* Testing */,
E913C9021FFFA51E001A83F7 /* Release */,
);
defaultConfigurationIsVisible = 0;
@@ -1439,7 +1345,6 @@
isa = XCConfigurationList;
buildConfigurations = (
E9EC344C20066D4A00C0E546 /* Debug */,
- E9722064201E654D004F2AAD /* Testing */,
E9EC344D20066D4A00C0E546 /* Release */,
);
defaultConfigurationIsVisible = 0;
diff --git a/Adamant/Adamant.entitlements b/Adamant/Adamant.entitlements
new file mode 100644
index 000000000..903def2af
--- /dev/null
+++ b/Adamant/Adamant.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ aps-environment
+ development
+
+
From 2f6aac3940872c7092661a62374347b22bb27b2f Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Sat, 7 Apr 2018 14:38:55 +0300
Subject: [PATCH 02/34] Working on
---
Adamant/AppDelegate.swift | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift
index 103783614..74c45af8a 100644
--- a/Adamant/AppDelegate.swift
+++ b/Adamant/AppDelegate.swift
@@ -150,6 +150,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}
+// UIApplication.shared.registerForRemoteNotifications()
+// UIApplication.shared.unregisterForRemoteNotifications()
+
return true
}
@@ -182,6 +185,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}
+// MARK: - Remote notifications
+extension AppDelegate {
+ func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
+ print(userInfo)
+ }
+
+ func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
+ let tokenParts = deviceToken.map { data -> String in
+ return String(format: "%02.2hhx", data)
+ }
+
+ let token = tokenParts.joined()
+ print("Device Token: \(token)")
+ }
+
+ func application(_ application: UIApplication,
+ didFailToRegisterForRemoteNotificationsWithError error: Error) {
+ print("Failed to register: \(error)")
+ }
+}
+
// MARK: - BackgroundFetch
extension AppDelegate {
From 737557477fca70af966dea4e5cb700ed5e22d03d Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Sat, 7 Apr 2018 15:49:01 +0300
Subject: [PATCH 03/34] pod updated FTIndicator: using forked repo with new
feature. Original repo not yet merged pull request.
---
Adamant.xcodeproj/project.pbxproj | 16 --------
Adamant/Services/AdamantDialogService.swift | 2 +-
Podfile | 2 +-
Podfile.lock | 45 ++++++++++++++++-----
4 files changed, 36 insertions(+), 29 deletions(-)
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index 18cc7a4e8..1ea16f4dc 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -747,7 +747,6 @@
E913C8EB1FFFA51D001A83F7 /* Frameworks */,
E913C8EC1FFFA51D001A83F7 /* Resources */,
1C93FABD2FEB3E73F76872C8 /* [CP] Embed Pods Frameworks */,
- 9103110972E84A36FC64A689 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -915,21 +914,6 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Adamant/Pods-Adamant-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 9103110972E84A36FC64A689 /* [CP] Copy Pods Resources */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "[CP] Copy Pods Resources";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Adamant/Pods-Adamant-resources.sh\"\n";
- showEnvVarsInLog = 0;
- };
E40A2E5936758D20792675C8 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
diff --git a/Adamant/Services/AdamantDialogService.swift b/Adamant/Services/AdamantDialogService.swift
index 4f7f22ed5..4dbf20e25 100644
--- a/Adamant/Services/AdamantDialogService.swift
+++ b/Adamant/Services/AdamantDialogService.swift
@@ -16,7 +16,7 @@ class AdamantDialogService: DialogService {
// Configure notifications
init() {
FTIndicator.setIndicatorStyle(.extraLight)
- FTNotificationIndicator.setDefaultNotificationDelay(4)
+ FTNotificationIndicator.setDefaultDismissTime(4)
}
}
diff --git a/Podfile b/Podfile
index 3aba6621a..acb3cf817 100644
--- a/Podfile
+++ b/Podfile
@@ -12,7 +12,7 @@ target 'Adamant' do
# UI
pod 'FreakingSimpleRoundImageView' # Round avatars
- pod 'FTIndicator' # Notifications and activity indicator
+ pod 'FTIndicator', :git => 'git@github.com:RealBonus/FTIndicator' # Notifications and activity indicator
pod 'Eureka' # Forms
pod 'MessageKit' # Chat UI
pod 'MyLittlePinpad' # Pinpad
diff --git a/Podfile.lock b/Podfile.lock
index 30afbc936..1580c5cec 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -1,8 +1,8 @@
PODS:
- - Alamofire (4.7.0)
+ - Alamofire (4.7.1)
- EFQRCode (4.2.1)
- Eureka (4.1.1)
- - FreakingSimpleRoundImageView (1.0.2)
+ - FreakingSimpleRoundImageView (1.1)
- FTIndicator (1.2.6):
- FTIndicator/FTNotificationIndicator (= 1.2.6)
- FTIndicator/FTProgressIndicator (= 1.2.6)
@@ -11,19 +11,19 @@ PODS:
- FTIndicator/FTProgressIndicator (1.2.6)
- FTIndicator/FTToastIndicator (1.2.6)
- KeychainAccess (3.1.0)
- - MessageKit (0.13.2)
+ - MessageKit (0.13.3)
- MyLittlePinpad (0.2.3)
- QRCodeReader.swift (8.1.1)
- ReachabilitySwift (4.1.0)
- RNCryptor (5.0.2)
- - Swinject (2.3.0)
+ - Swinject (2.4.0)
DEPENDENCIES:
- Alamofire
- EFQRCode
- Eureka
- FreakingSimpleRoundImageView
- - FTIndicator
+ - "FTIndicator (from `git@github.com:RealBonus/FTIndicator`)"
- KeychainAccess
- MessageKit
- MyLittlePinpad
@@ -32,20 +32,43 @@ DEPENDENCIES:
- RNCryptor
- Swinject
+SPEC REPOS:
+ https://github.com/CocoaPods/Specs.git:
+ - Alamofire
+ - EFQRCode
+ - Eureka
+ - FreakingSimpleRoundImageView
+ - KeychainAccess
+ - MessageKit
+ - MyLittlePinpad
+ - QRCodeReader.swift
+ - ReachabilitySwift
+ - RNCryptor
+ - Swinject
+
+EXTERNAL SOURCES:
+ FTIndicator:
+ :git: "git@github.com:RealBonus/FTIndicator"
+
+CHECKOUT OPTIONS:
+ FTIndicator:
+ :commit: 423510db2d3c8a1f7bfc49fa1c479d1e40bc6f6a
+ :git: "git@github.com:RealBonus/FTIndicator"
+
SPEC CHECKSUMS:
- Alamofire: 907e0a98eb68cdb7f9d1f541a563d6ac5dc77b25
+ Alamofire: 68d7d521118d49c615a8d2214d87cdf525599d30
EFQRCode: f3fd67049faa07adb575495c05d72a34e407a940
Eureka: b88fb930e42c79f8c03c373d0fcdc28c1d6c50ed
- FreakingSimpleRoundImageView: e87b0ef24c9b95b74b1c2cab5b03832dbabd4873
+ FreakingSimpleRoundImageView: 4a3a1cb1347beb247f8c63b5b5cae9d770b431ee
FTIndicator: d2a7c204341b7b0165b6dbeb5bf6979c6d7d4bb7
KeychainAccess: 94c5540b32eabf7bc32bfb976a268e8ea05fd6da
- MessageKit: 274efb4454ddc9b3408b031b3396498dd4ed2462
+ MessageKit: 63c5811bdf1087384c76732fa4b5479b2ed11568
MyLittlePinpad: 4ead0ea2da9957bb37d961b39743c1dfc8431efa
QRCodeReader.swift: b164a681887de276d405ff02bce854d82cd6360b
ReachabilitySwift: 6849231cd4e06559f3b9ef4a97a0a0f96d41e09f
RNCryptor: 59b9f92e9fa8a1af74c32dc6b1af5e30ff5e1b64
- Swinject: 6e29897396bab81fe4aa40c5ca92ba996136334b
+ Swinject: a1364b0f66c2736bb03c1c7cab54809e16df25da
-PODFILE CHECKSUM: d73e1c940db551ee6dfc242c1b5307dc8ed560be
+PODFILE CHECKSUM: ee6b879d8c9a3523dce4dcac9891d665bf9b7f39
-COCOAPODS: 1.4.0
+COCOAPODS: 1.5.0
From ced21b6f224849c06f3419afe1fc456542eb252f Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Sat, 21 Apr 2018 14:47:30 +0300
Subject: [PATCH 04/34] pod updated
---
Podfile | 2 +-
Podfile.lock | 50 +++++++++++++++++++++-----------------------------
2 files changed, 22 insertions(+), 30 deletions(-)
diff --git a/Podfile b/Podfile
index acb3cf817..3aba6621a 100644
--- a/Podfile
+++ b/Podfile
@@ -12,7 +12,7 @@ target 'Adamant' do
# UI
pod 'FreakingSimpleRoundImageView' # Round avatars
- pod 'FTIndicator', :git => 'git@github.com:RealBonus/FTIndicator' # Notifications and activity indicator
+ pod 'FTIndicator' # Notifications and activity indicator
pod 'Eureka' # Forms
pod 'MessageKit' # Chat UI
pod 'MyLittlePinpad' # Pinpad
diff --git a/Podfile.lock b/Podfile.lock
index 1580c5cec..f10cbca89 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -1,21 +1,21 @@
PODS:
- - Alamofire (4.7.1)
+ - Alamofire (4.7.2)
- EFQRCode (4.2.1)
- Eureka (4.1.1)
- FreakingSimpleRoundImageView (1.1)
- - FTIndicator (1.2.6):
- - FTIndicator/FTNotificationIndicator (= 1.2.6)
- - FTIndicator/FTProgressIndicator (= 1.2.6)
- - FTIndicator/FTToastIndicator (= 1.2.6)
- - FTIndicator/FTNotificationIndicator (1.2.6)
- - FTIndicator/FTProgressIndicator (1.2.6)
- - FTIndicator/FTToastIndicator (1.2.6)
- - KeychainAccess (3.1.0)
- - MessageKit (0.13.3)
- - MyLittlePinpad (0.2.3)
+ - FTIndicator (1.2.8):
+ - FTIndicator/FTNotificationIndicator (= 1.2.8)
+ - FTIndicator/FTProgressIndicator (= 1.2.8)
+ - FTIndicator/FTToastIndicator (= 1.2.8)
+ - FTIndicator/FTNotificationIndicator (1.2.8)
+ - FTIndicator/FTProgressIndicator (1.2.8)
+ - FTIndicator/FTToastIndicator (1.2.8)
+ - KeychainAccess (3.1.1)
+ - MessageKit (0.13.4)
+ - MyLittlePinpad (0.2.4)
- QRCodeReader.swift (8.1.1)
- ReachabilitySwift (4.1.0)
- - RNCryptor (5.0.2)
+ - RNCryptor (5.0.3)
- Swinject (2.4.0)
DEPENDENCIES:
@@ -23,7 +23,7 @@ DEPENDENCIES:
- EFQRCode
- Eureka
- FreakingSimpleRoundImageView
- - "FTIndicator (from `git@github.com:RealBonus/FTIndicator`)"
+ - FTIndicator
- KeychainAccess
- MessageKit
- MyLittlePinpad
@@ -38,6 +38,7 @@ SPEC REPOS:
- EFQRCode
- Eureka
- FreakingSimpleRoundImageView
+ - FTIndicator
- KeychainAccess
- MessageKit
- MyLittlePinpad
@@ -46,29 +47,20 @@ SPEC REPOS:
- RNCryptor
- Swinject
-EXTERNAL SOURCES:
- FTIndicator:
- :git: "git@github.com:RealBonus/FTIndicator"
-
-CHECKOUT OPTIONS:
- FTIndicator:
- :commit: 423510db2d3c8a1f7bfc49fa1c479d1e40bc6f6a
- :git: "git@github.com:RealBonus/FTIndicator"
-
SPEC CHECKSUMS:
- Alamofire: 68d7d521118d49c615a8d2214d87cdf525599d30
+ Alamofire: e4fa87002c137ba2d8d634d2c51fabcda0d5c223
EFQRCode: f3fd67049faa07adb575495c05d72a34e407a940
Eureka: b88fb930e42c79f8c03c373d0fcdc28c1d6c50ed
FreakingSimpleRoundImageView: 4a3a1cb1347beb247f8c63b5b5cae9d770b431ee
- FTIndicator: d2a7c204341b7b0165b6dbeb5bf6979c6d7d4bb7
- KeychainAccess: 94c5540b32eabf7bc32bfb976a268e8ea05fd6da
- MessageKit: 63c5811bdf1087384c76732fa4b5479b2ed11568
- MyLittlePinpad: 4ead0ea2da9957bb37d961b39743c1dfc8431efa
+ FTIndicator: 5218a42e6a3bb6623f58e4931fc36bb09b7e1b78
+ KeychainAccess: 7bd430028059754a3debab3cfc0bd1fc7fb85df3
+ MessageKit: b600e3f466632f93c0333c78fd9006c960c8cbe2
+ MyLittlePinpad: dc5f8a7fc13a4ad6fc9dc8d3359d91f1b5b1c7e8
QRCodeReader.swift: b164a681887de276d405ff02bce854d82cd6360b
ReachabilitySwift: 6849231cd4e06559f3b9ef4a97a0a0f96d41e09f
- RNCryptor: 59b9f92e9fa8a1af74c32dc6b1af5e30ff5e1b64
+ RNCryptor: c93d19029dcf7ff160aca0f24d6c9e7b0d82f664
Swinject: a1364b0f66c2736bb03c1c7cab54809e16df25da
-PODFILE CHECKSUM: ee6b879d8c9a3523dce4dcac9891d665bf9b7f39
+PODFILE CHECKSUM: d73e1c940db551ee6dfc242c1b5307dc8ed560be
COCOAPODS: 1.5.0
From c4af4839a59e1412e1464c92e01d123d576baf09 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Sat, 5 May 2018 15:36:02 +0300
Subject: [PATCH 05/34] Localisation update.
---
.../Assets/l18n/en.lproj/Localizable.strings | 6 ++++++
.../Assets/l18n/ru.lproj/Localizable.strings | Bin 37510 -> 38008 bytes
2 files changed, 6 insertions(+)
diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings
index 2e1091a0c..7a4f9d9cf 100644
--- a/Adamant/Assets/l18n/en.lproj/Localizable.strings
+++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings
@@ -209,9 +209,15 @@
/* Notifications: New message notification title */
"NotificationsService.NewMessage.Title" = "New Message";
+/* Notifications: New single message notification body */
+"NotificationsService.NewMessage.BodySingle" = "You have new message";
+
/* Notifications: New transfer transaction title */
"NotificationsService.NewTransfer.Title" = "New Transfer";
+/* Notifications: New single transfer transaction body */
+"NotificationsService.NewTransfer.BodySingle" = "You have new transfer";
+
/* Notifications: User has disabled notifications. Head him into settings */
"NotificationsService.NotificationsDisabled" = "Notifications disabled. You can enable notifications in Settings";
diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings
index 973e0f6b635fc9e185b7e31c6f12231bf3dddf8b..cffc78e45c767fe75b969be56d2ea5bf49f2eaea 100644
GIT binary patch
delta 144
zcmZo$%JgFe(*~m$&SHj4hCGIJhMdV0^MxnJM9Z)zG2}C(FjP)%jP~Ytg0LBaA*v={
zjFz5!#YSNAJ3Anbk(;a)^GMi_A(f$=L4g5o5<}`_{TN*&bF3#Xh!MhKmgwXKL2R2Z
I#cVMJ0HBmCumAu6
delta 18
acmeydf~jpO(*~oM$vUyOHvfyOG6eurt_Z>a
From ccff3433db98efc4f01f5d8be35711f03d3649c4 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Mon, 7 May 2018 21:45:47 +0300
Subject: [PATCH 06/34] pod: Haring
---
Adamant.xcodeproj/project.pbxproj | 2 ++
Podfile | 1 +
Podfile.lock | 6 +++++-
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index b46cf24fc..1974cf0d8 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -891,6 +891,7 @@
"${BUILT_PRODUCTS_DIR}/Eureka/Eureka.framework",
"${BUILT_PRODUCTS_DIR}/FTIndicator/FTIndicator.framework",
"${BUILT_PRODUCTS_DIR}/FreakingSimpleRoundImageView/FreakingSimpleRoundImageView.framework",
+ "${BUILT_PRODUCTS_DIR}/Haring/Haring.framework",
"${BUILT_PRODUCTS_DIR}/KeychainAccess/KeychainAccess.framework",
"${BUILT_PRODUCTS_DIR}/MessageKit/MessageKit.framework",
"${BUILT_PRODUCTS_DIR}/MyLittlePinpad/MyLittlePinpad.framework",
@@ -906,6 +907,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Eureka.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FTIndicator.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FreakingSimpleRoundImageView.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Haring.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KeychainAccess.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessageKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MyLittlePinpad.framework",
diff --git a/Podfile b/Podfile
index 3aba6621a..ed9f1912a 100644
--- a/Podfile
+++ b/Podfile
@@ -9,6 +9,7 @@ target 'Adamant' do
pod 'RNCryptor' # Cryptor
pod 'Swinject' # Dependency Injection
pod 'ReachabilitySwift' # Network status
+ pod 'Haring' # Markdown parser
# UI
pod 'FreakingSimpleRoundImageView' # Round avatars
diff --git a/Podfile.lock b/Podfile.lock
index f10cbca89..6945a7a13 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -10,6 +10,7 @@ PODS:
- FTIndicator/FTNotificationIndicator (1.2.8)
- FTIndicator/FTProgressIndicator (1.2.8)
- FTIndicator/FTToastIndicator (1.2.8)
+ - Haring (2.0.7)
- KeychainAccess (3.1.1)
- MessageKit (0.13.4)
- MyLittlePinpad (0.2.4)
@@ -24,6 +25,7 @@ DEPENDENCIES:
- Eureka
- FreakingSimpleRoundImageView
- FTIndicator
+ - Haring
- KeychainAccess
- MessageKit
- MyLittlePinpad
@@ -39,6 +41,7 @@ SPEC REPOS:
- Eureka
- FreakingSimpleRoundImageView
- FTIndicator
+ - Haring
- KeychainAccess
- MessageKit
- MyLittlePinpad
@@ -53,6 +56,7 @@ SPEC CHECKSUMS:
Eureka: b88fb930e42c79f8c03c373d0fcdc28c1d6c50ed
FreakingSimpleRoundImageView: 4a3a1cb1347beb247f8c63b5b5cae9d770b431ee
FTIndicator: 5218a42e6a3bb6623f58e4931fc36bb09b7e1b78
+ Haring: e9fa27a6ca648045fbacccc59547abed44b26694
KeychainAccess: 7bd430028059754a3debab3cfc0bd1fc7fb85df3
MessageKit: b600e3f466632f93c0333c78fd9006c960c8cbe2
MyLittlePinpad: dc5f8a7fc13a4ad6fc9dc8d3359d91f1b5b1c7e8
@@ -61,6 +65,6 @@ SPEC CHECKSUMS:
RNCryptor: c93d19029dcf7ff160aca0f24d6c9e7b0d82f664
Swinject: a1364b0f66c2736bb03c1c7cab54809e16df25da
-PODFILE CHECKSUM: d73e1c940db551ee6dfc242c1b5307dc8ed560be
+PODFILE CHECKSUM: 2afa7e8b57ec3b78d5cea2c6ce51fd5f71e3c1f6
COCOAPODS: 1.5.0
From 331ffc871b35247dbff303d680dcac854b8a72a8 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Mon, 7 May 2018 21:46:49 +0300
Subject: [PATCH 07/34] NotificationsViewController
---
Adamant.xcodeproj/project.pbxproj | 4 +
.../Assets/l18n/en.lproj/Localizable.strings | 36 +++
.../Assets/l18n/ru.lproj/Localizable.strings | Bin 38008 -> 43642 bytes
.../Stories/Login/LoginViewController.swift | 14 +-
.../NotificationsViewController.swift | 226 ++++++++++++++++++
Adamant/Stories/Settings/SettingsRoutes.swift | 5 +
.../Settings/SettingsViewController.swift | 48 ++--
7 files changed, 312 insertions(+), 21 deletions(-)
create mode 100644 Adamant/Stories/Settings/NotificationsViewController.swift
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index 1974cf0d8..c721325b3 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -42,6 +42,7 @@
E91947B020002393001362F8 /* AdamantApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91947AF20002393001362F8 /* AdamantApiService.swift */; };
E91947B22000246A001362F8 /* AdamantError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91947B12000246A001362F8 /* AdamantError.swift */; };
E91947B420002809001362F8 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91947B320002809001362F8 /* Account.swift */; };
+ E91A063A209F05AA0018A102 /* NotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91A0639209F05AA0018A102 /* NotificationsViewController.swift */; };
E9215973206119FB0000CA5C /* ReachabilityMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9215972206119FB0000CA5C /* ReachabilityMonitor.swift */; };
E921597520611A6A0000CA5C /* AdamantReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921597420611A6A0000CA5C /* AdamantReachability.swift */; };
E921597B206503000000CA5C /* ButtonsStripeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921597A206503000000CA5C /* ButtonsStripeView.swift */; };
@@ -224,6 +225,7 @@
E91947AF20002393001362F8 /* AdamantApiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantApiService.swift; sourceTree = ""; };
E91947B12000246A001362F8 /* AdamantError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantError.swift; sourceTree = ""; };
E91947B320002809001362F8 /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; };
+ E91A0639209F05AA0018A102 /* NotificationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsViewController.swift; sourceTree = ""; };
E9215972206119FB0000CA5C /* ReachabilityMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReachabilityMonitor.swift; sourceTree = ""; };
E921597420611A6A0000CA5C /* AdamantReachability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantReachability.swift; sourceTree = ""; };
E921597A206503000000CA5C /* ButtonsStripeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonsStripeView.swift; sourceTree = ""; };
@@ -626,6 +628,7 @@
E982F69B20235B4D00566AC7 /* SettingsViewController.swift */,
E93D7AC12052EE21005D19DC /* SettingsViewController+StayIn.swift */,
E9942B7F203C058C00C163AF /* QRGeneratorViewController.swift */,
+ E91A0639209F05AA0018A102 /* NotificationsViewController.swift */,
);
path = Settings;
sourceTree = "";
@@ -1001,6 +1004,7 @@
E93EFE13200D1156000BB482 /* ChatViewController.swift in Sources */,
E9B3D39E201F99F40019EB36 /* DataProvider.swift in Sources */,
E9E7CDC02003AF6D00DFC4DB /* AdamantCellFactory.swift in Sources */,
+ E91A063A209F05AA0018A102 /* NotificationsViewController.swift in Sources */,
E93B0D742028B21400126346 /* ChatsProvider.swift in Sources */,
E9150BA12066DA210065A985 /* ChatTransaction+CoreDataClass.swift in Sources */,
E9CAE8D42018AC1800345E76 /* AdamantApi+Keys.swift in Sources */,
diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings
index 7a4f9d9cf..57020292b 100644
--- a/Adamant/Assets/l18n/en.lproj/Localizable.strings
+++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings
@@ -82,6 +82,9 @@
/* Unknown internal error */
"ApiService.InternalError.UnknownError" = "Unknown error. Report a bug";
+/* Eureka forms Cancel button */
+"Cancel" = "Cancel";
+
/* ChatList: outgoing message preview format, like 'You: %@' */
"ChatListPage.SentMessageFormat" = "You: %@";
@@ -206,6 +209,39 @@
/* New chat: scene title */
"NewChatScene.Title" = "New Chat";
+/* Notifications: Modes description. Markdown supported. */
+"Notifications.ModesDescription" = "#### Notification modes\n\n#### Disabled\nNo notifications.\n\n#### Background Fetch\nYour device fetchs for new messages by itself. No external calls. Fetch is initiated by iOS, the actual time determined by the operating system based on many factors like battery charge, cellular network, application usage patterns and cannot be predicted. This can be 20 minutes, or 6 hours, or maybe even a day. You still can open app and check for a new message though.\n\n#### Push\nNotifications sent to your device by ADAMANT Notification Service. You receive notification almost instantly after a message was sent and approved by the Blockchain - a few seconds delay. But this mode requires your device to register it's Device Token in the Service's database. Device tokens are safe and secure, and this option is recommended in most cases.\n\nYou can read more about device registration on ADAMANT's Github page.";
+
+/* Notifications: Notifications update mode */
+"Notifications.Section.Settings" = "Notifications settings";
+
+/* Notifications: Selected notifications types */
+"Notifications.Section.NotificationsType" = "Notifications";
+
+/* Notifications: About ANS */
+"Notifications.Section.AboutANS" = "About ANS";
+
+/* Notifications: Disable notifications */
+"Notifications.Mode.NotificationsDisabled" = "Disabled";
+
+/* Notifications: Use Background fetch notifications */
+"Notifications.Mode.BackgroundFetch" = "Background Fetch";
+
+/* Notifications: Use Apple Push notifications */
+"Notifications.Mode.ApplePush" = "Push";
+
+/* Notifications: Visit Github */
+"Notifications.Row.VisitGithub" = "Visit Github";
+
+/* Notifications: Mode */
+"Notifications.Row.Mode" = "Notifications mode";
+
+/* Notifications: Send new messages notifications */
+"Notifications.Row.Messages" = "Messages";
+
+/* Notifications: Send new transfers notifications */
+"Notifications.Row.Transfers" = "Transfers";
+
/* Notifications: New message notification title */
"NotificationsService.NewMessage.Title" = "New Message";
diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings
index cffc78e45c767fe75b969be56d2ea5bf49f2eaea..aa2d5b53852c9a12de33e7b45ca54185b299a5c9 100644
GIT binary patch
delta 2898
zcmaJ@U2hvj6rEHXTBJm@365*0q?@EsTL{K+Y}c{Zc6ObQA`=Hl1w~Gc(xkW%iBrW6
z5igj$Ac0CHG>8X4NPVk#LgW|pCqzPs2Oe7f0OAcHR5)j5x4UkWDq3fD=6;-e@40vO
z?|aAo`Q_M8Uqr@c#g5n(H^hfxMVR6Pv4yuy(ZsrhorYM&*@hf04$7-rG5Klnglt56
z_J4@nv*pI7DQ{P!^5^J){CEAFd{7&DegOv7pwg6GS0fi=FtY$N+c3X|lU*1zVSGp2
z!XI6=AiE}Fa<4kp8-uK?EoQ#D8)}b0IFDyUjJkiOFpZRvHZq1~lnr1s5_nG<1-$35
z%Hw3pScV%uzSfZ87+FYEjCmzhRx&SPZHl5X4J{k?az-3-Z(xshvTeL(v6wy5QB1k%
zbaf73@^}(>Xue=fxbGgolyZI+ZYh<*|13PwDc{S=pN+Nr&FVQng##ii0LUd}G>Mf1
zyo)%op=jaVl6O<#wq-EkorX)0%BZ}|K`sMJCAf63yDk>Rk{E|Z#+7jtm;`L*RCX=A
zr;%OS&=`EU^vdA4&kCfBTpN%o0ccDrO9PZ$L1s6AZ~<}3?27N1%rJ=|tiPs4UFKLlMsc62T$kT2~uA>xo`cJhGaC~t>LrXe>Ezr?A-
z3(7I9Sv;tA+cnKhahsWBrm|S)5UR(~BwVHtD&Y=6k@=dJu6&SV;
zeoi)~YV-*I+LNxqA|lnkH7PS{j<5upzLSWRNI8MHGR1|bio{*}MTlgPmON^3Q8gdL
z<$rc4!itcBlPUxU!Nd)=>>^q_G{(~e4M4&RGo>m$?8>9hE=j+6D-dJUFhe*yEGHS}q)a`sx|7G6pp2GHtqJO5
zC5`nQB4Xy4Z9XR-p6MU(PUb<57Rb3Hu7e=*Q8gz2sGSa(>e?cwcxjnn7A`B7?2jw^Qlcby?Y`~@*{<6QT!_J^@=L&ehQQK4;*oMD@
zJCUw3i7)4>-wsqdRCA14|$MF#rGn
delta 30
ocmV+(0O9}o)B^aZ0IQG*Ir~m)}
diff --git a/Adamant/Stories/Login/LoginViewController.swift b/Adamant/Stories/Login/LoginViewController.swift
index a4157b3c0..ec56a030b 100644
--- a/Adamant/Stories/Login/LoginViewController.swift
+++ b/Adamant/Stories/Login/LoginViewController.swift
@@ -223,13 +223,13 @@ class LoginViewController: FormViewController {
$0.hidden = Condition.function([], { [weak self] form -> Bool in
return self?.hideNewPassphrase ?? false
})
- }.cellUpdate({ (cell, row) in
- cell.textView.textAlignment = .center
- cell.textView.font = UIFont.adamantPrimary(size: 14)
- cell.textView.textColor = UIColor.adamantPrimary
- cell.textView.isSelectable = false
- cell.textView.isEditable = false
- })
+ }.cellUpdate({ (cell, _) in
+ cell.textView.textAlignment = .center
+ cell.textView.font = UIFont.adamantPrimary(size: 14)
+ cell.textView.textColor = UIColor.adamantPrimary
+ cell.textView.isSelectable = false
+ cell.textView.isEditable = false
+ })
// New genegated passphrase
<<< PassphraseRow() {
diff --git a/Adamant/Stories/Settings/NotificationsViewController.swift b/Adamant/Stories/Settings/NotificationsViewController.swift
new file mode 100644
index 000000000..576358e2d
--- /dev/null
+++ b/Adamant/Stories/Settings/NotificationsViewController.swift
@@ -0,0 +1,226 @@
+//
+// NotificationsViewController.swift
+// Adamant
+//
+// Created by Anokhov Pavel on 06.05.2018.
+// Copyright © 2018 Adamant. All rights reserved.
+//
+
+import UIKit
+import SafariServices
+import Eureka
+import Haring
+
+extension String.adamantLocalized {
+ struct notificationsScene {
+ static let title = NSLocalizedString("Notifications.Title", comment: "Notifications: scene title")
+ static let modesDescription = NSLocalizedString("Notifications.ModesDescription", comment: "Notifications: Modes description. Markdown supported.")
+ }
+}
+
+enum NotificationMode: CustomStringConvertible {
+ case disabled
+ case backgroundFetch
+ case push
+
+ var localized: String {
+ switch self {
+ case .disabled:
+ return NSLocalizedString("Notifications.Mode.NotificationsDisabled", comment: "Notifications: Disable notifications")
+
+ case .backgroundFetch:
+ return NSLocalizedString("Notifications.Mode.BackgroundFetch", comment: "Notifications: Use Background fetch notifications")
+
+ case .push:
+ return NSLocalizedString("Notifications.Mode.ApplePush", comment: "Notifications: Use Apple Push notifications")
+ }
+ }
+
+ var description: String {
+ return localized
+ }
+}
+
+class NotificationsViewController: FormViewController {
+ private static let githubUrl = URL.init(string: "https://github.com/Adamant-im/AdamantNotificationService/blob/master/README.md")
+
+ // MARK: Sections & Rows
+ enum Sections {
+ case settings
+ case types
+ case ans
+
+ var localized: String {
+ switch self {
+ case .settings:
+ return NSLocalizedString("Notifications.Section.Settings", comment: "Notifications: Notifications settings")
+
+ case .types:
+ return NSLocalizedString("Notifications.Section.NotificationsType", comment: "Notifications: Selected notifications types")
+
+ case .ans:
+ return NSLocalizedString("Notifications.Section.AboutANS", comment: "Notifications: About ANS")
+ }
+ }
+
+ var tag: String {
+ switch self {
+ case .settings: return "sttngs"
+ case .types: return "tps"
+ case .ans: return "ans"
+ }
+ }
+ }
+
+ enum Rows {
+ case notificationsMode
+ case messages
+ case transfers
+ case description
+ case github
+
+ var localized: String {
+ switch self {
+ case .notificationsMode:
+ return NSLocalizedString("Notifications.Row.Mode", comment: "Notifications: Mode")
+
+ case .messages:
+ return NSLocalizedString("Notifications.Row.Messages", comment: "Notifications: Send new messages notifications")
+
+ case .transfers:
+ return NSLocalizedString("Notifications.Row.Transfers", comment: "Notifications: Send new transfers notifications")
+
+ case .description:
+ return String.adamantLocalized.notificationsScene.modesDescription
+
+ case .github:
+ return NSLocalizedString("Notifications.Row.VisitGithub", comment: "Notifications: Visit Github")
+ }
+ }
+
+ var tag: String {
+ switch self {
+ case .notificationsMode: return "md"
+ case .messages: return "msgs"
+ case .transfers: return "trsfsrs"
+ case .description: return "dscrptn"
+ case .github: return "gthb"
+ }
+ }
+ }
+
+ // MARK: Properties
+
+ private(set) var notificationMode: NotificationMode = .disabled
+
+ private var notificationTypesHidden: Bool = true
+
+ // MARK: Lifecycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.navigationItem.title = String.adamantLocalized.notificationsScene.title
+ navigationOptions = .Disabled
+
+ // MARK: Modes
+ form +++ Section(Sections.settings.localized) {
+ $0.tag = Sections.settings.tag
+ }
+
+ <<< ActionSheetRow() {
+ $0.tag = Rows.notificationsMode.tag
+ $0.title = Rows.notificationsMode.localized
+ $0.selectorTitle = Rows.notificationsMode.localized
+ $0.options = [.disabled, .backgroundFetch, .push]
+ $0.value = NotificationMode.disabled
+ }.onChange({ [weak self] row in
+ guard let mode = row.value else {
+ return
+ }
+
+ self?.setNotificationMode(mode)
+ }).cellUpdate({ (cell, _) in
+ cell.accessoryType = .disclosureIndicator
+ })
+
+
+ // MARK: Types
+ +++ Section(Sections.types.localized) {
+ $0.tag = Sections.types.tag
+ $0.hidden = Condition.function([Rows.notificationsMode.tag], { [weak self] _ -> Bool in
+ guard let notificationTypesHidden = self?.notificationTypesHidden else {
+ return true
+ }
+ return notificationTypesHidden
+ })
+ }
+
+ <<< SwitchRow() {
+ $0.tag = Rows.messages.tag
+ $0.title = Rows.messages.localized
+ }
+
+ <<< SwitchRow() {
+ $0.tag = Rows.transfers.tag
+ $0.title = Rows.transfers.localized
+ }
+
+
+ // MARK: ANS
+
+ +++ Section(Sections.ans.localized) {
+ $0.tag = Sections.ans.tag
+ }
+
+ <<< TextAreaRow() {
+ $0.textAreaHeight = .dynamic(initialTextViewHeight: 44)
+ $0.tag = Rows.description.tag
+ }.cellUpdate({ (cell, _) in
+ let parser = MarkdownParser(font: UIFont.systemFont(ofSize: UIFont.systemFontSize))
+ cell.textView.attributedText = parser.parse(Rows.description.localized)
+ cell.textView.isSelectable = false
+ cell.textView.isEditable = false
+ })
+
+ <<< LabelRow() {
+ $0.title = Rows.github.localized
+ $0.tag = Rows.github.tag
+ }.cellSetup({ (cell, _) in
+ cell.selectionStyle = .gray
+ }).onCellSelection({ [weak self] (_, row) in
+ guard let url = NotificationsViewController.githubUrl else {
+ return
+ }
+
+ let safari = SFSafariViewController(url: url)
+ safari.preferredControlTintColor = UIColor.adamantPrimary
+ self?.present(safari, animated: true, completion: nil)
+ }).cellUpdate({ (cell, _) in
+ cell.accessoryType = .disclosureIndicator
+ })
+ }
+
+ // MARK: Logic
+
+ func setNotificationMode(_ mode: NotificationMode) {
+ guard mode != notificationMode else {
+ return
+ }
+
+ print(mode.localized)
+
+ notificationMode = mode;
+ notificationTypesHidden = mode == .disabled
+
+// switch mode {
+// case .disabled:
+// return
+//
+// case .backgroundFetch:
+// return
+//
+// case .push:
+// return
+// }
+ }
+}
diff --git a/Adamant/Stories/Settings/SettingsRoutes.swift b/Adamant/Stories/Settings/SettingsRoutes.swift
index 7cc5772b4..b15f9fc6c 100644
--- a/Adamant/Stories/Settings/SettingsRoutes.swift
+++ b/Adamant/Stories/Settings/SettingsRoutes.swift
@@ -26,6 +26,11 @@ extension AdamantScene {
return c
})
+ static let notifications = AdamantScene(identifier: "NotificationsViewController") { r -> UIViewController in
+ let c = NotificationsViewController()
+ return c
+ }
+
private init() {}
}
}
diff --git a/Adamant/Stories/Settings/SettingsViewController.swift b/Adamant/Stories/Settings/SettingsViewController.swift
index 845324fc1..8bb09a03f 100644
--- a/Adamant/Stories/Settings/SettingsViewController.swift
+++ b/Adamant/Stories/Settings/SettingsViewController.swift
@@ -17,6 +17,8 @@ extension String.adamantLocalized {
static let stayInTurnOff = NSLocalizedString("SettingsPage.DoNotStayLoggedIn", comment: "Config: turn off 'Stay Logged In' confirmation")
static let biometryOnReason = NSLocalizedString("SettingsPage.UseBiometry", comment: "Config: Authorization reason for turning biometry on")
static let biometryOffReason = NSLocalizedString("SettingsPage.DoNotUseBiometry", comment: "Config: Authorization reason for turning biometry off")
+
+ private init() {}
}
}
@@ -145,26 +147,44 @@ class SettingsViewController: FormViewController {
})
// Notifications
- <<< SwitchRow() {
- $0.tag = Rows.notifications.tag
+// <<< SwitchRow() {
+// $0.tag = Rows.notifications.tag
+// $0.title = Rows.notifications.localized
+// $0.value = notificationsService.notificationsEnabled
+//
+// $0.hidden = Condition.function([Rows.stayLoggedIn.tag], { form -> Bool in
+// guard let row: SwitchRow = form.rowBy(tag: Rows.stayLoggedIn.tag), let value = row.value else {
+// return true
+// }
+//
+// return !value
+// })
+// }.onChange({ [weak self] row in
+// guard let enabled = row.value else { return }
+// self?.setNotifications(enabled: enabled)
+// }).cellUpdate({ (cell, _) in
+// if let label = cell.textLabel {
+// label.font = UIFont.adamantPrimary(size: 17)
+// label.textColor = UIColor.adamantPrimary
+// }
+// })
+ <<< LabelRow() {
$0.title = Rows.notifications.localized
- $0.value = notificationsService.notificationsEnabled
-
- $0.hidden = Condition.function([Rows.stayLoggedIn.tag], { form -> Bool in
- guard let row: SwitchRow = form.rowBy(tag: Rows.stayLoggedIn.tag), let value = row.value else {
- return true
- }
-
- return !value
- })
- }.onChange({ [weak self] row in
- guard let enabled = row.value else { return }
- self?.setNotifications(enabled: enabled)
+ $0.tag = Rows.notifications.tag
+ }.cellSetup({ (cell, _) in
+ cell.selectionStyle = .gray
+ }).onCellSelection({ [weak self] (cell, _) in
+ guard let nav = self?.navigationController, let vc = self?.router.get(scene: AdamantScene.Settings.notifications) else {
+ return
+ }
+ nav.pushViewController(vc, animated: true)
}).cellUpdate({ (cell, _) in
if let label = cell.textLabel {
label.font = UIFont.adamantPrimary(size: 17)
label.textColor = UIColor.adamantPrimary
}
+
+ cell.accessoryType = .disclosureIndicator
})
// MARK: Utilities
From e20e10da32fa2cf58b24f8fde8c69f74e371d852 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Tue, 8 May 2018 17:14:47 +0300
Subject: [PATCH 08/34] AdamantNotificationService refactored: modes.
AnsApiService draft
---
Adamant.xcodeproj/project.pbxproj | 4 +
Adamant/AppDelegate.swift | 70 ++++----
Adamant/Info.plist | 1 +
.../NotificationsService.swift | 16 +-
.../Services/AdamantNotificationService.swift | 156 ++++++++++--------
.../Services/ApiService/AnsApiService.swift | 27 +++
.../NotificationsViewController.swift | 110 ++++++------
Adamant/Stories/Settings/SettingsRoutes.swift | 2 +-
.../Settings/SettingsViewController.swift | 79 ---------
Adamant/SwinjectDependencies.swift | 9 -
10 files changed, 232 insertions(+), 242 deletions(-)
create mode 100644 Adamant/Services/ApiService/AnsApiService.swift
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index c721325b3..de217ca00 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -99,6 +99,7 @@
E95F85C4200A540B0070534A /* Chat.json in Resources */ = {isa = PBXBuildFile; fileRef = E95F85C3200A540B0070534A /* Chat.json */; };
E95F85C7200A9B070070534A /* ChatTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85C5200A9B070070534A /* ChatTableViewCell.swift */; };
E95F85C8200A9B070070534A /* ChatTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E95F85C6200A9B070070534A /* ChatTableViewCell.xib */; };
+ E961E94D20A1E203005C8872 /* AnsApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E961E94C20A1E203005C8872 /* AnsApiService.swift */; };
E9722066201F42BB004F2AAD /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9722065201F42BB004F2AAD /* CoreDataStack.swift */; };
E9722068201F42CC004F2AAD /* InMemoryCoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9722067201F42CC004F2AAD /* InMemoryCoreDataStack.swift */; };
E972206B201F44CA004F2AAD /* TransfersProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E972206A201F44CA004F2AAD /* TransfersProvider.swift */; };
@@ -285,6 +286,7 @@
E95F85C3200A540B0070534A /* Chat.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Chat.json; sourceTree = ""; };
E95F85C5200A9B070070534A /* ChatTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTableViewCell.swift; sourceTree = ""; };
E95F85C6200A9B070070534A /* ChatTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ChatTableViewCell.xib; sourceTree = ""; };
+ E961E94C20A1E203005C8872 /* AnsApiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnsApiService.swift; sourceTree = ""; };
E9722065201F42BB004F2AAD /* CoreDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = ""; };
E9722067201F42CC004F2AAD /* InMemoryCoreDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InMemoryCoreDataStack.swift; sourceTree = ""; };
E972206A201F44CA004F2AAD /* TransfersProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransfersProvider.swift; sourceTree = ""; };
@@ -667,6 +669,7 @@
E9CAE8D32018AC1800345E76 /* AdamantApi+Keys.swift */,
E9CAE8D52018AC5300345E76 /* AdamantApi+Transactions.swift */,
E9CAE8D72018ACA700345E76 /* AdamantApi+Transfers.swift */,
+ E961E94C20A1E203005C8872 /* AnsApiService.swift */,
);
path = ApiService;
sourceTree = "";
@@ -1003,6 +1006,7 @@
E9942B87203D9E5100C163AF /* EurekaQRRow.swift in Sources */,
E93EFE13200D1156000BB482 /* ChatViewController.swift in Sources */,
E9B3D39E201F99F40019EB36 /* DataProvider.swift in Sources */,
+ E961E94D20A1E203005C8872 /* AnsApiService.swift in Sources */,
E9E7CDC02003AF6D00DFC4DB /* AdamantCellFactory.swift in Sources */,
E91A063A209F05AA0018A102 /* NotificationsViewController.swift in Sources */,
E93B0D742028B21400126346 /* ChatsProvider.swift in Sources */,
diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift
index 74c45af8a..0be06a269 100644
--- a/Adamant/AppDelegate.swift
+++ b/Adamant/AppDelegate.swift
@@ -17,14 +17,29 @@ extension String.adamantLocalized {
}
}
+// MARK: Resources
+struct AdamantResources {
+ // Storyboard
+ static let jsCore = Bundle.main.url(forResource: "adamant-core", withExtension: "js")!
+ static let api = URL(string: "https://endless.adamant.im")!
+ static let ans = URL(string: "")!
+ static let coreDataModel = Bundle.main.url(forResource: "ChatModels", withExtension: "momd")!
+
+ private init() {}
+}
+
+
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var repeater: RepeaterService!
var container: Container!
+ // MARK: Dependencies
+ weak var ansApiService: AnsApiService?
weak var accountService: AccountService?
weak var notificationService: NotificationsService?
+ weak var dialogService: DialogService?
// MARK: - Lifecycle
@@ -120,25 +135,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
- // MARK: 6. Notifications
- if let service = container.resolve(NotificationsService.self) {
- if service.notificationsEnabled {
- UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
- } else {
- UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalNever)
- }
-
- NotificationCenter.default.addObserver(forName: Notification.Name.AdamantNotificationService.showNotificationsChanged, object: service, queue: OperationQueue.main) { _ in
- if service.notificationsEnabled {
- UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
- } else {
- UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalNever)
- }
- }
- }
-
-
- // MARK: 7. Logout reset
+ // MARK: 6. Logout reset
NotificationCenter.default.addObserver(forName: Notification.Name.AdamantAccountService.userLoggedOut, object: nil, queue: OperationQueue.main) { [weak self] _ in
// On logout, pop all navigators to root.
guard let tbc = self?.window?.rootViewController as? UITabBarController, let vcs = tbc.viewControllers else {
@@ -150,9 +147,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}
-// UIApplication.shared.registerForRemoteNotifications()
-// UIApplication.shared.unregisterForRemoteNotifications()
-
return true
}
@@ -166,6 +160,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
repeater.pauseAll()
}
+ // MARK: Notifications
+
func applicationDidBecomeActive(_ application: UIApplication) {
if accountService?.account != nil {
notificationService?.removeAllDeliveredNotifications()
@@ -187,27 +183,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: - Remote notifications
extension AppDelegate {
- func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
- print(userInfo)
- }
+// func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
+// print(userInfo)
+// }
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
- let tokenParts = deviceToken.map { data -> String in
- return String(format: "%02.2hhx", data)
- }
+ let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
- let token = tokenParts.joined()
- print("Device Token: \(token)")
+ ansApiService?.register(token: token) { [weak self] result in
+ switch result {
+ case .success:
+ return
+
+ case .failure(let error):
+ self?.notificationService?.setNotificationsMode(.disabled, completion: nil)
+ self?.dialogService?.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription))
+ }
+ }
}
- func application(_ application: UIApplication,
- didFailToRegisterForRemoteNotificationsWithError error: Error) {
- print("Failed to register: \(error)")
+ func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
+ notificationService?.setNotificationsMode(.disabled, completion: nil)
+ dialogService?.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription))
}
}
-// MARK: - BackgroundFetch
+// MARK: - Background Fetch
extension AppDelegate {
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
let container = Container()
diff --git a/Adamant/Info.plist b/Adamant/Info.plist
index 93677bded..cc3c97d3a 100644
--- a/Adamant/Info.plist
+++ b/Adamant/Info.plist
@@ -47,6 +47,7 @@
UIBackgroundModes
fetch
+ remote-notification
UILaunchStoryboardName
LaunchScreen
diff --git a/Adamant/ServiceProtocols/NotificationsService.swift b/Adamant/ServiceProtocols/NotificationsService.swift
index af88356a0..70cb8feb4 100644
--- a/Adamant/ServiceProtocols/NotificationsService.swift
+++ b/Adamant/ServiceProtocols/NotificationsService.swift
@@ -18,19 +18,27 @@ extension String.adamantLocalized {
static let newTransferTitle = NSLocalizedString("NotificationsService.NewTransfer.Title", comment: "Notifications: New transfer transaction title")
static let newTransferBody = NSLocalizedString("NotificationsService.NewTransfer.BodyFormat", comment: "Notifications: New transfer notification body. Using %d for amount")
+ static let registerRemotesError = NSLocalizedString("NotificationsService.Error.RegistrationRemotesFormat", comment: "Notifications: Something went wrong with registration remote notifications. %@ for description")
+
private init() {}
}
}
extension StoreKey {
struct notificationsService {
- static let notificationsEnabled = "notifications.show"
+ static let notificationsMode = "notifications.mode"
static let customBadgeNumber = "notifications.number"
private init() {}
}
}
+enum NotificationsMode: Int {
+ case disabled
+ case backgroundFetch
+ case push
+}
+
/// Supported notification types
///
/// - message: text message
@@ -72,7 +80,7 @@ enum AdamantNotificationType {
extension Notification.Name {
struct AdamantNotificationService {
/// Raised when user has logged out.
- static let showNotificationsChanged = Notification.Name("adamant.notificationService.showNotifications")
+ static let notificationsModeChanged = Notification.Name("adamant.notificationService.notificationsMode")
private init() {}
}
@@ -86,9 +94,9 @@ enum NotificationsServiceResult {
}
protocol NotificationsService: class {
- var notificationsEnabled: Bool { get }
+ var notificationsMode: NotificationsMode { get }
- func setNotificationsEnabled(_ enabled: Bool, completion: @escaping (NotificationsServiceResult) -> Void)
+ func setNotificationsMode(_ mode: NotificationsMode, completion: ((NotificationsServiceResult) -> Void)?)
func showNotification(title: String, body: String, type: AdamantNotificationType)
diff --git a/Adamant/Services/AdamantNotificationService.swift b/Adamant/Services/AdamantNotificationService.swift
index bf05b0161..0833ed866 100644
--- a/Adamant/Services/AdamantNotificationService.swift
+++ b/Adamant/Services/AdamantNotificationService.swift
@@ -10,36 +10,27 @@ import Foundation
import UIKit
import UserNotifications
-class AdamantNotificationsService: NotificationsService {
- // MARK: Dependencies
- var securedStore: SecuredStore! {
- didSet {
- if let raw = securedStore.get(StoreKey.notificationsService.notificationsEnabled), let show = Bool(raw) {
- if show {
- UNUserNotificationCenter.current().getNotificationSettings { [weak self] settings in
- switch settings.authorizationStatus {
- case .authorized:
- self?.notificationsEnabled = true
-
- case .denied:
- self?.notificationsEnabled = false
-
- case .notDetermined:
- self?.notificationsEnabled = false
- }
- }
- } else {
- notificationsEnabled = false
- }
- } else {
- notificationsEnabled = false
- }
+extension NotificationsMode {
+ func toRaw() -> String {
+ return String(self.rawValue)
+ }
+
+ init?(string: String) {
+ guard let int = Int(string: string), let mode = NotificationsMode(rawValue: int) else {
+ return nil
}
+
+ self = mode
}
+}
+
+class AdamantNotificationsService: NotificationsService {
+ // MARK: Dependencies
+ var securedStore: SecuredStore!
// MARK: Properties
- private(set) var notificationsEnabled = false
+ private(set) var notificationsMode: NotificationsMode = .disabled
private(set) var customBadgeNumber = 0
private var isBackgroundSession = false
@@ -47,62 +38,94 @@ class AdamantNotificationsService: NotificationsService {
// MARK: Lifecycle
init() {
- NotificationCenter.default.addObserver(forName: Notification.Name.AdamantAccountService.userLoggedIn, object: nil, queue: OperationQueue.main) { _ in
+ NotificationCenter.default.addObserver(forName: Notification.Name.AdamantAccountService.userLoggedIn, object: nil, queue: OperationQueue.main) { [weak self] _ in
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
UIApplication.shared.applicationIconBadgeNumber = 0
+
+ if let securedStore = self?.securedStore, let raw = securedStore.get(StoreKey.notificationsService.notificationsMode), let mode = NotificationsMode(string: raw) {
+ self?.setNotificationsMode(mode, completion: nil)
+ } else {
+ self?.setNotificationsMode(.disabled, completion: nil)
+ }
}
NotificationCenter.default.addObserver(forName: Notification.Name.AdamantAccountService.userLoggedOut, object: nil, queue: nil) { [weak self] _ in
- self?.notificationsEnabled = false
- self?.securedStore.remove(StoreKey.notificationsService.notificationsEnabled)
- NotificationCenter.default.post(name: Notification.Name.AdamantNotificationService.showNotificationsChanged, object: self)
+ self?.setNotificationsMode(.disabled, completion: nil)
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
-
-
- // MARK: Notifications authorization
- func setNotificationsEnabled(_ enabled: Bool, completion: @escaping (NotificationsServiceResult) -> Void) {
- guard notificationsEnabled != enabled else {
+}
+
+
+// MARK: - Notifications mode {
+extension AdamantNotificationsService {
+ func setNotificationsMode(_ mode: NotificationsMode, completion: ((NotificationsServiceResult) -> Void)?) {
+ switch mode {
+ case .disabled:
+ UIApplication.shared.unregisterForRemoteNotifications()
+ UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalNever)
+ securedStore.remove(StoreKey.notificationsService.notificationsMode)
+ notificationsMode = mode
+ NotificationCenter.default.post(name: Notification.Name.AdamantNotificationService.notificationsModeChanged, object: self)
+ completion?(.success)
return
- }
-
- if enabled { // MARK: Turn on
- UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { [weak self] settings in
- switch settings.authorizationStatus {
- case .authorized:
- self?.notificationsEnabled = true
- self?.securedStore.set(String(true), for: StoreKey.notificationsService.notificationsEnabled)
- NotificationCenter.default.post(name: Notification.Name.AdamantNotificationService.showNotificationsChanged, object: self)
- completion(.success)
-
- case .denied:
- self?.notificationsEnabled = false
- completion(.denied(error: nil))
-
- case .notDetermined:
- UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { (granted, error) in
- self?.notificationsEnabled = false
- if granted {
- completion(.success)
- } else {
- completion(.denied(error: error))
- }
- })
+
+ case .backgroundFetch:
+ authorizeNotifications { [weak self] (success, error) in
+ guard success else {
+ completion?(.denied(error: error))
+ return
}
- })
- } else { // MARK: Turn off
- notificationsEnabled = false
- securedStore.remove(StoreKey.notificationsService.notificationsEnabled)
- NotificationCenter.default.post(name: Notification.Name.AdamantNotificationService.showNotificationsChanged, object: self)
+
+ UIApplication.shared.unregisterForRemoteNotifications()
+ UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
+ self?.securedStore.set(mode.toRaw(), for: StoreKey.notificationsService.notificationsMode)
+ self?.notificationsMode = mode
+ NotificationCenter.default.post(name: Notification.Name.AdamantNotificationService.notificationsModeChanged, object: self)
+ completion?(.success)
+ }
+
+ case .push:
+ authorizeNotifications { [weak self] (success, error) in
+ guard success else {
+ completion?(.denied(error: error))
+ return
+ }
+
+ UIApplication.shared.registerForRemoteNotifications()
+ UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalNever)
+ self?.securedStore.set(mode.toRaw(), for: StoreKey.notificationsService.notificationsMode)
+ self?.notificationsMode = mode
+ NotificationCenter.default.post(name: Notification.Name.AdamantNotificationService.notificationsModeChanged, object: self)
+ completion?(.success)
+ }
}
}
-
- // MARK: Posting & removing Notifications
+ private func authorizeNotifications(completion: @escaping (Bool, Error?) -> Void) {
+ UNUserNotificationCenter.current().getNotificationSettings { settings in
+ switch settings.authorizationStatus {
+ case .authorized:
+ completion(true, nil)
+
+ case .denied:
+ completion(false, nil)
+
+ case .notDetermined:
+ UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { (granted, error) in
+ completion(granted, error)
+ })
+ }
+ }
+ }
+}
+
+
+// MARK: - Posting & removing Notifications
+extension AdamantNotificationsService {
func showNotification(title: String, body: String, type: AdamantNotificationType) {
let content = UNMutableNotificationContent()
content.title = title
@@ -156,7 +179,8 @@ class AdamantNotificationsService: NotificationsService {
}
}
-// MARK: Background batch notifications
+
+// MARK: - Background batch notifications
extension AdamantNotificationsService {
func startBackgroundBatchNotifications() {
isBackgroundSession = true
diff --git a/Adamant/Services/ApiService/AnsApiService.swift b/Adamant/Services/ApiService/AnsApiService.swift
new file mode 100644
index 000000000..4e3b6903b
--- /dev/null
+++ b/Adamant/Services/ApiService/AnsApiService.swift
@@ -0,0 +1,27 @@
+//
+// AnsApiService.swift
+// Adamant
+//
+// Created by Anokhov Pavel on 08.05.2018.
+// Copyright © 2018 Adamant. All rights reserved.
+//
+
+import Foundation
+import Alamofire
+
+enum AnsApiServiceResult {
+ case success
+ case failure(ApiServiceError)
+}
+
+class AnsApiService {
+ struct ApiCommants {
+ static let register = "/api/reg"
+
+ private init() {}
+ }
+
+ func register(token: String, completion: @escaping (AnsApiServiceResult) -> Void) {
+
+ }
+}
diff --git a/Adamant/Stories/Settings/NotificationsViewController.swift b/Adamant/Stories/Settings/NotificationsViewController.swift
index 576358e2d..ed007e9cf 100644
--- a/Adamant/Stories/Settings/NotificationsViewController.swift
+++ b/Adamant/Stories/Settings/NotificationsViewController.swift
@@ -18,11 +18,7 @@ extension String.adamantLocalized {
}
}
-enum NotificationMode: CustomStringConvertible {
- case disabled
- case backgroundFetch
- case push
-
+extension NotificationsMode: CustomStringConvertible {
var localized: String {
switch self {
case .disabled:
@@ -42,8 +38,6 @@ enum NotificationMode: CustomStringConvertible {
}
class NotificationsViewController: FormViewController {
- private static let githubUrl = URL.init(string: "https://github.com/Adamant-im/AdamantNotificationService/blob/master/README.md")
-
// MARK: Sections & Rows
enum Sections {
case settings
@@ -109,12 +103,17 @@ class NotificationsViewController: FormViewController {
}
}
- // MARK: Properties
+ // MARK: Dependencies
+
+ var notificationsService: NotificationsService!
- private(set) var notificationMode: NotificationMode = .disabled
+ // MARK: Properties
+
+ private static let githubUrl = URL.init(string: "https://github.com/Adamant-im/AdamantNotificationService/blob/master/README.md")
private var notificationTypesHidden: Bool = true
+
// MARK: Lifecycle
override func viewDidLoad() {
@@ -127,12 +126,12 @@ class NotificationsViewController: FormViewController {
$0.tag = Sections.settings.tag
}
- <<< ActionSheetRow() {
+ <<< ActionSheetRow() {
$0.tag = Rows.notificationsMode.tag
$0.title = Rows.notificationsMode.localized
$0.selectorTitle = Rows.notificationsMode.localized
$0.options = [.disabled, .backgroundFetch, .push]
- $0.value = NotificationMode.disabled
+ $0.value = notificationsService.notificationsMode
}.onChange({ [weak self] row in
guard let mode = row.value else {
return
@@ -144,30 +143,7 @@ class NotificationsViewController: FormViewController {
})
- // MARK: Types
- +++ Section(Sections.types.localized) {
- $0.tag = Sections.types.tag
- $0.hidden = Condition.function([Rows.notificationsMode.tag], { [weak self] _ -> Bool in
- guard let notificationTypesHidden = self?.notificationTypesHidden else {
- return true
- }
- return notificationTypesHidden
- })
- }
-
- <<< SwitchRow() {
- $0.tag = Rows.messages.tag
- $0.title = Rows.messages.localized
- }
-
- <<< SwitchRow() {
- $0.tag = Rows.transfers.tag
- $0.title = Rows.transfers.localized
- }
-
-
// MARK: ANS
-
+++ Section(Sections.ans.localized) {
$0.tag = Sections.ans.tag
}
@@ -202,25 +178,61 @@ class NotificationsViewController: FormViewController {
// MARK: Logic
- func setNotificationMode(_ mode: NotificationMode) {
- guard mode != notificationMode else {
+ func setNotificationMode(_ mode: NotificationsMode) {
+ guard mode != notificationsService.notificationsMode else {
return
}
- print(mode.localized)
+ switch mode {
+ case .backgroundFetch:
+ notificationTypesHidden = false
+
+ if let msgs: SwitchRow = form.rowBy(tag: Rows.messages.tag),
+ let tgs: SwitchRow = form.rowBy(tag: Rows.transfers.tag) {
+ msgs.value = true
+ tgs.value = true
+ }
+
+ default: notificationTypesHidden = true
+ }
+
+ notificationsService.setNotificationsMode(mode) { [weak self] result in
+ switch result {
+ case .success:
+ return
+
+ case .denied(error: _):
+ if let row: SwitchRow = self?.form.rowBy(tag: Rows.notificationsMode.tag) {
+ row.value = false
+ row.updateCell()
+ }
+
+ self?.notificationTypesHidden = true
+
+ if let section = self?.form.sectionBy(tag: Sections.types.tag) {
+ section.evaluateHidden()
+ }
+
+ DispatchQueue.main.async {
+ self?.presentNotificationsDeniedError()
+ }
+ }
+ }
+ }
+
+ private func presentNotificationsDeniedError() {
+ let alert = UIAlertController(title: nil, message: String.adamantLocalized.notifications.notificationsDisabled, preferredStyle: .alert)
+
+ alert.addAction(UIAlertAction(title: String.adamantLocalized.alert.settings, style: .default) { _ in
+ DispatchQueue.main.async {
+ if let settingsURL = URL(string: UIApplicationOpenSettingsURLString) {
+ UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil)
+ }
+ }
+ })
- notificationMode = mode;
- notificationTypesHidden = mode == .disabled
+ alert.addAction(UIAlertAction(title: String.adamantLocalized.alert.cancel, style: .cancel, handler: nil))
-// switch mode {
-// case .disabled:
-// return
-//
-// case .backgroundFetch:
-// return
-//
-// case .push:
-// return
-// }
+ present(alert, animated: true, completion: nil)
}
}
diff --git a/Adamant/Stories/Settings/SettingsRoutes.swift b/Adamant/Stories/Settings/SettingsRoutes.swift
index b15f9fc6c..c093d9b78 100644
--- a/Adamant/Stories/Settings/SettingsRoutes.swift
+++ b/Adamant/Stories/Settings/SettingsRoutes.swift
@@ -15,7 +15,6 @@ extension AdamantScene {
c.dialogService = r.resolve(DialogService.self)
c.accountService = r.resolve(AccountService.self)
c.localAuth = r.resolve(LocalAuthentication.self)
- c.notificationsService = r.resolve(NotificationsService.self)
c.router = r.resolve(Router.self)
return c
})
@@ -28,6 +27,7 @@ extension AdamantScene {
static let notifications = AdamantScene(identifier: "NotificationsViewController") { r -> UIViewController in
let c = NotificationsViewController()
+ c.notificationsService = r.resolve(NotificationsService.self)
return c
}
diff --git a/Adamant/Stories/Settings/SettingsViewController.swift b/Adamant/Stories/Settings/SettingsViewController.swift
index 8bb09a03f..f1b90458e 100644
--- a/Adamant/Stories/Settings/SettingsViewController.swift
+++ b/Adamant/Stories/Settings/SettingsViewController.swift
@@ -85,7 +85,6 @@ class SettingsViewController: FormViewController {
var accountService: AccountService!
var dialogService: DialogService!
var localAuth: LocalAuthentication!
- var notificationsService: NotificationsService!
var router: Router!
@@ -147,27 +146,6 @@ class SettingsViewController: FormViewController {
})
// Notifications
-// <<< SwitchRow() {
-// $0.tag = Rows.notifications.tag
-// $0.title = Rows.notifications.localized
-// $0.value = notificationsService.notificationsEnabled
-//
-// $0.hidden = Condition.function([Rows.stayLoggedIn.tag], { form -> Bool in
-// guard let row: SwitchRow = form.rowBy(tag: Rows.stayLoggedIn.tag), let value = row.value else {
-// return true
-// }
-//
-// return !value
-// })
-// }.onChange({ [weak self] row in
-// guard let enabled = row.value else { return }
-// self?.setNotifications(enabled: enabled)
-// }).cellUpdate({ (cell, _) in
-// if let label = cell.textLabel {
-// label.font = UIFont.adamantPrimary(size: 17)
-// label.textColor = UIColor.adamantPrimary
-// }
-// })
<<< LabelRow() {
$0.title = Rows.notifications.localized
$0.tag = Rows.notifications.tag
@@ -258,16 +236,6 @@ class SettingsViewController: FormViewController {
row.evaluateHidden()
}
}
-
- // MARK: Notifications
- NotificationCenter.default.addObserver(forName: Notification.Name.AdamantNotificationService.showNotificationsChanged, object: nil, queue: OperationQueue.main) { [weak self] _ in
- guard let row: SwitchRow = self?.form.rowBy(tag: Rows.notifications.tag), let value = self?.notificationsService.notificationsEnabled else {
- return
- }
-
- row.value = value
- row.updateCell()
- }
}
override func viewWillAppear(_ animated: Bool) {
@@ -281,50 +249,3 @@ class SettingsViewController: FormViewController {
NotificationCenter.default.removeObserver(self)
}
}
-
-
-// MARK: - Properties
-extension SettingsViewController {
- func setNotifications(enabled: Bool) {
- guard enabled != notificationsService.notificationsEnabled else {
- return
- }
-
- notificationsService.setNotificationsEnabled(enabled) { [weak self] result in
- switch result {
- case .success:
- break
-
- case .denied(error: _):
- DispatchQueue.main.async {
- let alert = UIAlertController(title: nil, message: String.adamantLocalized.notifications.notificationsDisabled, preferredStyle: .alert)
-
- alert.addAction(UIAlertAction(title: String.adamantLocalized.alert.settings, style: .default) { _ in
- DispatchQueue.main.async {
- if let row: SwitchRow = self?.form.rowBy(tag: Rows.notifications.tag) {
- row.value = false
- row.updateCell()
- }
-
- if let settingsURL = URL(string: UIApplicationOpenSettingsURLString) {
- UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil)
- }
- }
- })
-
- alert.addAction(UIAlertAction(title: String.adamantLocalized.alert.cancel, style: .cancel, handler: { _ in
- if let row: SwitchRow = self?.form.rowBy(tag: Rows.notifications.tag) {
- row.value = false
- row.updateCell()
- }
- }))
-
- self?.present(alert, animated: true, completion: nil)
- }
- }
- }
- }
-}
-
-
-
diff --git a/Adamant/SwinjectDependencies.swift b/Adamant/SwinjectDependencies.swift
index 3ce08909b..ba69519b4 100644
--- a/Adamant/SwinjectDependencies.swift
+++ b/Adamant/SwinjectDependencies.swift
@@ -8,15 +8,6 @@
import Swinject
-// MARK: - Resources
-private struct AdamantResources {
- // Storyboard
- static let jsCore = Bundle.main.url(forResource: "adamant-core", withExtension: "js")!
- static let api = URL(string: "https://endless.adamant.im")!
- static let coreDataModel = Bundle.main.url(forResource: "ChatModels", withExtension: "momd")!
-
- private init() {}
-}
// MARK: - Services
extension Container {
From 390a489c8fa3647a03b9ea1a7af2de5faff84fb2 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Wed, 9 May 2018 21:03:09 +0300
Subject: [PATCH 09/34] Fixes.
---
Adamant/AppDelegate.swift | 31 +++++++++-----
.../Assets/l18n/en.lproj/Localizable.strings | 3 ++
.../Assets/l18n/ru.lproj/Localizable.strings | Bin 43642 -> 44106 bytes
.../BackgroundFetchService.swift | 2 +-
.../NotificationsService.swift | 2 +-
.../Services/AdamantNotificationService.swift | 35 +++++++++++++---
.../Services/ApiService/AnsApiService.swift | 39 ++++++++++++++++--
...AdamantChatsProvider+backgroundFetch.swift | 4 +-
...antTransfersProvider+backgroundFetch.swift | 4 +-
9 files changed, 95 insertions(+), 25 deletions(-)
diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift
index 0be06a269..6c5190ad2 100644
--- a/Adamant/AppDelegate.swift
+++ b/Adamant/AppDelegate.swift
@@ -36,10 +36,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var container: Container!
// MARK: Dependencies
- weak var ansApiService: AnsApiService?
- weak var accountService: AccountService?
- weak var notificationService: NotificationsService?
- weak var dialogService: DialogService?
+ var ansApiService: AnsApiService!
+ var accountService: AccountService!
+ var notificationService: NotificationsService!
// MARK: - Lifecycle
@@ -50,6 +49,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
accountService = container.resolve(AccountService.self)
notificationService = container.resolve(NotificationsService.self)
+ ansApiService = AnsApiService(ansApi: AdamantResources.ans)
// MARK: 2. Init UI
window = UIWindow(frame: UIScreen.main.bounds)
@@ -163,8 +163,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: Notifications
func applicationDidBecomeActive(_ application: UIApplication) {
- if accountService?.account != nil {
- notificationService?.removeAllDeliveredNotifications()
+ if accountService.account != nil {
+ notificationService.removeAllDeliveredNotifications()
}
if let connection = container.resolve(ReachabilityMonitor.self)?.connection {
@@ -188,23 +188,34 @@ extension AppDelegate {
// }
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
+ guard let address = accountService.account?.address else {
+ print("Trying to register with no user logged")
+ UIApplication.shared.unregisterForRemoteNotifications()
+ return
+ }
+
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
- ansApiService?.register(token: token) { [weak self] result in
+ ansApiService?.register(token: token, address: address) { [weak self] result in
switch result {
case .success:
return
case .failure(let error):
self?.notificationService?.setNotificationsMode(.disabled, completion: nil)
- self?.dialogService?.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription))
+ if let service = self?.container.resolve(DialogService.self) {
+ service.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription))
+ }
}
}
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
notificationService?.setNotificationsMode(.disabled, completion: nil)
- dialogService?.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription))
+
+ if let service = container.resolve(DialogService.self) {
+ service.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription))
+ }
}
}
@@ -234,7 +245,7 @@ extension AppDelegate {
for service in services {
group.enter()
- service.fetchBackgroundData(notificationService: notificationsService) { result in
+ service.fetchBackgroundData(notificationsService: notificationsService) { result in
defer {
group.leave()
}
diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings
index 57020292b..40b09acb0 100644
--- a/Adamant/Assets/l18n/en.lproj/Localizable.strings
+++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings
@@ -242,6 +242,9 @@
/* Notifications: Send new transfers notifications */
"Notifications.Row.Transfers" = "Transfers";
+/* Notifications: Something went wrong while registering remote notifications. %@ for description */
+"NotificationsService.Error.RegistrationRemotesFormat" = "Registration in ANS failed. Please, try again later. Reason %@";
+
/* Notifications: New message notification title */
"NotificationsService.NewMessage.Title" = "New Message";
diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings
index aa2d5b53852c9a12de33e7b45ca54185b299a5c9..0e3f9da069be95ce9b714a3dc724069dd6de9fde 100644
GIT binary patch
delta 314
zcmex$h3V7{rVanH!-E;}8FCp?8A=#37&0017}6OO7|MbCJfH}ORRolWiGoz*0Ob^b
zqNzZYnGD51RjEK8OeaV@7pS9TvS5zv$}>Y6vy}
diff --git a/Adamant/ServiceProtocols/BackgroundFetchService.swift b/Adamant/ServiceProtocols/BackgroundFetchService.swift
index 9c82c048d..002d10a66 100644
--- a/Adamant/ServiceProtocols/BackgroundFetchService.swift
+++ b/Adamant/ServiceProtocols/BackgroundFetchService.swift
@@ -15,6 +15,6 @@ enum FetchResult {
}
protocol BackgroundFetchService {
- func fetchBackgroundData(notificationService: NotificationsService, completion: @escaping (FetchResult) -> Void)
+ func fetchBackgroundData(notificationsService: NotificationsService, completion: @escaping (FetchResult) -> Void)
func dropStateData()
}
diff --git a/Adamant/ServiceProtocols/NotificationsService.swift b/Adamant/ServiceProtocols/NotificationsService.swift
index 70cb8feb4..5f4812511 100644
--- a/Adamant/ServiceProtocols/NotificationsService.swift
+++ b/Adamant/ServiceProtocols/NotificationsService.swift
@@ -18,7 +18,7 @@ extension String.adamantLocalized {
static let newTransferTitle = NSLocalizedString("NotificationsService.NewTransfer.Title", comment: "Notifications: New transfer transaction title")
static let newTransferBody = NSLocalizedString("NotificationsService.NewTransfer.BodyFormat", comment: "Notifications: New transfer notification body. Using %d for amount")
- static let registerRemotesError = NSLocalizedString("NotificationsService.Error.RegistrationRemotesFormat", comment: "Notifications: Something went wrong with registration remote notifications. %@ for description")
+ static let registerRemotesError = NSLocalizedString("NotificationsService.Error.RegistrationRemotesFormat", comment: "Notifications: while registering remote notifications. %@ for description")
private init() {}
}
diff --git a/Adamant/Services/AdamantNotificationService.swift b/Adamant/Services/AdamantNotificationService.swift
index 0833ed866..234a0b747 100644
--- a/Adamant/Services/AdamantNotificationService.swift
+++ b/Adamant/Services/AdamantNotificationService.swift
@@ -65,8 +65,7 @@ extension AdamantNotificationsService {
func setNotificationsMode(_ mode: NotificationsMode, completion: ((NotificationsServiceResult) -> Void)?) {
switch mode {
case .disabled:
- UIApplication.shared.unregisterForRemoteNotifications()
- UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalNever)
+ AdamantNotificationsService.configureUIApplicationFor(mode: mode)
securedStore.remove(StoreKey.notificationsService.notificationsMode)
notificationsMode = mode
NotificationCenter.default.post(name: Notification.Name.AdamantNotificationService.notificationsModeChanged, object: self)
@@ -80,8 +79,7 @@ extension AdamantNotificationsService {
return
}
- UIApplication.shared.unregisterForRemoteNotifications()
- UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
+ AdamantNotificationsService.configureUIApplicationFor(mode: mode)
self?.securedStore.set(mode.toRaw(), for: StoreKey.notificationsService.notificationsMode)
self?.notificationsMode = mode
NotificationCenter.default.post(name: Notification.Name.AdamantNotificationService.notificationsModeChanged, object: self)
@@ -95,8 +93,7 @@ extension AdamantNotificationsService {
return
}
- UIApplication.shared.registerForRemoteNotifications()
- UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalNever)
+ AdamantNotificationsService.configureUIApplicationFor(mode: mode)
self?.securedStore.set(mode.toRaw(), for: StoreKey.notificationsService.notificationsMode)
self?.notificationsMode = mode
NotificationCenter.default.post(name: Notification.Name.AdamantNotificationService.notificationsModeChanged, object: self)
@@ -121,6 +118,32 @@ extension AdamantNotificationsService {
}
}
}
+
+ private static func configureUIApplicationFor(mode: NotificationsMode) {
+ let callback = {
+ switch mode {
+ case .disabled:
+ UIApplication.shared.unregisterForRemoteNotifications()
+ UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalNever)
+
+ case .backgroundFetch:
+ UIApplication.shared.unregisterForRemoteNotifications()
+ UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
+
+ case .push:
+ UIApplication.shared.registerForRemoteNotifications()
+ UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalNever)
+ }
+ }
+
+ if Thread.isMainThread {
+ callback()
+ } else {
+ DispatchQueue.main.sync {
+ callback()
+ }
+ }
+ }
}
diff --git a/Adamant/Services/ApiService/AnsApiService.swift b/Adamant/Services/ApiService/AnsApiService.swift
index 4e3b6903b..5b717bd32 100644
--- a/Adamant/Services/ApiService/AnsApiService.swift
+++ b/Adamant/Services/ApiService/AnsApiService.swift
@@ -15,13 +15,46 @@ enum AnsApiServiceResult {
}
class AnsApiService {
- struct ApiCommants {
- static let register = "/api/reg"
+ struct ApiCommands {
+ static let register = "/api/devices/register"
private init() {}
}
- func register(token: String, completion: @escaping (AnsApiServiceResult) -> Void) {
+ // MARK: Properties
+
+ let ansApi: URL
+ var defaultResponseDispatchQueue = DispatchQueue(label: "com.adamant.ans-queue", qos: .utility, attributes: [.concurrent])
+
+ init(ansApi: URL) {
+ self.ansApi = ansApi
+ }
+
+ func register(token: String, address: String, completion: @escaping (AnsApiServiceResult) -> Void) {
+ let parameters: [String: Any] = [
+ "token": token,
+ "address": address
+ ]
+
+ let headers = [
+ "Content-Type": "application/json"
+ ]
+
+ guard var components = URLComponents(url: ansApi, resolvingAgainstBaseURL: false) else {
+ fatalError("Parsing API URL failed: \(ansApi)")
+ }
+
+ components.path = ApiCommands.register
+ let url = try! components.asURL()
+ Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).validate().responseData(queue: defaultResponseDispatchQueue) { response in
+ switch response.result {
+ case .success:
+ completion(.success)
+
+ case .failure(let error):
+ completion(.failure(.networkError(error: error)))
+ }
+ }
}
}
diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider+backgroundFetch.swift b/Adamant/Services/DataProviders/AdamantChatsProvider+backgroundFetch.swift
index 5ce97ac27..8a3a6bf3a 100644
--- a/Adamant/Services/DataProviders/AdamantChatsProvider+backgroundFetch.swift
+++ b/Adamant/Services/DataProviders/AdamantChatsProvider+backgroundFetch.swift
@@ -9,7 +9,7 @@
import Foundation
extension AdamantChatsProvider: BackgroundFetchService {
- func fetchBackgroundData(notificationService: NotificationsService, completion: @escaping (FetchResult) -> Void) {
+ func fetchBackgroundData(notificationsService: NotificationsService, completion: @escaping (FetchResult) -> Void) {
guard let securedStore = self.securedStore,
let address = securedStore.get(StoreKey.chatProvider.address) else {
completion(.failed)
@@ -45,7 +45,7 @@ extension AdamantChatsProvider: BackgroundFetchService {
securedStore.set(String(newLastHeight), for: StoreKey.chatProvider.notifiedLastHeight)
}
- notificationService.showNotification(title: String.adamantLocalized.notifications.newMessageTitle, body: String.localizedStringWithFormat(String.adamantLocalized.notifications.newMessageBody, total + notifiedCount), type: .newMessages(count: total))
+ notificationsService.showNotification(title: String.adamantLocalized.notifications.newMessageTitle, body: String.localizedStringWithFormat(String.adamantLocalized.notifications.newMessageBody, total + notifiedCount), type: .newMessages(count: total))
completion(.newData)
} else {
diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider+backgroundFetch.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider+backgroundFetch.swift
index a6742ff28..0c303d42f 100644
--- a/Adamant/Services/DataProviders/AdamantTransfersProvider+backgroundFetch.swift
+++ b/Adamant/Services/DataProviders/AdamantTransfersProvider+backgroundFetch.swift
@@ -9,7 +9,7 @@
import Foundation
extension AdamantTransfersProvider: BackgroundFetchService {
- func fetchBackgroundData(notificationService: NotificationsService, completion: @escaping (FetchResult) -> Void) {
+ func fetchBackgroundData(notificationsService: NotificationsService, completion: @escaping (FetchResult) -> Void) {
guard let securedStore = self.securedStore,
let address = securedStore.get(StoreKey.transfersProvider.address) else {
completion(.failed)
@@ -47,7 +47,7 @@ extension AdamantTransfersProvider: BackgroundFetchService {
securedStore.set(String(newLastHeight), for: StoreKey.transfersProvider.notifiedLastHeight)
}
- notificationService.showNotification(title: String.adamantLocalized.notifications.newTransferTitle, body: String.localizedStringWithFormat(String.adamantLocalized.notifications.newTransferBody, total + notifiedCount), type: .newTransactions(count: total))
+ notificationsService.showNotification(title: String.adamantLocalized.notifications.newTransferTitle, body: String.localizedStringWithFormat(String.adamantLocalized.notifications.newTransferBody, total + notifiedCount), type: .newTransactions(count: total))
completion(.newData)
} else {
From 3940f2cc38bc99f03247773dafcf80fa833371e9 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Thu, 17 May 2018 17:25:09 +0300
Subject: [PATCH 10/34] Some updates.
---
Adamant.xcodeproj/project.pbxproj | 10 +++++++++-
.../AnsApiService.swift | 7 +++++++
Adamant/Stories/Chats/ChatTableViewCell.xib | 4 ++--
3 files changed, 18 insertions(+), 3 deletions(-)
rename Adamant/Services/{ApiService => AdamantServices}/AnsApiService.swift (87%)
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index de217ca00..7f50eb5d2 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -441,6 +441,7 @@
E913C9061FFFA92E001A83F7 /* Services */ = {
isa = PBXGroup;
children = (
+ E965A52E20ADC6080041A3EA /* AdamantServices */,
E9CAE8D02018AA5000345E76 /* ApiService */,
E9B3D39F201FA2090019EB36 /* DataProviders */,
E9E7CD922002740500DFC4DB /* AdamantAccountService.swift */,
@@ -623,6 +624,14 @@
path = Parsing;
sourceTree = "";
};
+ E965A52E20ADC6080041A3EA /* AdamantServices */ = {
+ isa = PBXGroup;
+ children = (
+ E961E94C20A1E203005C8872 /* AnsApiService.swift */,
+ );
+ path = AdamantServices;
+ sourceTree = "";
+ };
E982F69820235AF000566AC7 /* Settings */ = {
isa = PBXGroup;
children = (
@@ -669,7 +678,6 @@
E9CAE8D32018AC1800345E76 /* AdamantApi+Keys.swift */,
E9CAE8D52018AC5300345E76 /* AdamantApi+Transactions.swift */,
E9CAE8D72018ACA700345E76 /* AdamantApi+Transfers.swift */,
- E961E94C20A1E203005C8872 /* AnsApiService.swift */,
);
path = ApiService;
sourceTree = "";
diff --git a/Adamant/Services/ApiService/AnsApiService.swift b/Adamant/Services/AdamantServices/AnsApiService.swift
similarity index 87%
rename from Adamant/Services/ApiService/AnsApiService.swift
rename to Adamant/Services/AdamantServices/AnsApiService.swift
index 5b717bd32..1998cee95 100644
--- a/Adamant/Services/ApiService/AnsApiService.swift
+++ b/Adamant/Services/AdamantServices/AnsApiService.swift
@@ -15,6 +15,13 @@ enum AnsApiServiceResult {
}
class AnsApiService {
+
+ /// Fallback registration ANS address
+ static private let ansAccount = (
+ address: "U10629337621822775991",
+ publicKey: "188b24bd116a556ac8ba905bbbdaa16e237dfb14269f5a4f9a26be77537d977c"
+ )
+
struct ApiCommands {
static let register = "/api/devices/register"
diff --git a/Adamant/Stories/Chats/ChatTableViewCell.xib b/Adamant/Stories/Chats/ChatTableViewCell.xib
index 13a453117..7f0c5f443 100644
--- a/Adamant/Stories/Chats/ChatTableViewCell.xib
+++ b/Adamant/Stories/Chats/ChatTableViewCell.xib
@@ -1,11 +1,11 @@
-
+
-
+
From a7bc310500b0e387a9567ce14fc749cad280c340 Mon Sep 17 00:00:00 2001
From: Anton B
Date: Thu, 24 May 2018 12:59:10 +0300
Subject: [PATCH 11/34] Servers list
Add servers list
Add random server selecting
Add server status check
---
.../ApiService/AdamantApiService.swift | 46 ++++++++++++++++++-
Adamant/SwinjectDependencies.swift | 19 ++++++--
2 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/Adamant/Services/ApiService/AdamantApiService.swift b/Adamant/Services/ApiService/AdamantApiService.swift
index f1d30783f..3f889070c 100644
--- a/Adamant/Services/ApiService/AdamantApiService.swift
+++ b/Adamant/Services/ApiService/AdamantApiService.swift
@@ -54,7 +54,8 @@ class AdamantApiService: ApiService {
// MARK: - Properties
- let apiUrl: URL
+ var apiUrl: URL
+ let apiUrls: [String]
var defaultResponseDispatchQueue = DispatchQueue(label: "com.adamant.response-queue", qos: .utility, attributes: [.concurrent])
@@ -62,8 +63,25 @@ class AdamantApiService: ApiService {
init(apiUrl: URL) {
self.apiUrl = apiUrl
+ self.apiUrls = [apiUrl.absoluteString] // Temp
}
-
+
+ init(apiUrls: [String]) {
+ self.apiUrls = apiUrls
+ self.apiUrl = URL(string: apiUrls[0])! // Temp
+
+ self.newServerAddress()
+ }
+
+ func newServerAddress() {
+ let randomIndex = Int(arc4random_uniform(UInt32(self.apiUrls.count)))
+ let url = self.apiUrls[randomIndex]
+ self.apiUrl = URL(string: url)!
+
+ self.testServer { (isAlive) in
+ if isAlive == false { self.newServerAddress() }
+ }
+ }
// MARK: - Tools
@@ -122,4 +140,28 @@ class AdamantApiService: ApiService {
return .serverError(error: error)
}
}
+
+ // Test current server is it alive or not
+ func testServer(completion: @escaping (Bool) -> Void) {
+ // MARK: 1. Build endpoint
+ let endpoint: URL
+ do {
+ endpoint = try buildUrl(path: ApiCommands.Accounts.newAccount)
+ } catch {
+ completion(false)
+ return
+ }
+
+ let headers = [
+ "Content-Type": "application/json"
+ ]
+
+ // MARK: 2. Send
+ sendRequest(url: endpoint, method: .post, encoding: .json, headers: headers) { (serverResponse: ApiServiceResult>) in
+ switch serverResponse {
+ case .success: completion(true)
+ case .failure: completion(false)
+ }
+ }
+ }
}
diff --git a/Adamant/SwinjectDependencies.swift b/Adamant/SwinjectDependencies.swift
index 3ce08909b..8df772678 100644
--- a/Adamant/SwinjectDependencies.swift
+++ b/Adamant/SwinjectDependencies.swift
@@ -14,12 +14,21 @@ private struct AdamantResources {
static let jsCore = Bundle.main.url(forResource: "adamant-core", withExtension: "js")!
static let api = URL(string: "https://endless.adamant.im")!
static let coreDataModel = Bundle.main.url(forResource: "ChatModels", withExtension: "momd")!
-
+
+
+ static let servers = [
+ "https://endless.adamant.im",
+// "https://clown.adamant.im",
+// "https://lake.adamant.im",
+ "https://fake.adamant.im"
+ ]
+
private init() {}
}
// MARK: - Services
extension Container {
+
func registerAdamantServices() {
// MARK: - Standalone services
// MARK: AdamantCore
@@ -68,9 +77,9 @@ extension Container {
// MARK: ApiService
self.register(ApiService.self) { r in
- let service = AdamantApiService(apiUrl: AdamantResources.api)
- service.adamantCore = r.resolve(AdamantCore.self)!
- return service
+ let service = AdamantApiService(apiUrls: AdamantResources.servers)
+ service.adamantCore = r.resolve(AdamantCore.self)!
+ return service
}.inObjectScope(.container)
// MARK: AccountService
@@ -127,7 +136,7 @@ extension Container {
// MARK: ApiService
// No need to init AdamantCore
- self.register(ApiService.self) { r in AdamantApiService(apiUrl: AdamantResources.api)}.inObjectScope(.container)
+ self.register(ApiService.self) { r in AdamantApiService(apiUrls: AdamantResources.servers)}.inObjectScope(.container)
// MARK: Notifications
self.register(NotificationsService.self) { r in
From d8144e7f26ce01f14ee7b119f44b4f1b8f8e9371 Mon Sep 17 00:00:00 2001
From: RealBonus
Date: Thu, 24 May 2018 19:15:51 +0300
Subject: [PATCH 12/34] Update README.md
Changes from pull request #6 (https://github.com/Adamant-im/Adamant-iOS/pull/6), but merged into develop branch, and fixed markup.
---
README.md | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 751490da6..0e76f5dd7 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,18 @@
-# Adamant-iOS
-Adamant Messenger - iOS native client
+# iOS version of ADAMANT Messenger
-https://adamant.im
+It's a native iOS version of ADAMANT Messenger. Available at App Store *(now it's in TestFlight)*. You can use this repository to build your own ADAMANT iOS application.
+
+ADAMANT is the most secure and anonymous messenger, encrypted with Blockchain.
+
+Highlights:
+
+- The most secure and anonymous messenger (see comparison table)
+- PWA version is also available https://msg.adamant.im/
+- Trusted. Open-source project.
+- The only one which is Blockchain-powered
+- Integrated token transfers
+
+Read more about ADAMANT at https://adamant.im
## Build Setup
From ebe93db5aba00475a27c26f478ecb46c51ff6076 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Fri, 25 May 2018 17:33:33 +0300
Subject: [PATCH 13/34] StateAsset JSCore updated
---
Adamant.xcodeproj/project.pbxproj | 24 +++++++++++------
Adamant/Helpers/JSModels.swift | 27 ++++++++++++++++---
.../Models/{Chat.swift => ChatAsset.swift} | 4 +--
Adamant/Models/NormalizedTransaction.swift | 19 ++++++++++---
Adamant/Models/StateAsset.swift | 23 ++++++++++++++++
Adamant/Models/StateType.swift | 13 +++++++++
Adamant/Models/TransactionAsset.swift | 8 +++---
Adamant/Models/TransactionType.swift | 1 +
Adamant/ServiceProtocols/AdamantCore.swift | 13 ++++++++-
Adamant/Services/JSAdamantCore.swift | 11 ++++----
AdamantTests/Parsing/ParsingModelsTests.swift | 2 +-
11 files changed, 116 insertions(+), 29 deletions(-)
rename Adamant/Models/{Chat.swift => ChatAsset.swift} (91%)
create mode 100644 Adamant/Models/StateAsset.swift
create mode 100644 Adamant/Models/StateType.swift
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index 7f50eb5d2..4f082758b 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -86,9 +86,8 @@
E95F857A2007F0260070534A /* ServerResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85792007F0260070534A /* ServerResponse.swift */; };
E95F85802008C8D70070534A /* ChatsRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F857E2008C8D60070534A /* ChatsRoutes.swift */; };
E95F85852008CB3A0070534A /* ChatListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85842008CB3A0070534A /* ChatListViewController.swift */; };
- E95F85872008FDBF0070534A /* Chat.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85862008FDBF0070534A /* Chat.swift */; };
+ E95F85872008FDBF0070534A /* ChatAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85862008FDBF0070534A /* ChatAsset.swift */; };
E95F8589200900B10070534A /* ChatType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F8588200900B10070534A /* ChatType.swift */; };
- E95F858B200931410070534A /* TransactionAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F858A200931410070534A /* TransactionAsset.swift */; };
E95F85B3200954D00070534A /* ChatModels.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = E95F85B1200954D00070534A /* ChatModels.xcdatamodeld */; };
E95F85B7200A4D8F0070534A /* TestTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85B6200A4D8F0070534A /* TestTools.swift */; };
E95F85BA200A4DC90070534A /* TransactionSend.json in Resources */ = {isa = PBXBuildFile; fileRef = E95F85B9200A4DC90070534A /* TransactionSend.json */; };
@@ -100,6 +99,9 @@
E95F85C7200A9B070070534A /* ChatTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85C5200A9B070070534A /* ChatTableViewCell.swift */; };
E95F85C8200A9B070070534A /* ChatTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E95F85C6200A9B070070534A /* ChatTableViewCell.xib */; };
E961E94D20A1E203005C8872 /* AnsApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E961E94C20A1E203005C8872 /* AnsApiService.swift */; };
+ E965A53220B82C850041A3EA /* StateType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A53120B82C850041A3EA /* StateType.swift */; };
+ E965A53420B833A00041A3EA /* StateAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A53320B833A00041A3EA /* StateAsset.swift */; };
+ E965A53620B8370C0041A3EA /* TransactionAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A53520B8370C0041A3EA /* TransactionAsset.swift */; };
E9722066201F42BB004F2AAD /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9722065201F42BB004F2AAD /* CoreDataStack.swift */; };
E9722068201F42CC004F2AAD /* InMemoryCoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9722067201F42CC004F2AAD /* InMemoryCoreDataStack.swift */; };
E972206B201F44CA004F2AAD /* TransfersProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E972206A201F44CA004F2AAD /* TransfersProvider.swift */; };
@@ -273,9 +275,8 @@
E95F85792007F0260070534A /* ServerResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerResponse.swift; sourceTree = ""; };
E95F857E2008C8D60070534A /* ChatsRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsRoutes.swift; sourceTree = ""; };
E95F85842008CB3A0070534A /* ChatListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewController.swift; sourceTree = ""; };
- E95F85862008FDBF0070534A /* Chat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Chat.swift; sourceTree = ""; };
+ E95F85862008FDBF0070534A /* ChatAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatAsset.swift; sourceTree = ""; };
E95F8588200900B10070534A /* ChatType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatType.swift; sourceTree = ""; };
- E95F858A200931410070534A /* TransactionAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionAsset.swift; sourceTree = ""; };
E95F85B2200954D00070534A /* ChatModels.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = ChatModels.xcdatamodel; sourceTree = ""; };
E95F85B6200A4D8F0070534A /* TestTools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestTools.swift; sourceTree = ""; };
E95F85B9200A4DC90070534A /* TransactionSend.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = TransactionSend.json; sourceTree = ""; };
@@ -287,6 +288,9 @@
E95F85C5200A9B070070534A /* ChatTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTableViewCell.swift; sourceTree = ""; };
E95F85C6200A9B070070534A /* ChatTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ChatTableViewCell.xib; sourceTree = ""; };
E961E94C20A1E203005C8872 /* AnsApiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnsApiService.swift; sourceTree = ""; };
+ E965A53120B82C850041A3EA /* StateType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateType.swift; sourceTree = ""; };
+ E965A53320B833A00041A3EA /* StateAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateAsset.swift; sourceTree = ""; };
+ E965A53520B8370C0041A3EA /* TransactionAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionAsset.swift; sourceTree = ""; };
E9722065201F42BB004F2AAD /* CoreDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = ""; };
E9722067201F42CC004F2AAD /* InMemoryCoreDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InMemoryCoreDataStack.swift; sourceTree = ""; };
E972206A201F44CA004F2AAD /* TransfersProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransfersProvider.swift; sourceTree = ""; };
@@ -462,13 +466,15 @@
isa = PBXGroup;
children = (
E91947B320002809001362F8 /* Account.swift */,
- E95F85862008FDBF0070534A /* Chat.swift */,
+ E95F85862008FDBF0070534A /* ChatAsset.swift */,
E95F8588200900B10070534A /* ChatType.swift */,
+ E965A53320B833A00041A3EA /* StateAsset.swift */,
+ E965A53120B82C850041A3EA /* StateType.swift */,
E95F85682006AB9D0070534A /* NormalizedTransaction.swift */,
E9E7CDC920040CC200DFC4DB /* Transaction.swift */,
- E95F858A200931410070534A /* TransactionAsset.swift */,
E9E7CDCB20040FDC00DFC4DB /* TransactionType.swift */,
E9393FA92055D03300EE6F30 /* AdamantMessage.swift */,
+ E965A53520B8370C0041A3EA /* TransactionAsset.swift */,
);
path = Models;
sourceTree = "";
@@ -990,7 +996,6 @@
E9FAE5E2203ED1AE008D3A6B /* ShareQrViewController.swift in Sources */,
E94E7B06205D48B20042B639 /* TransactionsRoutes.swift in Sources */,
E95F85B3200954D00070534A /* ChatModels.xcdatamodeld in Sources */,
- E95F858B200931410070534A /* TransactionAsset.swift in Sources */,
E90A494B204D9EB8009F6A65 /* AdamantAuthentication.swift in Sources */,
E9215973206119FB0000CA5C /* ReachabilityMonitor.swift in Sources */,
E9E7CDAC2002AFA500DFC4DB /* AccountViewController.swift in Sources */,
@@ -999,7 +1004,7 @@
E905D39F204C281400DDB504 /* LoginViewController.swift in Sources */,
E9150B9C2066DA210065A985 /* BaseTransaction+CoreDataProperties.swift in Sources */,
E9E7CDB52002BA6900DFC4DB /* SwinjectedRouter.swift in Sources */,
- E95F85872008FDBF0070534A /* Chat.swift in Sources */,
+ E95F85872008FDBF0070534A /* ChatAsset.swift in Sources */,
E9393FAA2055D03300EE6F30 /* AdamantMessage.swift in Sources */,
E90A494D204DA932009F6A65 /* LocalAuthentication.swift in Sources */,
E95F856F2007B61D0070534A /* GetPublicKeyResponse.swift in Sources */,
@@ -1044,6 +1049,8 @@
E95F857A2007F0260070534A /* ServerResponse.swift in Sources */,
E9A174B32057EC47003667CD /* BackgroundFetchService.swift in Sources */,
E9E7CDBE2003AEFB00DFC4DB /* CellFactory.swift in Sources */,
+ E965A53220B82C850041A3EA /* StateType.swift in Sources */,
+ E965A53620B8370C0041A3EA /* TransactionAsset.swift in Sources */,
E9061B97207501E40011F104 /* AdamantUserInfoKey.swift in Sources */,
E9CAE8D62018AC5300345E76 /* AdamantApi+Transactions.swift in Sources */,
E93D7ABE2052CEE1005D19DC /* NotificationsService.swift in Sources */,
@@ -1056,6 +1063,7 @@
E93D7AC02052CF63005D19DC /* AdamantNotificationService.swift in Sources */,
E93B0D762028B28E00126346 /* AdamantChatsProvider.swift in Sources */,
E90A4943204C5ED6009F6A65 /* EurekaPassphraseRow.swift in Sources */,
+ E965A53420B833A00041A3EA /* StateAsset.swift in Sources */,
E9393FA82055C92700EE6F30 /* Decimal+adamant.swift in Sources */,
E95F8589200900B10070534A /* ChatType.swift in Sources */,
E913C9081FFFA943001A83F7 /* AdamantCore.swift in Sources */,
diff --git a/Adamant/Helpers/JSModels.swift b/Adamant/Helpers/JSModels.swift
index 02a1dc302..034dee6e4 100644
--- a/Adamant/Helpers/JSModels.swift
+++ b/Adamant/Helpers/JSModels.swift
@@ -37,14 +37,12 @@ import JavaScriptCore
@objc protocol JSAssetProtocol: JSExport {
var chat: JSChat? { get set }
+ var store: JSStore? { get set }
}
@objc class JSAsset: NSObject, JSAssetProtocol {
dynamic var chat: JSChat?
-
- init(chat: JSChat?) {
- self.chat = chat
- }
+ dynamic var store: JSStore?
}
@@ -69,6 +67,27 @@ import JavaScriptCore
}
+// MARK: - Store
+
+@objc protocol JSStoreProtocol: JSExport {
+ var key: String { get set }
+ var value: String { get set }
+ var type: Int { get set }
+}
+
+@objc class JSStore: NSObject, JSStoreProtocol {
+ dynamic var key: String
+ dynamic var value: String
+ dynamic var type: Int
+
+ init(key: String, value: String, type: Int) {
+ self.key = key
+ self.value = value
+ self.type = type
+ }
+}
+
+
// MARK: - Transaction
@objc protocol JSTransactionProtocol: JSExport {
diff --git a/Adamant/Models/Chat.swift b/Adamant/Models/ChatAsset.swift
similarity index 91%
rename from Adamant/Models/Chat.swift
rename to Adamant/Models/ChatAsset.swift
index 42681c26a..260d8f3af 100644
--- a/Adamant/Models/Chat.swift
+++ b/Adamant/Models/ChatAsset.swift
@@ -1,5 +1,5 @@
//
-// Chat.swift
+// ChatAsset.swift
// Adamant
//
// Created by Anokhov Pavel on 12.01.2018.
@@ -8,7 +8,7 @@
import Foundation
-struct Chat: Codable {
+struct ChatAsset: Codable {
enum CodingKeys: String, CodingKey {
case message, ownMessage = "own_message", type
}
diff --git a/Adamant/Models/NormalizedTransaction.swift b/Adamant/Models/NormalizedTransaction.swift
index 777c86465..388a5856c 100644
--- a/Adamant/Models/NormalizedTransaction.swift
+++ b/Adamant/Models/NormalizedTransaction.swift
@@ -8,17 +8,30 @@
import Foundation
-struct NormalizedTransaction {
+struct NormalizedTransaction: SignableTransaction {
let type: TransactionType
let amount: Decimal
let senderPublicKey: String
let requesterPublicKey: String?
let timestamp: UInt64
- let recipientId: String
+ let recipientId: String?
let asset: TransactionAsset
var date: Date {
- return AdamantUtilities.decodeAdamantDate(timestamp: TimeInterval(timestamp))
+ return AdamantUtilities.decodeAdamant(timestamp: TimeInterval(timestamp))
+ }
+}
+
+// Convinient init
+extension NormalizedTransaction {
+ init(type: TransactionType, amount: Decimal, senderPublicKey: String, requesterPublicKey: String?, date: Date, recipientId: String?, asset: TransactionAsset) {
+ self.type = type
+ self.amount = amount
+ self.senderPublicKey = senderPublicKey
+ self.requesterPublicKey = requesterPublicKey
+ self.timestamp = UInt64(AdamantUtilities.encodeAdamant(date: date))
+ self.recipientId = recipientId
+ self.asset = asset
}
}
diff --git a/Adamant/Models/StateAsset.swift b/Adamant/Models/StateAsset.swift
new file mode 100644
index 000000000..aa4eb199d
--- /dev/null
+++ b/Adamant/Models/StateAsset.swift
@@ -0,0 +1,23 @@
+//
+// StateAsset.swift
+// Adamant
+//
+// Created by Anokhov Pavel on 25.05.2018.
+// Copyright © 2018 Adamant. All rights reserved.
+//
+
+import Foundation
+
+struct StateAsset: Codable {
+ let key: String
+ let value: String
+ let type: StateType
+}
+
+/* JSON
+"state": {
+ "value": "myValue",
+ "key": "myKey",
+ "type": 0
+}
+*/
diff --git a/Adamant/Models/StateType.swift b/Adamant/Models/StateType.swift
new file mode 100644
index 000000000..4b8f6402c
--- /dev/null
+++ b/Adamant/Models/StateType.swift
@@ -0,0 +1,13 @@
+//
+// StateType.swift
+// Adamant
+//
+// Created by Anokhov Pavel on 25.05.2018.
+// Copyright © 2018 Adamant. All rights reserved.
+//
+
+import Foundation
+
+enum StateType: Int16, Codable {
+ case keyValue = 0
+}
diff --git a/Adamant/Models/TransactionAsset.swift b/Adamant/Models/TransactionAsset.swift
index 5d6f50b82..b3b00fdeb 100644
--- a/Adamant/Models/TransactionAsset.swift
+++ b/Adamant/Models/TransactionAsset.swift
@@ -2,15 +2,13 @@
// TransactionAsset.swift
// Adamant
//
-// Created by Anokhov Pavel on 12.01.2018.
+// Created by Anokhov Pavel on 25.05.2018.
// Copyright © 2018 Adamant. All rights reserved.
//
import Foundation
-/*
- Позже сделаю это дело расширяемым с помощью extension
-*/
struct TransactionAsset: Codable {
- let chat: Chat?
+ let chat: ChatAsset?
+ let state: StateAsset?
}
diff --git a/Adamant/Models/TransactionType.swift b/Adamant/Models/TransactionType.swift
index e7efbd5be..84e4dddd6 100644
--- a/Adamant/Models/TransactionType.swift
+++ b/Adamant/Models/TransactionType.swift
@@ -18,4 +18,5 @@ enum TransactionType: Int, Codable {
case inTransfer
case outTransfer
case chatMessage
+ case state
}
diff --git a/Adamant/ServiceProtocols/AdamantCore.swift b/Adamant/ServiceProtocols/AdamantCore.swift
index 07fd4340e..0d700f0d0 100644
--- a/Adamant/ServiceProtocols/AdamantCore.swift
+++ b/Adamant/ServiceProtocols/AdamantCore.swift
@@ -15,9 +15,20 @@ protocol AdamantCore: class {
func generateNewPassphrase() -> String
// MARK: - Signing transactions
- func sign(transaction: NormalizedTransaction, senderId: String, keypair: Keypair) -> String?
+ func sign(transaction: SignableTransaction, senderId: String, keypair: Keypair) -> String?
// MARK: - Encoding messages
func encodeMessage(_ message: String, recipientPublicKey: String, privateKey: String) -> (message: String, nonce: String)?
func decodeMessage(rawMessage: String, rawNonce: String, senderPublicKey: String, privateKey: String) -> String?
}
+
+protocol SignableTransaction {
+ var type: TransactionType { get }
+ var amount: Decimal { get }
+ var senderPublicKey: String { get }
+ var requesterPublicKey: String? { get }
+ var timestamp: UInt64 { get }
+ var recipientId: String? { get }
+
+ var asset: TransactionAsset { get }
+}
diff --git a/Adamant/Services/JSAdamantCore.swift b/Adamant/Services/JSAdamantCore.swift
index 067f626c9..66b248401 100644
--- a/Adamant/Services/JSAdamantCore.swift
+++ b/Adamant/Services/JSAdamantCore.swift
@@ -256,12 +256,13 @@ extension JSAdamantCore {
// MARK: - Transactions
extension JSAdamantCore {
- func sign(transaction t: NormalizedTransaction, senderId: String, keypair: Keypair) -> String? {
- let asset: JSAsset
+ func sign(transaction t: SignableTransaction, senderId: String, keypair: Keypair) -> String? {
+ let asset = JSAsset()
if let chat = t.asset.chat {
- asset = JSAsset(chat: JSChat(type: Int(chat.type.rawValue), message: chat.message, own_message: chat.ownMessage))
- } else {
- asset = JSAsset(chat: nil)
+ asset.chat = JSChat(type: Int(chat.type.rawValue), message: chat.message, own_message: chat.ownMessage)
+ }
+ if let store = t.asset.state {
+ asset.store = JSStore(key: store.key, value: store.value, type: Int(store.type.rawValue))
}
let jsTransaction = JSTransaction(id: 0,
diff --git a/AdamantTests/Parsing/ParsingModelsTests.swift b/AdamantTests/Parsing/ParsingModelsTests.swift
index 29864f460..ce8064877 100644
--- a/AdamantTests/Parsing/ParsingModelsTests.swift
+++ b/AdamantTests/Parsing/ParsingModelsTests.swift
@@ -71,7 +71,7 @@ class ParsingModelsTests: XCTestCase {
}
func testChat() {
- let c: Chat = TestTools.LoadJsonAndDecode(filename: "Chat")
+ let c: AssetChat = TestTools.LoadJsonAndDecode(filename: "Chat")
XCTAssertEqual(c.message, "7b7b3802f1d081e10624a373628fd0ba57e9348a7bca196c7511b05403a10611e3b4cf8b37cb9858f7f52cd5")
XCTAssertEqual(c.ownMessage, "f4f7972f735997b4c2014d87cb491bb156f9cc4d0404cb9c")
From f86db5899d4cf6a6459a6d5cd279cccb7f2eb5b2 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Fri, 25 May 2018 17:36:18 +0300
Subject: [PATCH 14/34] AdamantUtilities.decodeAdamant(timestamp:) and
encodeAdamant(date:)
---
Adamant/Models/Transaction.swift | 2 +-
Adamant/Utilities/AdamantUtilities.swift | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/Adamant/Models/Transaction.swift b/Adamant/Models/Transaction.swift
index ce9bcd386..0c639a37e 100644
--- a/Adamant/Models/Transaction.swift
+++ b/Adamant/Models/Transaction.swift
@@ -76,7 +76,7 @@ extension Transaction: Decodable {
let fee = try container.decode(Decimal.self, forKey: .fee)
self.fee = fee.shiftedFromAdamant()
- self.date = AdamantUtilities.decodeAdamantDate(timestamp: TimeInterval(self.timestamp))
+ self.date = AdamantUtilities.decodeAdamant(timestamp: TimeInterval(self.timestamp))
}
}
diff --git a/Adamant/Utilities/AdamantUtilities.swift b/Adamant/Utilities/AdamantUtilities.swift
index 580229e6f..f6b081682 100644
--- a/Adamant/Utilities/AdamantUtilities.swift
+++ b/Adamant/Utilities/AdamantUtilities.swift
@@ -94,7 +94,11 @@ extension AdamantUtilities {
// MARK: - Dates
extension AdamantUtilities {
- static func decodeAdamantDate(timestamp: TimeInterval) -> Date {
+ static func encodeAdamant(date: Date) -> TimeInterval {
+ return date.timeIntervalSince1970 - magicAdamantTimeInterval
+ }
+
+ static func decodeAdamant(timestamp: TimeInterval) -> Date {
return Date(timeIntervalSince1970: timestamp + magicAdamantTimeInterval)
}
From 8b4e31a62ebba25be02467e18acd07493e4544e7 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Fri, 25 May 2018 17:37:30 +0300
Subject: [PATCH 15/34] ProcessTransactionResponse -> TransactionIdResponse
---
Adamant.xcodeproj/project.pbxproj | 8 ++++----
...nsactionResponse.swift => TransactionIdResponse.swift} | 2 +-
Adamant/Services/ApiService/AdamantApi+Chats.swift | 4 ++--
3 files changed, 7 insertions(+), 7 deletions(-)
rename Adamant/ServerResponses/{ProcessTransactionResponse.swift => TransactionIdResponse.swift} (93%)
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index 4f082758b..0d39ea5bb 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -120,7 +120,7 @@
E9B3D3A9202082450019EB36 /* AdamantTransfersProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9B3D3A8202082450019EB36 /* AdamantTransfersProvider.swift */; };
E9C51ECF200E2D1100385EB7 /* FeeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9C51ECE200E2D1100385EB7 /* FeeTests.swift */; };
E9C51EED2011416E00385EB7 /* adamant-core.js in Resources */ = {isa = PBXBuildFile; fileRef = E9C51EEC2011416E00385EB7 /* adamant-core.js */; };
- E9C51EEF20139DC600385EB7 /* ProcessTransactionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9C51EEE20139DC600385EB7 /* ProcessTransactionResponse.swift */; };
+ E9C51EEF20139DC600385EB7 /* TransactionIdResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9C51EEE20139DC600385EB7 /* TransactionIdResponse.swift */; };
E9C51EF12013F18000385EB7 /* NewChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9C51EF02013F18000385EB7 /* NewChatViewController.swift */; };
E9CAE8D22018AA7700345E76 /* AdamantApi+Accounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9CAE8D12018AA7700345E76 /* AdamantApi+Accounts.swift */; };
E9CAE8D42018AC1800345E76 /* AdamantApi+Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9CAE8D32018AC1800345E76 /* AdamantApi+Keys.swift */; };
@@ -309,7 +309,7 @@
E9B3D3A8202082450019EB36 /* AdamantTransfersProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantTransfersProvider.swift; sourceTree = ""; };
E9C51ECE200E2D1100385EB7 /* FeeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeeTests.swift; sourceTree = ""; };
E9C51EEC2011416E00385EB7 /* adamant-core.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "adamant-core.js"; sourceTree = ""; };
- E9C51EEE20139DC600385EB7 /* ProcessTransactionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessTransactionResponse.swift; sourceTree = ""; };
+ E9C51EEE20139DC600385EB7 /* TransactionIdResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionIdResponse.swift; sourceTree = ""; };
E9C51EF02013F18000385EB7 /* NewChatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewChatViewController.swift; sourceTree = ""; };
E9CAE8D12018AA7700345E76 /* AdamantApi+Accounts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdamantApi+Accounts.swift"; sourceTree = ""; };
E9CAE8D32018AC1800345E76 /* AdamantApi+Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdamantApi+Keys.swift"; sourceTree = ""; };
@@ -541,7 +541,7 @@
isa = PBXGroup;
children = (
E95F85792007F0260070534A /* ServerResponse.swift */,
- E9C51EEE20139DC600385EB7 /* ProcessTransactionResponse.swift */,
+ E9C51EEE20139DC600385EB7 /* TransactionIdResponse.swift */,
E95F856E2007B61D0070534A /* GetPublicKeyResponse.swift */,
);
path = ServerResponses;
@@ -1025,7 +1025,7 @@
E93B0D742028B21400126346 /* ChatsProvider.swift in Sources */,
E9150BA12066DA210065A985 /* ChatTransaction+CoreDataClass.swift in Sources */,
E9CAE8D42018AC1800345E76 /* AdamantApi+Keys.swift in Sources */,
- E9C51EEF20139DC600385EB7 /* ProcessTransactionResponse.swift in Sources */,
+ E9C51EEF20139DC600385EB7 /* TransactionIdResponse.swift in Sources */,
E9722066201F42BB004F2AAD /* CoreDataStack.swift in Sources */,
E913C8F21FFFA51D001A83F7 /* AppDelegate.swift in Sources */,
E905D39B2048A9BD00DDB504 /* KeychainStore.swift in Sources */,
diff --git a/Adamant/ServerResponses/ProcessTransactionResponse.swift b/Adamant/ServerResponses/TransactionIdResponse.swift
similarity index 93%
rename from Adamant/ServerResponses/ProcessTransactionResponse.swift
rename to Adamant/ServerResponses/TransactionIdResponse.swift
index 9cf072c21..58ed08c33 100644
--- a/Adamant/ServerResponses/ProcessTransactionResponse.swift
+++ b/Adamant/ServerResponses/TransactionIdResponse.swift
@@ -8,7 +8,7 @@
import Foundation
-class ProcessTransactionResponse: ServerResponse {
+class TransactionIdResponse: ServerResponse {
let transactionId: UInt64?
required init(from decoder: Decoder) throws {
diff --git a/Adamant/Services/ApiService/AdamantApi+Chats.swift b/Adamant/Services/ApiService/AdamantApi+Chats.swift
index a9bb85c4b..a761782d9 100644
--- a/Adamant/Services/ApiService/AdamantApi+Chats.swift
+++ b/Adamant/Services/ApiService/AdamantApi+Chats.swift
@@ -105,7 +105,7 @@ extension AdamantApiService {
"senderPublicKey": normalizedTransaction.senderPublicKey,
"requesterPublicKey": normalizedTransaction.requesterPublicKey ?? NSNull(),
"timestamp": normalizedTransaction.timestamp,
- "recipientId": normalizedTransaction.recipientId,
+ "recipientId": normalizedTransaction.recipientId ?? NSNull(),
"senderId": senderId,
"signature": signature,
"asset": [
@@ -122,7 +122,7 @@ extension AdamantApiService {
]
// MARK: 5. Send
- self.sendRequest(url: processEndpoin, method: .post, parameters: params, encoding: .json, headers: headers) { (serverResponse: ApiServiceResult) in
+ self.sendRequest(url: processEndpoin, method: .post, parameters: params, encoding: .json, headers: headers) { (serverResponse: ApiServiceResult) in
switch serverResponse {
case .success(let response):
if let id = response.transactionId {
From 9e69d6d08a01a327d27dbe3b1e2c44bc834f3cee Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Fri, 25 May 2018 17:37:49 +0300
Subject: [PATCH 16/34] AdamantApi+States States.store method.
---
Adamant.xcodeproj/project.pbxproj | 4 +
Adamant/ServiceProtocols/ApiService.swift | 6 ++
.../ApiService/AdamantApi+States.swift | 76 +++++++++++++++++++
3 files changed, 86 insertions(+)
create mode 100644 Adamant/Services/ApiService/AdamantApi+States.swift
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index 0d39ea5bb..bb8d63f01 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -99,6 +99,7 @@
E95F85C7200A9B070070534A /* ChatTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85C5200A9B070070534A /* ChatTableViewCell.swift */; };
E95F85C8200A9B070070534A /* ChatTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E95F85C6200A9B070070534A /* ChatTableViewCell.xib */; };
E961E94D20A1E203005C8872 /* AnsApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E961E94C20A1E203005C8872 /* AnsApiService.swift */; };
+ E965A53020B594120041A3EA /* AdamantApi+States.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A52F20B594120041A3EA /* AdamantApi+States.swift */; };
E965A53220B82C850041A3EA /* StateType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A53120B82C850041A3EA /* StateType.swift */; };
E965A53420B833A00041A3EA /* StateAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A53320B833A00041A3EA /* StateAsset.swift */; };
E965A53620B8370C0041A3EA /* TransactionAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A53520B8370C0041A3EA /* TransactionAsset.swift */; };
@@ -288,6 +289,7 @@
E95F85C5200A9B070070534A /* ChatTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTableViewCell.swift; sourceTree = ""; };
E95F85C6200A9B070070534A /* ChatTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ChatTableViewCell.xib; sourceTree = ""; };
E961E94C20A1E203005C8872 /* AnsApiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnsApiService.swift; sourceTree = ""; };
+ E965A52F20B594120041A3EA /* AdamantApi+States.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdamantApi+States.swift"; sourceTree = ""; };
E965A53120B82C850041A3EA /* StateType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateType.swift; sourceTree = ""; };
E965A53320B833A00041A3EA /* StateAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateAsset.swift; sourceTree = ""; };
E965A53520B8370C0041A3EA /* TransactionAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionAsset.swift; sourceTree = ""; };
@@ -684,6 +686,7 @@
E9CAE8D32018AC1800345E76 /* AdamantApi+Keys.swift */,
E9CAE8D52018AC5300345E76 /* AdamantApi+Transactions.swift */,
E9CAE8D72018ACA700345E76 /* AdamantApi+Transfers.swift */,
+ E965A52F20B594120041A3EA /* AdamantApi+States.swift */,
);
path = ApiService;
sourceTree = "";
@@ -1042,6 +1045,7 @@
E95F85852008CB3A0070534A /* ChatListViewController.swift in Sources */,
E91947AC20001A9A001362F8 /* ApiService.swift in Sources */,
E9150B9F2066DA210065A985 /* MessageTransaction+CoreDataClass.swift in Sources */,
+ E965A53020B594120041A3EA /* AdamantApi+States.swift in Sources */,
E9150B982066DA210065A985 /* CoreDataAccount+CoreDataProperties.swift in Sources */,
E9B3D3A1201FA26B0019EB36 /* AdamantAccountsProvider.swift in Sources */,
E9FAE5DA203DBFEF008D3A6B /* Comparable+Clamped.swift in Sources */,
diff --git a/Adamant/ServiceProtocols/ApiService.swift b/Adamant/ServiceProtocols/ApiService.swift
index 98b9a56ce..a2b6376a0 100644
--- a/Adamant/ServiceProtocols/ApiService.swift
+++ b/Adamant/ServiceProtocols/ApiService.swift
@@ -78,6 +78,12 @@ protocol ApiService: class {
func transferFunds(sender: String, recipient: String, amount: Decimal, keypair: Keypair, completion: @escaping (ApiServiceResult) -> Void)
+ // MARK: - States
+
+ /// - Returns: Transaction ID
+ func store(key: String, value: String, type: StateType, sender: String, keypair: Keypair, completion: @escaping (ApiServiceResult) -> Void)
+
+
// MARK: - Chats
/// Get chat transactions (type 8)
diff --git a/Adamant/Services/ApiService/AdamantApi+States.swift b/Adamant/Services/ApiService/AdamantApi+States.swift
new file mode 100644
index 000000000..591ea7cd2
--- /dev/null
+++ b/Adamant/Services/ApiService/AdamantApi+States.swift
@@ -0,0 +1,76 @@
+//
+// AdamantApi+States.swift
+// Adamant
+//
+// Created by Anokhov Pavel on 23.05.2018.
+// Copyright © 2018 Adamant. All rights reserved.
+//
+
+import Foundation
+
+extension AdamantApiService.ApiCommands {
+ static let States = (
+ root: "/api/states",
+ getTransactions: "/api/states/get",
+ store: "/api/states/store"
+ )
+}
+
+extension AdamantApiService {
+ func store(key: String, value: String, type: StateType, sender: String, keypair: Keypair, completion: @escaping (ApiServiceResult) -> Void) {
+
+ // MARK: 1. Create and sign transaction
+ let asset = TransactionAsset(chat: nil, state: StateAsset(key: key, value: value, type: .keyValue))
+ let transaction = NormalizedTransaction(type: .state,
+ amount: 0,
+ senderPublicKey: keypair.publicKey,
+ requesterPublicKey: nil,
+ date: Date(),
+ recipientId: nil,
+ asset: asset)
+ guard let signature = adamantCore.sign(transaction: transaction, senderId: sender, keypair: keypair) else {
+ completion(.failure(.internalError(message: "Failed to sign transaction", error: nil)))
+ return
+ }
+
+ let params: [String: Any] = [
+ "type": transaction.type.rawValue,
+ "amount": transaction.amount,
+ "senderPublicKey": transaction.senderPublicKey,
+ "senderId": sender,
+ "timestamp": transaction.timestamp,
+ "signature": signature,
+ "asset": transaction.asset
+ ]
+
+ let headers = [
+ "Content-Type": "application/json"
+ ]
+
+ // MARK: 2. Build endpoints
+ let endpoint: URL
+
+ do {
+ endpoint = try buildUrl(path: ApiCommands.States.store)
+ } catch {
+ let err = InternalError.endpointBuildFailed.apiServiceErrorWith(error: error)
+ completion(.failure(err))
+ return
+ }
+
+ // MARK: 3. Send
+ sendRequest(url: endpoint, method: .post, parameters: params, encoding: .json, headers: headers) { (serverResponse: ApiServiceResult) in
+ switch serverResponse {
+ case .success(let response):
+ if let id = response.transactionId {
+ completion(.success(id))
+ } else {
+ completion(ApiServiceResult.success(0))
+ }
+
+ case .failure(let error):
+ completion(.failure(.networkError(error: error)))
+ }
+ }
+ }
+}
From 175eaf6ca5f7bffb0985c29912a16ef70914675e Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Sat, 26 May 2018 17:35:20 +0300
Subject: [PATCH 17/34] adamant-core.js fixed. store -> state
---
Adamant/Assets/adamant-core.js | 42 +++++++++++++++++--
Adamant/Helpers/JSModels.swift | 8 ++--
.../TransactionIdResponse.swift | 2 +-
.../ApiService/AdamantApi+States.swift | 23 ++++++----
Adamant/Services/JSAdamantCore.swift | 4 +-
5 files changed, 62 insertions(+), 17 deletions(-)
diff --git a/Adamant/Assets/adamant-core.js b/Adamant/Assets/adamant-core.js
index 187e68009..0f6cf48e0 100644
--- a/Adamant/Assets/adamant-core.js
+++ b/Adamant/Assets/adamant-core.js
@@ -42501,12 +42501,19 @@ class Adamant {
var assetBytes = null
switch (transaction.type) {
- case 0:
+ case 0: // Send
break
- case 8:
+
+ case 8: // Chat message
assetBytes = this.chatGetBytes(transaction)
assetSize = assetBytes.length
break
+
+ case 9: // State
+ assetBytes = this.statesGetBytes(transaction)
+ assetSize = assetBytes.length
+ break
+
default:
alert('Not supported yet')
}
@@ -42578,7 +42585,7 @@ class Adamant {
static transactionSign (trs, keypair) {
var hash = Adamant.getHash(trs)
- return Adamant.sign(hash, keypair).toString('hex')
+ return Adamant.sign(hash, keypair).toString('hex');
}
static chatGetBytes (trs) {
@@ -42596,6 +42603,7 @@ class Adamant {
var bb = new ByteBuffer(4 + 4, true)
bb.writeInt(trs.asset.chat.type)
bb.flip()
+
buf = Buffer.concat([buf, Buffer.from(bb.toBuffer())])
} catch (e) {
throw e
@@ -42604,6 +42612,34 @@ class Adamant {
return buf
}
+ static statesGetBytes (trs) {
+ if (!trs.asset.state.value) {
+ return null;
+ }
+ var buf;
+
+ try {
+ buf = Buffer.from([]);
+ var stateBuf = Buffer.from(trs.asset.state.value);
+ buf = Buffer.concat([buf, stateBuf]);
+
+ if (trs.asset.state.key) {
+ var keyBuf = Buffer.from(trs.asset.state.key);
+ buf = Buffer.concat([buf, keyBuf]);
+ }
+
+ var bb = new ByteBuffer(4 + 4, true);
+ bb.writeInt(trs.asset.state.type);
+ bb.flip();
+
+ buf = Buffer.concat([buf, Buffer.from(bb.toBuffer())]);
+ } catch (e) {
+ throw e;
+ }
+
+ return buf;
+ }
+
/**
* Creates a signature based on a hash and a keypair.
* @implements {sodium}
diff --git a/Adamant/Helpers/JSModels.swift b/Adamant/Helpers/JSModels.swift
index 034dee6e4..73b34ce11 100644
--- a/Adamant/Helpers/JSModels.swift
+++ b/Adamant/Helpers/JSModels.swift
@@ -37,12 +37,12 @@ import JavaScriptCore
@objc protocol JSAssetProtocol: JSExport {
var chat: JSChat? { get set }
- var store: JSStore? { get set }
+ var state: JSState? { get set }
}
@objc class JSAsset: NSObject, JSAssetProtocol {
dynamic var chat: JSChat?
- dynamic var store: JSStore?
+ dynamic var state: JSState?
}
@@ -69,13 +69,13 @@ import JavaScriptCore
// MARK: - Store
-@objc protocol JSStoreProtocol: JSExport {
+@objc protocol JSStateProtocol: JSExport {
var key: String { get set }
var value: String { get set }
var type: Int { get set }
}
-@objc class JSStore: NSObject, JSStoreProtocol {
+@objc class JSState: NSObject, JSStateProtocol {
dynamic var key: String
dynamic var value: String
dynamic var type: Int
diff --git a/Adamant/ServerResponses/TransactionIdResponse.swift b/Adamant/ServerResponses/TransactionIdResponse.swift
index 58ed08c33..5091ad889 100644
--- a/Adamant/ServerResponses/TransactionIdResponse.swift
+++ b/Adamant/ServerResponses/TransactionIdResponse.swift
@@ -16,7 +16,7 @@ class TransactionIdResponse: ServerResponse {
let success = try container.decode(Bool.self, forKey: .success)
let error = try? container.decode(String.self, forKey: .error)
- if let idRaw = try? container.decode(String.self, forKey: CodingKeys.init(stringValue: "transactionId")!) {
+ if let idRaw = try? container.decode(String.self, forKey: CodingKeys(stringValue: "transactionId")!) {
transactionId = UInt64(idRaw)
} else {
transactionId = nil
diff --git a/Adamant/Services/ApiService/AdamantApi+States.swift b/Adamant/Services/ApiService/AdamantApi+States.swift
index 591ea7cd2..5c3ce2bfe 100644
--- a/Adamant/Services/ApiService/AdamantApi+States.swift
+++ b/Adamant/Services/ApiService/AdamantApi+States.swift
@@ -34,13 +34,22 @@ extension AdamantApiService {
}
let params: [String: Any] = [
- "type": transaction.type.rawValue,
- "amount": transaction.amount,
- "senderPublicKey": transaction.senderPublicKey,
- "senderId": sender,
- "timestamp": transaction.timestamp,
- "signature": signature,
- "asset": transaction.asset
+ "transaction": [
+ "type": transaction.type.rawValue,
+ "amount": transaction.amount,
+ "senderPublicKey": transaction.senderPublicKey,
+ "senderId": sender,
+ "timestamp": transaction.timestamp,
+ "signature": signature,
+ "recipientId": NSNull(),
+ "asset": [
+ "state": [
+ "key": key,
+ "value": value,
+ "type": type.rawValue
+ ]
+ ]
+ ]
]
let headers = [
diff --git a/Adamant/Services/JSAdamantCore.swift b/Adamant/Services/JSAdamantCore.swift
index 66b248401..7b361c51c 100644
--- a/Adamant/Services/JSAdamantCore.swift
+++ b/Adamant/Services/JSAdamantCore.swift
@@ -261,8 +261,8 @@ extension JSAdamantCore {
if let chat = t.asset.chat {
asset.chat = JSChat(type: Int(chat.type.rawValue), message: chat.message, own_message: chat.ownMessage)
}
- if let store = t.asset.state {
- asset.store = JSStore(key: store.key, value: store.value, type: Int(store.type.rawValue))
+ if let state = t.asset.state {
+ asset.state = JSState(key: state.key, value: state.value, type: Int(state.type.rawValue))
}
let jsTransaction = JSTransaction(id: 0,
From b406f6507ae7500a77cd2ee26ca7f44e0fd01156 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Sat, 26 May 2018 19:02:57 +0300
Subject: [PATCH 18/34] AppDelegate: storing device token's hash, checking if
it has changed and needed to be stored in blockchain.
---
Adamant.xcodeproj/project.pbxproj | 2 +
Adamant/AppDelegate.swift | 46 +++++++++++++------
.../Services/AdamantNotificationService.swift | 1 +
Podfile | 1 +
Podfile.lock | 6 ++-
5 files changed, 40 insertions(+), 16 deletions(-)
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index bb8d63f01..3b4acc332 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -910,6 +910,7 @@
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Adamant/Pods-Adamant-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
+ "${BUILT_PRODUCTS_DIR}/CryptoSwift/CryptoSwift.framework",
"${BUILT_PRODUCTS_DIR}/EFQRCode/EFQRCode.framework",
"${BUILT_PRODUCTS_DIR}/Eureka/Eureka.framework",
"${BUILT_PRODUCTS_DIR}/FTIndicator/FTIndicator.framework",
@@ -926,6 +927,7 @@
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptoSwift.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EFQRCode.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Eureka.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FTIndicator.framework",
diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift
index 6c5190ad2..37a45c6a6 100644
--- a/Adamant/AppDelegate.swift
+++ b/Adamant/AppDelegate.swift
@@ -8,6 +8,7 @@
import UIKit
import Swinject
+import CryptoSwift
extension String.adamantLocalized {
struct tabItems {
@@ -17,12 +18,20 @@ extension String.adamantLocalized {
}
}
+extension StoreKey {
+ struct application {
+ static let deviceTokenHash = "app.deviceTokenHash"
+
+ private init() {}
+ }
+}
+
// MARK: Resources
struct AdamantResources {
// Storyboard
static let jsCore = Bundle.main.url(forResource: "adamant-core", withExtension: "js")!
static let api = URL(string: "https://endless.adamant.im")!
- static let ans = URL(string: "")!
+ static let ans = URL(string: "https://192.168.1.69:44340")!
static let coreDataModel = Bundle.main.url(forResource: "ChatModels", withExtension: "momd")!
private init() {}
@@ -36,7 +45,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var container: Container!
// MARK: Dependencies
- var ansApiService: AnsApiService!
var accountService: AccountService!
var notificationService: NotificationsService!
@@ -49,8 +57,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
accountService = container.resolve(AccountService.self)
notificationService = container.resolve(NotificationsService.self)
- ansApiService = AnsApiService(ansApi: AdamantResources.ans)
-
// MARK: 2. Init UI
window = UIWindow(frame: UIScreen.main.bounds)
window!.rootViewController = UITabBarController()
@@ -183,12 +189,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: - Remote notifications
extension AppDelegate {
-// func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
-// print(userInfo)
-// }
-
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
- guard let address = accountService.account?.address else {
+ guard let address = accountService.account?.address, let keypair = accountService.keypair else {
print("Trying to register with no user logged")
UIApplication.shared.unregisterForRemoteNotifications()
return
@@ -196,23 +198,37 @@ extension AppDelegate {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
- ansApiService?.register(token: token, address: address) { [weak self] result in
+ // Checking, if device token had not changed
+ guard let securedStore = container.resolve(SecuredStore.self) else {
+ fatalError("can't get secured store to get device token hash")
+ }
+
+ let tokenHash = token.md5()
+
+ if let savedHash = securedStore.get(StoreKey.application.deviceTokenHash), tokenHash == savedHash {
+ return
+ } else {
+ securedStore.set(tokenHash, for: StoreKey.application.deviceTokenHash)
+ }
+
+ // Storing new token in blockchain
+ guard let apiService = container.resolve(ApiService.self) else {
+ fatalError("can't get api service to register device token")
+ }
+
+ apiService.store(key: "deviceToken", value: token, type: StateType.keyValue, sender: address, keypair: keypair) { [weak self] result in
switch result {
case .success:
return
case .failure(let error):
+ print("Failed to store device token: \(error)")
self?.notificationService?.setNotificationsMode(.disabled, completion: nil)
- if let service = self?.container.resolve(DialogService.self) {
- service.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription))
- }
}
}
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
- notificationService?.setNotificationsMode(.disabled, completion: nil)
-
if let service = container.resolve(DialogService.self) {
service.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription))
}
diff --git a/Adamant/Services/AdamantNotificationService.swift b/Adamant/Services/AdamantNotificationService.swift
index 234a0b747..0e3df7406 100644
--- a/Adamant/Services/AdamantNotificationService.swift
+++ b/Adamant/Services/AdamantNotificationService.swift
@@ -51,6 +51,7 @@ class AdamantNotificationsService: NotificationsService {
NotificationCenter.default.addObserver(forName: Notification.Name.AdamantAccountService.userLoggedOut, object: nil, queue: nil) { [weak self] _ in
self?.setNotificationsMode(.disabled, completion: nil)
+ self?.securedStore.remove(StoreKey.notificationsService.notificationsMode)
}
}
diff --git a/Podfile b/Podfile
index ed9f1912a..afb89baf9 100644
--- a/Podfile
+++ b/Podfile
@@ -7,6 +7,7 @@ target 'Adamant' do
pod 'Alamofire' # Network
pod 'KeychainAccess' # Keychain
pod 'RNCryptor' # Cryptor
+ pod 'CryptoSwift' # MD5 hash
pod 'Swinject' # Dependency Injection
pod 'ReachabilitySwift' # Network status
pod 'Haring' # Markdown parser
diff --git a/Podfile.lock b/Podfile.lock
index 6945a7a13..85de429a3 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -1,5 +1,6 @@
PODS:
- Alamofire (4.7.2)
+ - CryptoSwift (0.9.0)
- EFQRCode (4.2.1)
- Eureka (4.1.1)
- FreakingSimpleRoundImageView (1.1)
@@ -21,6 +22,7 @@ PODS:
DEPENDENCIES:
- Alamofire
+ - CryptoSwift
- EFQRCode
- Eureka
- FreakingSimpleRoundImageView
@@ -37,6 +39,7 @@ DEPENDENCIES:
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
- Alamofire
+ - CryptoSwift
- EFQRCode
- Eureka
- FreakingSimpleRoundImageView
@@ -52,6 +55,7 @@ SPEC REPOS:
SPEC CHECKSUMS:
Alamofire: e4fa87002c137ba2d8d634d2c51fabcda0d5c223
+ CryptoSwift: bca8c5b653dcc2d9734409242a070ff53bafac86
EFQRCode: f3fd67049faa07adb575495c05d72a34e407a940
Eureka: b88fb930e42c79f8c03c373d0fcdc28c1d6c50ed
FreakingSimpleRoundImageView: 4a3a1cb1347beb247f8c63b5b5cae9d770b431ee
@@ -65,6 +69,6 @@ SPEC CHECKSUMS:
RNCryptor: c93d19029dcf7ff160aca0f24d6c9e7b0d82f664
Swinject: a1364b0f66c2736bb03c1c7cab54809e16df25da
-PODFILE CHECKSUM: 2afa7e8b57ec3b78d5cea2c6ce51fd5f71e3c1f6
+PODFILE CHECKSUM: 12f69d43b1b41b997e7c698aa56457f71d9ade99
COCOAPODS: 1.5.0
From a77a8b43f1c04e6ff96b50c93219654c86bea437 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Sat, 26 May 2018 19:34:08 +0300
Subject: [PATCH 19/34] AnsApiService deprecated and removed.
---
Adamant.xcodeproj/project.pbxproj | 12 ----
Adamant/AppDelegate.swift | 2 -
.../AdamantServices/AnsApiService.swift | 67 -------------------
3 files changed, 81 deletions(-)
delete mode 100644 Adamant/Services/AdamantServices/AnsApiService.swift
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index 3b4acc332..7b52b988c 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -98,7 +98,6 @@
E95F85C4200A540B0070534A /* Chat.json in Resources */ = {isa = PBXBuildFile; fileRef = E95F85C3200A540B0070534A /* Chat.json */; };
E95F85C7200A9B070070534A /* ChatTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85C5200A9B070070534A /* ChatTableViewCell.swift */; };
E95F85C8200A9B070070534A /* ChatTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E95F85C6200A9B070070534A /* ChatTableViewCell.xib */; };
- E961E94D20A1E203005C8872 /* AnsApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E961E94C20A1E203005C8872 /* AnsApiService.swift */; };
E965A53020B594120041A3EA /* AdamantApi+States.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A52F20B594120041A3EA /* AdamantApi+States.swift */; };
E965A53220B82C850041A3EA /* StateType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A53120B82C850041A3EA /* StateType.swift */; };
E965A53420B833A00041A3EA /* StateAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E965A53320B833A00041A3EA /* StateAsset.swift */; };
@@ -288,7 +287,6 @@
E95F85C3200A540B0070534A /* Chat.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Chat.json; sourceTree = ""; };
E95F85C5200A9B070070534A /* ChatTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTableViewCell.swift; sourceTree = ""; };
E95F85C6200A9B070070534A /* ChatTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ChatTableViewCell.xib; sourceTree = ""; };
- E961E94C20A1E203005C8872 /* AnsApiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnsApiService.swift; sourceTree = ""; };
E965A52F20B594120041A3EA /* AdamantApi+States.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdamantApi+States.swift"; sourceTree = ""; };
E965A53120B82C850041A3EA /* StateType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateType.swift; sourceTree = ""; };
E965A53320B833A00041A3EA /* StateAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateAsset.swift; sourceTree = ""; };
@@ -447,7 +445,6 @@
E913C9061FFFA92E001A83F7 /* Services */ = {
isa = PBXGroup;
children = (
- E965A52E20ADC6080041A3EA /* AdamantServices */,
E9CAE8D02018AA5000345E76 /* ApiService */,
E9B3D39F201FA2090019EB36 /* DataProviders */,
E9E7CD922002740500DFC4DB /* AdamantAccountService.swift */,
@@ -632,14 +629,6 @@
path = Parsing;
sourceTree = "";
};
- E965A52E20ADC6080041A3EA /* AdamantServices */ = {
- isa = PBXGroup;
- children = (
- E961E94C20A1E203005C8872 /* AnsApiService.swift */,
- );
- path = AdamantServices;
- sourceTree = "";
- };
E982F69820235AF000566AC7 /* Settings */ = {
isa = PBXGroup;
children = (
@@ -1024,7 +1013,6 @@
E9942B87203D9E5100C163AF /* EurekaQRRow.swift in Sources */,
E93EFE13200D1156000BB482 /* ChatViewController.swift in Sources */,
E9B3D39E201F99F40019EB36 /* DataProvider.swift in Sources */,
- E961E94D20A1E203005C8872 /* AnsApiService.swift in Sources */,
E9E7CDC02003AF6D00DFC4DB /* AdamantCellFactory.swift in Sources */,
E91A063A209F05AA0018A102 /* NotificationsViewController.swift in Sources */,
E93B0D742028B21400126346 /* ChatsProvider.swift in Sources */,
diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift
index 37a45c6a6..0bdeba30f 100644
--- a/Adamant/AppDelegate.swift
+++ b/Adamant/AppDelegate.swift
@@ -28,10 +28,8 @@ extension StoreKey {
// MARK: Resources
struct AdamantResources {
- // Storyboard
static let jsCore = Bundle.main.url(forResource: "adamant-core", withExtension: "js")!
static let api = URL(string: "https://endless.adamant.im")!
- static let ans = URL(string: "https://192.168.1.69:44340")!
static let coreDataModel = Bundle.main.url(forResource: "ChatModels", withExtension: "momd")!
private init() {}
diff --git a/Adamant/Services/AdamantServices/AnsApiService.swift b/Adamant/Services/AdamantServices/AnsApiService.swift
deleted file mode 100644
index 1998cee95..000000000
--- a/Adamant/Services/AdamantServices/AnsApiService.swift
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-// AnsApiService.swift
-// Adamant
-//
-// Created by Anokhov Pavel on 08.05.2018.
-// Copyright © 2018 Adamant. All rights reserved.
-//
-
-import Foundation
-import Alamofire
-
-enum AnsApiServiceResult {
- case success
- case failure(ApiServiceError)
-}
-
-class AnsApiService {
-
- /// Fallback registration ANS address
- static private let ansAccount = (
- address: "U10629337621822775991",
- publicKey: "188b24bd116a556ac8ba905bbbdaa16e237dfb14269f5a4f9a26be77537d977c"
- )
-
- struct ApiCommands {
- static let register = "/api/devices/register"
-
- private init() {}
- }
-
- // MARK: Properties
-
- let ansApi: URL
- var defaultResponseDispatchQueue = DispatchQueue(label: "com.adamant.ans-queue", qos: .utility, attributes: [.concurrent])
-
- init(ansApi: URL) {
- self.ansApi = ansApi
- }
-
- func register(token: String, address: String, completion: @escaping (AnsApiServiceResult) -> Void) {
- let parameters: [String: Any] = [
- "token": token,
- "address": address
- ]
-
- let headers = [
- "Content-Type": "application/json"
- ]
-
- guard var components = URLComponents(url: ansApi, resolvingAgainstBaseURL: false) else {
- fatalError("Parsing API URL failed: \(ansApi)")
- }
-
- components.path = ApiCommands.register
- let url = try! components.asURL()
-
- Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).validate().responseData(queue: defaultResponseDispatchQueue) { response in
- switch response.result {
- case .success:
- completion(.success)
-
- case .failure(let error):
- completion(.failure(.networkError(error: error)))
- }
- }
- }
-}
From 398d18be5a7502aefa4d622dfb488a6f7af9c3a1 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Sat, 26 May 2018 19:35:03 +0300
Subject: [PATCH 20/34] AccountService: disable notifications on dropping saved
user. SettingsViewController: hiding notification options.
---
Adamant/Services/AdamantAccountService.swift | 2 +
.../SettingsViewController+StayIn.swift | 40 +++++++++++++------
.../Settings/SettingsViewController.swift | 19 +++++++--
Adamant/SwinjectDependencies.swift | 1 +
4 files changed, 45 insertions(+), 17 deletions(-)
diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift
index 931a3be61..a6b17e915 100644
--- a/Adamant/Services/AdamantAccountService.swift
+++ b/Adamant/Services/AdamantAccountService.swift
@@ -14,6 +14,7 @@ class AdamantAccountService {
var apiService: ApiService!
var adamantCore: AdamantCore!
+ var notificationsService: NotificationsService!
var securedStore: SecuredStore! {
didSet {
securedStoreSemaphore.wait()
@@ -134,6 +135,7 @@ extension AdamantAccountService {
securedStore.remove(.privateKey)
securedStore.remove(.useBiometry)
hasStayInAccount = false
+ notificationsService.setNotificationsMode(.disabled, completion: nil)
}
}
diff --git a/Adamant/Stories/Settings/SettingsViewController+StayIn.swift b/Adamant/Stories/Settings/SettingsViewController+StayIn.swift
index d8dc9b7d6..51c5f30dc 100644
--- a/Adamant/Stories/Settings/SettingsViewController+StayIn.swift
+++ b/Adamant/Stories/Settings/SettingsViewController+StayIn.swift
@@ -47,7 +47,7 @@ extension SettingsViewController {
// MARK: Use biometry
func setBiometry(enabled: Bool) {
- guard showBiometryRow, accountService.hasStayInAccount, accountService.useBiometry != enabled else {
+ guard showLoggedInOptions, accountService.hasStayInAccount, accountService.useBiometry != enabled else {
return
}
@@ -86,18 +86,20 @@ extension SettingsViewController {
case .failed:
DispatchQueue.main.async {
- guard let row: SwitchRow = self?.form.rowBy(tag: Rows.biometry.tag) else {
- return
+ if let row: SwitchRow = self?.form.rowBy(tag: Rows.biometry.tag) {
+ if let value = self?.accountService.useBiometry {
+ row.value = value
+ } else {
+ row.value = false
+ }
+
+ row.updateCell()
+ row.evaluateHidden()
}
- if let value = self?.accountService.useBiometry {
- row.value = value
- } else {
- row.value = false
+ if let row: LabelRow = self?.form.rowBy(tag: Rows.notifications.tag) {
+ row.evaluateHidden()
}
-
- row.updateCell()
- row.evaluateHidden()
}
}
}
@@ -134,12 +136,16 @@ extension SettingsViewController: PinpadViewControllerDelegate {
if let biometryType = self?.localAuth.biometryType,
biometryType == .touchID || biometryType == .faceID,
let row: SwitchRow = self?.form.rowBy(tag: Rows.biometry.tag) {
- self?.showBiometryRow = true
+ self?.showLoggedInOptions = true
row.value = false
row.updateCell()
row.evaluateHidden()
}
+ if let row: LabelRow = self?.form.rowBy(tag: Rows.notifications.tag) {
+ row.evaluateHidden()
+ }
+
pinpad.dismiss(animated: true, completion: nil)
}
@@ -159,12 +165,16 @@ extension SettingsViewController: PinpadViewControllerDelegate {
accountService.dropSavedAccount()
if let row: SwitchRow = form.rowBy(tag: Rows.biometry.tag) {
- showBiometryRow = false
+ showLoggedInOptions = false
row.value = false
row.updateCell()
row.evaluateHidden()
}
+ if let row: LabelRow = form.rowBy(tag: Rows.notifications.tag) {
+ row.evaluateHidden()
+ }
+
pinpad.dismiss(animated: true, completion: nil)
@@ -208,12 +218,16 @@ extension SettingsViewController: PinpadViewControllerDelegate {
DispatchQueue.main.async {
if let row: SwitchRow = self?.form.rowBy(tag: Rows.biometry.tag) {
- self?.showBiometryRow = false
+ self?.showLoggedInOptions = false
row.value = false
row.updateCell()
row.evaluateHidden()
}
+ if let row: LabelRow = self?.form.rowBy(tag: Rows.notifications.tag) {
+ row.evaluateHidden()
+ }
+
pinpad.dismiss(animated: true, completion: nil)
}
diff --git a/Adamant/Stories/Settings/SettingsViewController.swift b/Adamant/Stories/Settings/SettingsViewController.swift
index f1b90458e..b87bf20d1 100644
--- a/Adamant/Stories/Settings/SettingsViewController.swift
+++ b/Adamant/Stories/Settings/SettingsViewController.swift
@@ -89,7 +89,7 @@ class SettingsViewController: FormViewController {
// MARK: Properties
- var showBiometryRow = false
+ var showLoggedInOptions = false
var pinpadRequest: SettingsViewController.PinpadRequest?
@@ -98,7 +98,7 @@ class SettingsViewController: FormViewController {
super.viewDidLoad()
self.navigationItem.title = String.adamantLocalized.settings.title
navigationOptions = .Disabled
- showBiometryRow = accountService.hasStayInAccount
+ showLoggedInOptions = accountService.hasStayInAccount
// MARK: Settings
form +++ Section(Sections.settings.localized)
@@ -123,7 +123,7 @@ class SettingsViewController: FormViewController {
$0.tag = Rows.biometry.tag
$0.value = accountService.useBiometry
$0.hidden = Condition.function([], { [weak self] _ -> Bool in
- guard let showBiometry = self?.showBiometryRow else {
+ guard let showBiometry = self?.showLoggedInOptions else {
return true
}
@@ -149,6 +149,13 @@ class SettingsViewController: FormViewController {
<<< LabelRow() {
$0.title = Rows.notifications.localized
$0.tag = Rows.notifications.tag
+ $0.hidden = Condition.function([], { [weak self] _ -> Bool in
+ guard let showNotifications = self?.showLoggedInOptions else {
+ return true
+ }
+
+ return !showNotifications
+ })
}.cellSetup({ (cell, _) in
cell.selectionStyle = .gray
}).onCellSelection({ [weak self] (cell, _) in
@@ -225,7 +232,7 @@ class SettingsViewController: FormViewController {
return
}
- self?.showBiometryRow = accountService.hasStayInAccount
+ self?.showLoggedInOptions = accountService.hasStayInAccount
if let row: SwitchRow = form.rowBy(tag: Rows.stayLoggedIn.tag) {
row.value = accountService.hasStayInAccount
@@ -235,6 +242,10 @@ class SettingsViewController: FormViewController {
row.value = accountService.hasStayInAccount && accountService.useBiometry
row.evaluateHidden()
}
+
+ if let row: LabelRow = form.rowBy(tag: Rows.notifications.tag) {
+ row.evaluateHidden()
+ }
}
}
diff --git a/Adamant/SwinjectDependencies.swift b/Adamant/SwinjectDependencies.swift
index ba69519b4..2f95da748 100644
--- a/Adamant/SwinjectDependencies.swift
+++ b/Adamant/SwinjectDependencies.swift
@@ -70,6 +70,7 @@ extension Container {
service.apiService = r.resolve(ApiService.self)!
service.adamantCore = r.resolve(AdamantCore.self)!
service.securedStore = r.resolve(SecuredStore.self)!
+ service.notificationsService = r.resolve(NotificationsService.self)!
return service
}.inObjectScope(.container)
From 0bbcbdbad7dcaa57a865249bfe1b9066a95780e8 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Sat, 26 May 2018 20:07:10 +0300
Subject: [PATCH 21/34] Pods updated
---
Podfile.lock | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/Podfile.lock b/Podfile.lock
index 85de429a3..2ae0fffe7 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -4,18 +4,18 @@ PODS:
- EFQRCode (4.2.1)
- Eureka (4.1.1)
- FreakingSimpleRoundImageView (1.1)
- - FTIndicator (1.2.8):
- - FTIndicator/FTNotificationIndicator (= 1.2.8)
- - FTIndicator/FTProgressIndicator (= 1.2.8)
- - FTIndicator/FTToastIndicator (= 1.2.8)
- - FTIndicator/FTNotificationIndicator (1.2.8)
- - FTIndicator/FTProgressIndicator (1.2.8)
- - FTIndicator/FTToastIndicator (1.2.8)
- - Haring (2.0.7)
+ - FTIndicator (1.2.9):
+ - FTIndicator/FTNotificationIndicator (= 1.2.9)
+ - FTIndicator/FTProgressIndicator (= 1.2.9)
+ - FTIndicator/FTToastIndicator (= 1.2.9)
+ - FTIndicator/FTNotificationIndicator (1.2.9)
+ - FTIndicator/FTProgressIndicator (1.2.9)
+ - FTIndicator/FTToastIndicator (1.2.9)
+ - Haring (2.0.8)
- KeychainAccess (3.1.1)
- - MessageKit (0.13.4)
+ - MessageKit (0.13.5)
- MyLittlePinpad (0.2.4)
- - QRCodeReader.swift (8.1.1)
+ - QRCodeReader.swift (8.2.0)
- ReachabilitySwift (4.1.0)
- RNCryptor (5.0.3)
- Swinject (2.4.0)
@@ -59,12 +59,12 @@ SPEC CHECKSUMS:
EFQRCode: f3fd67049faa07adb575495c05d72a34e407a940
Eureka: b88fb930e42c79f8c03c373d0fcdc28c1d6c50ed
FreakingSimpleRoundImageView: 4a3a1cb1347beb247f8c63b5b5cae9d770b431ee
- FTIndicator: 5218a42e6a3bb6623f58e4931fc36bb09b7e1b78
- Haring: e9fa27a6ca648045fbacccc59547abed44b26694
+ FTIndicator: f7f071fd159e5befa1d040a9ef2e3ab53fa9322c
+ Haring: d2a4cfc00dfb63836dffc93e45919369f850e134
KeychainAccess: 7bd430028059754a3debab3cfc0bd1fc7fb85df3
- MessageKit: b600e3f466632f93c0333c78fd9006c960c8cbe2
+ MessageKit: c4bd50e285a0f0761bc23a2e982aa7840873c4f5
MyLittlePinpad: dc5f8a7fc13a4ad6fc9dc8d3359d91f1b5b1c7e8
- QRCodeReader.swift: b164a681887de276d405ff02bce854d82cd6360b
+ QRCodeReader.swift: 003eb32f18a5a675b936ec82ba0ff368cddbff45
ReachabilitySwift: 6849231cd4e06559f3b9ef4a97a0a0f96d41e09f
RNCryptor: c93d19029dcf7ff160aca0f24d6c9e7b0d82f664
Swinject: a1364b0f66c2736bb03c1c7cab54809e16df25da
From 5b78cdf2e7fce5b5a91456c3067f128a9e9eac0b Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Tue, 29 May 2018 16:58:19 +0300
Subject: [PATCH 22/34] Readme.md updated.
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 0e76f5dd7..ba0d03393 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# iOS version of ADAMANT Messenger
+# Adamant-iOS
-It's a native iOS version of ADAMANT Messenger. Available at App Store *(now it's in TestFlight)*. You can use this repository to build your own ADAMANT iOS application.
+iOS native client for ADAMANT Messenger. Available at App Store *(now it's in TestFlight)*. You can use this repository to build your own ADAMANT iOS application.
ADAMANT is the most secure and anonymous messenger, encrypted with Blockchain.
From 7ddc41aacc8af6b5c7dfdfe1ed1b0ae1c5d319b5 Mon Sep 17 00:00:00 2001
From: Anton B
Date: Tue, 29 May 2018 19:42:46 +0300
Subject: [PATCH 23/34] add offset and limit
Add limit and offset parameters to transaction request
---
Adamant/ServiceProtocols/ApiService.swift | 2 +-
.../ApiService/AdamantApi+Transactions.swift | 71 ++++++++++---------
2 files changed, 40 insertions(+), 33 deletions(-)
diff --git a/Adamant/ServiceProtocols/ApiService.swift b/Adamant/ServiceProtocols/ApiService.swift
index a2b6376a0..962c42516 100644
--- a/Adamant/ServiceProtocols/ApiService.swift
+++ b/Adamant/ServiceProtocols/ApiService.swift
@@ -70,7 +70,7 @@ protocol ApiService: class {
// MARK: - Transactions
func getTransaction(id: UInt64, completion: @escaping (ApiServiceResult) -> Void)
- func getTransactions(forAccount: String, type: TransactionType, fromHeight: Int64?, completion: @escaping (ApiServiceResult<[Transaction]>) -> Void)
+ func getTransactions(forAccount: String, type: TransactionType, fromHeight: Int64?, offset: Int?, limit: Int?, completion: @escaping (ApiServiceResult<[Transaction]>) -> Void)
// MARK: - Funds
diff --git a/Adamant/Services/ApiService/AdamantApi+Transactions.swift b/Adamant/Services/ApiService/AdamantApi+Transactions.swift
index 5b4818b2f..107ae7c35 100644
--- a/Adamant/Services/ApiService/AdamantApi+Transactions.swift
+++ b/Adamant/Services/ApiService/AdamantApi+Transactions.swift
@@ -44,36 +44,43 @@ extension AdamantApiService {
}
}
- func getTransactions(forAccount account: String, type: TransactionType, fromHeight: Int64?, completion: @escaping (ApiServiceResult<[Transaction]>) -> Void) {
- var queryItems = [URLQueryItem(name: "inId", value: account),
- URLQueryItem(name: "and:type", value: String(type.rawValue))]
-
- if let fromHeight = fromHeight, fromHeight > 0 {
- queryItems.append(URLQueryItem(name: "and:fromHeight", value: String(fromHeight)))
- }
-
- let endpoint: URL
- do {
- endpoint = try buildUrl(path: ApiCommands.Transactions.root, queryItems: queryItems)
- } catch {
- let err = InternalError.endpointBuildFailed.apiServiceErrorWith(error: error)
- completion(.failure(err))
- return
- }
-
- sendRequest(url: endpoint) { (serverResponse: ApiServiceResult>) in
- switch serverResponse {
- case .success(let response):
- if let collection = response.collection {
- completion(.success(collection))
- } else {
- let error = AdamantApiService.translateServerError(response.error)
- completion(.failure(error))
- }
-
- case .failure(let error):
- completion(.failure(.networkError(error: error)))
- }
- }
- }
+ func getTransactions(forAccount account: String, type: TransactionType, fromHeight: Int64?, offset: Int?, limit: Int?, completion: @escaping (ApiServiceResult<[Transaction]>) -> Void) {
+
+ var queryItems = [URLQueryItem(name: "inId", value: account),
+ URLQueryItem(name: "and:type", value: String(type.rawValue))
+ ]
+
+ if let limit = limit { queryItems.append(URLQueryItem(name: "limit", value: String(limit))) }
+
+ if let offset = offset { queryItems.append(URLQueryItem(name: "offset", value: String(offset))) }
+
+ if let fromHeight = fromHeight, fromHeight > 0 {
+ queryItems.append(URLQueryItem(name: "and:fromHeight", value: String(fromHeight)))
+ }
+
+ let endpoint: URL
+ do {
+ endpoint = try buildUrl(path: ApiCommands.Transactions.root, queryItems: queryItems)
+ } catch {
+ let err = InternalError.endpointBuildFailed.apiServiceErrorWith(error: error)
+ completion(.failure(err))
+ return
+ }
+
+ sendRequest(url: endpoint) { (serverResponse: ApiServiceResult>) in
+ switch serverResponse {
+ case .success(let response):
+ if let collection = response.collection {
+ print("Recive \(collection.count) trantaction(s)")
+ completion(.success(collection))
+ } else {
+ let error = AdamantApiService.translateServerError(response.error)
+ completion(.failure(error))
+ }
+
+ case .failure(let error):
+ completion(.failure(.networkError(error: error)))
+ }
+ }
+ }
}
From 88e6de09d099df6d519da870313ece6d5cdb1602 Mon Sep 17 00:00:00 2001
From: Anton B
Date: Tue, 29 May 2018 20:23:45 +0300
Subject: [PATCH 24/34] Load transactions recursivly
---
.../DataProviders/TransfersProvider.swift | 3 +
...antTransfersProvider+backgroundFetch.swift | 2 +-
.../AdamantTransfersProvider.swift | 467 ++++++++++--------
3 files changed, 275 insertions(+), 197 deletions(-)
diff --git a/Adamant/ServiceProtocols/DataProviders/TransfersProvider.swift b/Adamant/ServiceProtocols/DataProviders/TransfersProvider.swift
index b77eba025..9f5c54f58 100644
--- a/Adamant/ServiceProtocols/DataProviders/TransfersProvider.swift
+++ b/Adamant/ServiceProtocols/DataProviders/TransfersProvider.swift
@@ -45,6 +45,9 @@ extension AdamantUserInfoKey {
// New received transactions
static let newTransactions = "transfersNewTransactions"
+
+ /// lastMessageHeight: new lastMessageHeight
+ static let lastTransactionHeight = "adamant.transfersProvider.newTransactions.lastHeight"
private init() {}
}
diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider+backgroundFetch.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider+backgroundFetch.swift
index 0c303d42f..06149276b 100644
--- a/Adamant/Services/DataProviders/AdamantTransfersProvider+backgroundFetch.swift
+++ b/Adamant/Services/DataProviders/AdamantTransfersProvider+backgroundFetch.swift
@@ -34,7 +34,7 @@ extension AdamantTransfersProvider: BackgroundFetchService {
}
}
- apiService.getTransactions(forAccount: address, type: .send, fromHeight: lastHeight) { result in
+ apiService.getTransactions(forAccount: address, type: .send, fromHeight: lastHeight, offset: 0, limit: 100) { result in
switch result {
case .success(let transactions):
let total = transactions.filter({$0.recipientId == address}).count
diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift
index 940b11919..a8b6e721d 100644
--- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift
+++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift
@@ -24,6 +24,7 @@ class AdamantTransfersProvider: TransfersProvider {
private(set) var isInitiallySynced: Bool = false
private(set) var receivedLastHeight: Int64?
private(set) var readedLastHeight: Int64?
+ private let apiTransactions = 100
private let processingQueue = DispatchQueue(label: "im.Adamant.processing.transfers", qos: .utility, attributes: [.concurrent])
private let stateSemaphore = DispatchSemaphore(value: 1)
@@ -129,50 +130,57 @@ extension AdamantTransfersProvider {
self.setState(.failedToUpdate(TransfersProviderError.notLogged), previous: prevState)
return
}
+
+ // MARK: 3. Get transactions
+ let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
+ privateContext.parent = self.stack.container.viewContext
+ let processingGroup = DispatchGroup()
+ let cms = DispatchSemaphore(value: 1)
+ let prevHeight = receivedLastHeight
- apiService.getTransactions(forAccount: address, type: .send, fromHeight: receivedLastHeight) { result in
- switch result {
- case .success(let transactions):
- guard transactions.count > 0 else {
- self.setState(.upToDate, previous: prevState)
- return
- }
-
- self.processingQueue.async {
- self.processRawTransactions(transactions, currentAddress: address) { [weak self] result in
- switch result {
- case .success(let total):
- self?.setState(.upToDate, previous: prevState)
- if total > 0 {
- NotificationCenter.default.post(name: Notification.Name.AdamantTransfersProvider.newTransactions,
- object: self,
- userInfo: [AdamantUserInfoKey.TransfersProvider.newTransactions: total])
- }
-
- if let h = self?.receivedLastHeight {
- self?.readedLastHeight = h
- } else {
- self?.readedLastHeight = 0
- }
-
- if let synced = self?.isInitiallySynced, !synced {
- self?.isInitiallySynced = true
- NotificationCenter.default.post(name: Notification.Name.AdamantTransfersProvider.initialSyncFinished, object: self)
- }
-
- case .error(let error):
- self?.setState(.failedToUpdate(error), previous: prevState)
-
- case .accountNotFound(let key):
- self?.setState(.failedToUpdate(TransfersProviderError.accountNotFound(key)), previous: prevState)
- }
- }
- }
-
- case .failure(let error):
- self.setState(.failedToUpdate(error), previous: prevState)
- }
- }
+ getTransactions(forAccount: address, type: .send, fromHeight: prevHeight, offset: nil, dispatchGroup: processingGroup, context: privateContext, contextMutatingSemaphore: cms)
+
+ // MARK: 4. Check
+ processingGroup.notify(queue: DispatchQueue.global(qos: .utility)) { [weak self] in
+ guard let state = self?.state else {
+ return
+ }
+
+ switch state {
+ case .failedToUpdate(_): // Processing failed
+ break
+
+ default:
+ self?.setState(.upToDate, previous: prevState)
+
+ if prevHeight != self?.receivedLastHeight, let h = self?.receivedLastHeight {
+ NotificationCenter.default.post(name: Notification.Name.AdamantChatsProvider.newUnreadMessages,
+ object: self,
+ userInfo: [AdamantUserInfoKey.TransfersProvider.lastTransactionHeight:h])
+ }
+
+ if let h = self?.receivedLastHeight {
+ self?.readedLastHeight = h
+ } else {
+ self?.readedLastHeight = 0
+ }
+
+ if let store = self?.securedStore {
+ if let h = self?.receivedLastHeight {
+ store.set(String(h), for: StoreKey.chatProvider.receivedLastHeight)
+ }
+
+ if let h = self?.readedLastHeight, h > 0 {
+ store.set(String(h), for: StoreKey.chatProvider.readedLastHeight)
+ }
+ }
+
+ if let synced = self?.isInitiallySynced, !synced {
+ self?.isInitiallySynced = true
+ NotificationCenter.default.post(name: Notification.Name.AdamantTransfersProvider.initialSyncFinished, object: self)
+ }
+ }
+ }
}
func reset() {
@@ -265,158 +273,225 @@ extension AdamantTransfersProvider {
case accountNotFound(address: String)
case error(Error)
}
+
+ /// Get transactions
+ ///
+ /// - Parameters:
+ /// - account: for account
+ /// - height: last transaction height.
+ /// - offset: offset, if greater than 100
+ /// - Returns: ammount of new messages was added
+ private func getTransactions(forAccount account: String,
+ type: TransactionType,
+ fromHeight: Int64?,
+ offset: Int?,
+ dispatchGroup: DispatchGroup,
+ context: NSManagedObjectContext,
+ contextMutatingSemaphore cms: DispatchSemaphore) {
+ // Enter 1
+ dispatchGroup.enter()
+
+ // MARK: 1. Get new transactions
+ apiService.getTransactions(forAccount: account, type: type, fromHeight: fromHeight, offset: offset, limit: self.apiTransactions) { result in
+
+ defer {
+ // Leave 1
+ dispatchGroup.leave()
+ }
+
+ switch result {
+ case .success(let transactions):
+ guard transactions.count > 0 else {
+ return
+ }
+
+ // MARK: 2. Process transactions in background
+ // Enter 2
+ dispatchGroup.enter()
+ self.processingQueue.async {
+ defer {
+ // Leave 2
+ dispatchGroup.leave()
+ }
+
+ self.processRawTransactions(transactions,
+ currentAddress: account,
+ context: context,
+ contextMutatingSemaphore: cms)
+ }
+
+ // MARK: 3. Get more transactions
+ if transactions.count == self.apiTransactions {
+ let newOffset: Int
+ if let offset = offset {
+ newOffset = offset + self.apiTransactions
+ } else {
+ newOffset = self.apiTransactions
+ }
+
+ self.getTransactions(forAccount: account, type: type, fromHeight: fromHeight, offset: newOffset, dispatchGroup: dispatchGroup, context: context, contextMutatingSemaphore: cms)
+ }
+
+ case .failure(let error):
+ self.setState(.failedToUpdate(error), previous: .updating)
+ }
+ }
+ }
+
+ private func processRawTransactions(_ transactions: [Transaction],
+ currentAddress address: String,
+ context: NSManagedObjectContext,
+ contextMutatingSemaphore cms: DispatchSemaphore) {
+ // MARK: 0. Transactions?
+ guard transactions.count > 0 else {
+ return
+ }
+
+ // MARK: 1. Collect all partners
+ var partnerIds: Set = []
+
+ for t in transactions {
+ if t.senderId == address {
+ partnerIds.insert(t.recipientId)
+ } else {
+ partnerIds.insert(t.senderId)
+ }
+ }
+
+ // MARK: 2. Let AccountProvider get all partners from server.
+ let partnersGroup = DispatchGroup()
+ var errors: [ProcessingResult] = []
+ for id in partnerIds {
+ partnersGroup.enter()
+ accountsProvider.getAccount(byAddress: id, completion: { result in
+ defer {
+ partnersGroup.leave()
+ }
+
+ switch result {
+ case .success(_):
+ break
+
+ case .notFound:
+ errors.append(ProcessingResult.accountNotFound(address: id))
+
+ case .serverError(let error):
+ errors.append(ProcessingResult.error(error))
+ }
+ })
+ }
+
+ partnersGroup.wait()
+
+ // MARK: 2.5. If we have any errors - drop processing.
+ if let error = errors.first {
+ print(error)
+ return
+ }
+
+
+ // MARK: 3. Create private context, and process transactions
+ let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
+ context.parent = self.stack.container.viewContext
+
+ var partners: [String:CoreDataAccount] = [:]
+ for id in partnerIds {
+ let request = NSFetchRequest(entityName: CoreDataAccount.entityName)
+ request.predicate = NSPredicate(format: "address == %@", id)
+ request.fetchLimit = 1
+ if let partner = (try? context.fetch(request))?.first {
+ partners[id] = partner
+ }
+ }
+
+ var transfers = [TransferTransaction]()
+ var height: Int64 = 0
+ for t in transactions {
+ let transfer = TransferTransaction(entity: TransferTransaction.entity(), insertInto: context)
+ transfer.amount = t.amount as NSDecimalNumber
+ transfer.date = t.date as NSDate
+ transfer.fee = t.fee as NSDecimalNumber
+ transfer.height = Int64(t.height)
+ transfer.recipientId = t.recipientId
+ transfer.senderId = t.senderId
+ transfer.transactionId = String(t.id)
+ transfer.type = Int16(t.type.rawValue)
+ transfer.blockId = t.blockId
+ transfer.confirmations = t.confirmations
+
+ transfer.isOutgoing = t.senderId == address
+ let partnerId = transfer.isOutgoing ? t.recipientId : t.senderId
+
+ if let partner = partners[partnerId] {
+ transfer.partner = partner
+ transfer.chatroom = partner.chatroom
+ }
+
+ if t.height > height {
+ height = t.height
+ }
+
+ transfers.append(transfer)
+ }
+
+
+ // MARK: 4. Check lastHeight
+ // API returns transactions from lastHeight INCLUDING transaction with height == lastHeight, so +1
+ if height > 0 {
+ let uH = Int64(height + 1)
+
+ if let lastHeight = receivedLastHeight {
+ if lastHeight < uH {
+ self.receivedLastHeight = uH
+ }
+ } else {
+ self.receivedLastHeight = uH
+ }
+ }
+
+ // MARK: 5. Unread transactions
+ if let unreadedHeight = readedLastHeight {
+ let unreadTransactions = transfers.filter { $0.height > unreadedHeight }
+ let chatrooms = Dictionary.init(grouping: unreadTransactions, by: ({ (t: TransferTransaction) -> Chatroom in t.chatroom!}))
+
+ for (chatroom, trs) in chatrooms {
+ chatroom.hasUnreadMessages = true
+ trs.forEach { $0.isUnread = true }
+ }
+
+ transfers.filter({$0.height > unreadedHeight}).forEach({$0.isUnread = true})
+
+ readedLastHeight = self.receivedLastHeight
+ }
+
+ if let h = self.receivedLastHeight {
+ securedStore.set(String(h), for: StoreKey.transfersProvider.receivedLastHeight)
+ }
+
+ if let h = self.readedLastHeight {
+ securedStore.set(String(h), for: StoreKey.transfersProvider.readedLastHeight)
+ }
+
+ // MARK: 6. Dump transactions to viewContext
+ if context.hasChanges {
+ do {
+ try context.save()
+
+ // MARK: 7. Update lastTransactions
+ let viewContextChatrooms = Set(transfers.compactMap { $0.chatroom }).compactMap { self.stack.container.viewContext.object(with: $0.objectID) as? Chatroom }
+ DispatchQueue.main.async {
+ viewContextChatrooms.forEach { $0.updateLastTransaction() }
+ }
+ print(".success \(transfers.count)")
+ // completion(.success(new: transfers.count))
+ } catch {
+ // completion(.error(error))
+ print(error)
+ }
+ } else {
+ // completion(.success(new: 0))
+ print(".success 0")
+ }
+ }
- private func processRawTransactions(_ transactions: [Transaction],
- currentAddress address: String,
- completion: @escaping (ProcessingResult) -> Void) {
- // MARK: 0. Transactions?
- guard transactions.count > 0 else {
- completion(.success(new: 0))
- return
- }
-
- // MARK: 1. Collect all partners
- var partnerIds: Set = []
-
- for t in transactions {
- if t.senderId == address {
- partnerIds.insert(t.recipientId)
- } else {
- partnerIds.insert(t.senderId)
- }
- }
-
- // MARK: 2. Let AccountProvider get all partners from server.
- let partnersGroup = DispatchGroup()
- var errors: [ProcessingResult] = []
- for id in partnerIds {
- partnersGroup.enter()
- accountsProvider.getAccount(byAddress: id, completion: { result in
- defer {
- partnersGroup.leave()
- }
-
- switch result {
- case .success(_):
- break
-
- case .notFound:
- errors.append(ProcessingResult.accountNotFound(address: id))
-
- case .serverError(let error):
- errors.append(ProcessingResult.error(error))
- }
- })
- }
-
- partnersGroup.wait()
-
- // MARK: 2.5. If we have any errors - drop processing.
- if let err = errors.first {
- completion(err)
- return
- }
-
-
- // MARK: 3. Create private context, and process transactions
- let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
- context.parent = self.stack.container.viewContext
-
- var partners: [String:CoreDataAccount] = [:]
- for id in partnerIds {
- let request = NSFetchRequest(entityName: CoreDataAccount.entityName)
- request.predicate = NSPredicate(format: "address == %@", id)
- request.fetchLimit = 1
- if let partner = (try? context.fetch(request))?.first {
- partners[id] = partner
- }
- }
-
- var transfers = [TransferTransaction]()
- var height: Int64 = 0
- for t in transactions {
- let transfer = TransferTransaction(entity: TransferTransaction.entity(), insertInto: context)
- transfer.amount = t.amount as NSDecimalNumber
- transfer.date = t.date as NSDate
- transfer.fee = t.fee as NSDecimalNumber
- transfer.height = Int64(t.height)
- transfer.recipientId = t.recipientId
- transfer.senderId = t.senderId
- transfer.transactionId = String(t.id)
- transfer.type = Int16(t.type.rawValue)
- transfer.blockId = t.blockId
- transfer.confirmations = t.confirmations
-
- transfer.isOutgoing = t.senderId == address
- let partnerId = transfer.isOutgoing ? t.recipientId : t.senderId
-
- if let partner = partners[partnerId] {
- transfer.partner = partner
- transfer.chatroom = partner.chatroom
- }
-
- if t.height > height {
- height = t.height
- }
-
- transfers.append(transfer)
- }
-
-
- // MARK: 4. Check lastHeight
- // API returns transactions from lastHeight INCLUDING transaction with height == lastHeight, so +1
- if height > 0 {
- let uH = Int64(height + 1)
-
- if let lastHeight = receivedLastHeight {
- if lastHeight < uH {
- self.receivedLastHeight = uH
- }
- } else {
- self.receivedLastHeight = uH
- }
- }
-
- // MARK: 5. Unread transactions
- if let unreadedHeight = readedLastHeight {
- let unreadTransactions = transfers.filter { $0.height > unreadedHeight }
- let chatrooms = Dictionary.init(grouping: unreadTransactions, by: ({ (t: TransferTransaction) -> Chatroom in t.chatroom!}))
-
- for (chatroom, trs) in chatrooms {
- chatroom.hasUnreadMessages = true
- trs.forEach { $0.isUnread = true }
- }
-
- transfers.filter({$0.height > unreadedHeight}).forEach({$0.isUnread = true})
-
- readedLastHeight = self.receivedLastHeight
- }
-
- if let h = self.receivedLastHeight {
- securedStore.set(String(h), for: StoreKey.transfersProvider.receivedLastHeight)
- }
-
- if let h = self.readedLastHeight {
- securedStore.set(String(h), for: StoreKey.transfersProvider.readedLastHeight)
- }
-
- // MARK: 6. Dump transactions to viewContext
- if context.hasChanges {
- do {
- try context.save()
-
- // MARK: 7. Update lastTransactions
- let viewContextChatrooms = Set(transfers.compactMap { $0.chatroom }).compactMap { self.stack.container.viewContext.object(with: $0.objectID) as? Chatroom }
- DispatchQueue.main.async {
- viewContextChatrooms.forEach { $0.updateLastTransaction() }
- }
-
- completion(.success(new: transfers.count))
- } catch {
- completion(.error(error))
- }
- } else {
- completion(.success(new: 0))
- }
- }
}
From ec97d965b44f4d51a75c71e7061af9fdac7eaf42 Mon Sep 17 00:00:00 2001
From: Anton B
Date: Tue, 29 May 2018 20:25:22 +0300
Subject: [PATCH 25/34] Update list of servers
---
Adamant/AppDelegate.swift | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift
index da2db9ad1..5c1ce8c64 100644
--- a/Adamant/AppDelegate.swift
+++ b/Adamant/AppDelegate.swift
@@ -37,9 +37,8 @@ struct AdamantResources {
static let servers = [
"https://endless.adamant.im",
-// "https://clown.adamant.im",
-// "https://lake.adamant.im",
- "https://fake.adamant.im"
+ "https://clown.adamant.im",
+ "https://lake.adamant.im"
]
private init() {}
From 49767541bd019980d76ddce1c9fd314509224f1b Mon Sep 17 00:00:00 2001
From: Anton B
Date: Thu, 31 May 2018 15:15:29 +0300
Subject: [PATCH 26/34] Add new error alert
Add New error alert
Add No internet connection notification
---
Adamant/AppDelegate.swift | 11 ++-
.../Icons/error.imageset/Contents.json | 23 +++++
.../Icons/error.imageset/error.png | Bin 0 -> 1949 bytes
.../Icons/error.imageset/error@2x.png | Bin 0 -> 4057 bytes
.../Icons/error.imageset/error@3x.png | Bin 0 -> 6365 bytes
.../Assets/l18n/en.lproj/Localizable.strings | 12 +++
.../Assets/l18n/ru.lproj/Localizable.strings | Bin 44106 -> 45170 bytes
Adamant/Helpers/GlobalConstants.swift | 3 +
Adamant/Helpers/String+localized.swift | 7 ++
Adamant/Helpers/UIColor+adamant.swift | 30 +++++++
Adamant/ServiceProtocols/DialogService.swift | 2 +
Adamant/Services/AdamantDialogService.swift | 79 +++++++++++++++++-
Adamant/Utilities/AdamantUtilities.swift | 13 +++
Podfile | 1 +
14 files changed, 177 insertions(+), 4 deletions(-)
create mode 100644 Adamant/Assets/Assets.xcassets/Icons/error.imageset/Contents.json
create mode 100644 Adamant/Assets/Assets.xcassets/Icons/error.imageset/error.png
create mode 100644 Adamant/Assets/Assets.xcassets/Icons/error.imageset/error@2x.png
create mode 100644 Adamant/Assets/Assets.xcassets/Icons/error.imageset/error@3x.png
create mode 100644 Adamant/Helpers/UIColor+adamant.swift
diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift
index da2db9ad1..14b0541a7 100644
--- a/Adamant/AppDelegate.swift
+++ b/Adamant/AppDelegate.swift
@@ -37,9 +37,8 @@ struct AdamantResources {
static let servers = [
"https://endless.adamant.im",
-// "https://clown.adamant.im",
-// "https://lake.adamant.im",
- "https://fake.adamant.im"
+ "https://clown.adamant.im",
+ "https://lake.adamant.im"
]
private init() {}
@@ -56,6 +55,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: Dependencies
var accountService: AccountService!
var notificationService: NotificationsService!
+ var dialogService: DialogService!
// MARK: - Lifecycle
@@ -65,6 +65,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
container.registerAdamantServices()
accountService = container.resolve(AccountService.self)
notificationService = container.resolve(NotificationsService.self)
+ dialogService = container.resolve(DialogService.self)
// MARK: 2. Init UI
window = UIWindow(frame: UIScreen.main.bounds)
@@ -116,9 +117,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
switch reachability.connection {
case .cellular, .wifi:
+ dialogService.dissmisNoConnectionNotification()
break
case .none:
+ dialogService.showNoConnectionNotification()
repeater.pauseAll()
}
@@ -130,9 +133,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
switch connection {
case .cellular, .wifi:
+ self?.dialogService.dissmisNoConnectionNotification()
repeater.resumeAll()
case .none:
+ self?.dialogService.showNoConnectionNotification()
repeater.pauseAll()
}
}
diff --git a/Adamant/Assets/Assets.xcassets/Icons/error.imageset/Contents.json b/Adamant/Assets/Assets.xcassets/Icons/error.imageset/Contents.json
new file mode 100644
index 000000000..3e5932ea6
--- /dev/null
+++ b/Adamant/Assets/Assets.xcassets/Icons/error.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "error.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "error@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "error@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Adamant/Assets/Assets.xcassets/Icons/error.imageset/error.png b/Adamant/Assets/Assets.xcassets/Icons/error.imageset/error.png
new file mode 100644
index 0000000000000000000000000000000000000000..2f1e3d747c3aa00e863199349a90a540cba00635
GIT binary patch
literal 1949
zcmV;O2V(e%P)Ik4pMwY{;aaH@>ijyDy0-)b93{8(b3UrjIR;T
z=|KQAh&K;bR8;(;=ebq)x+O?2r>UuF#@N`{GXQ(j44l?p_&X0DK0IIF->rJe00Qp8
z$;rt#Fm$@gSit#d^E_S@pau`C`Xxe73BdaL`aF!-OfIrv%maWdZES2@VWVxrO_n#Ytz?R?K&BHp5#+we
zmxGUjf`V+$y}cMEM}yNO-W$Dk?b_v*mXndaLNH4jb1N|k*
zv^zxocU61n+X?ZCBuKYtV~ZtcmZLO0JUokavmYV3W`Y266VHCMd-v{!DE?`Jfv-6l
z4#L|IMGt*Ko5e?EXJ_YCN~?j;cQw=sRriOUU=iv17;bE!tSJk^+cQ@8ICz
zG9Fo7|ao;>3v@!m523FK3@slCVn|
znP}jZ1E2|>$3RqExpL(mD@7_^!X9L5`Z?6sA+Ss(G)xD8gltR0b$tncE;PyUQYizv
zL3GD!sOKjPkn>57gIbKs&Y+CTuk+A%L#HzZ?L_h%Xo!8IcQcOzP<9khLdM+yFkNp;loa9v0n+-Zl~f*xK4UA4Sz>
z2AI)kE8BY(hAq+anR2u6t^l{ad5G?=uCAq;T)jxsx@~|w|79>m-
zNPv)whfSj&l_*Vj)zPCzZ6E%&03z7SXs;yiQ7N~e&!E3y2ZUyKgNYH%pj2STYP-)H
zwg8?vb0!0W4xq{Q$c3%KAbC|=O#hf@L;z|a!%c0K^vNdIT)%$Z&gK;V9F&pj?(Qx@
z=!>>2Fg6MRa?$bQ$0G+iyQ+^UW-fFIsuNDw@*G|)L5}iCGAuN%2iw}(=CC1rK!><(
z!$NgsItJyklSW3ozrQ~dnoQATo;F0h*it(%Ft7kQF*8G$(aF+i?Y1Dd^i|QF68RPb
zNHfWiqr&g^%h4bJlfLgEcLiR2EqZ2{*`)+1hTXb#YZ*fJn{`Z(7C_Fta378hSx1sB
zM?c)&qTC~wZJF|_HcEh}PMyl&BKsa4?SxV)`5`(${CF_>NRagyS(BEXEtts7W%;}#
zmu^)v(-D*r;U4xB0#4J>_Cc&t0(}&KZOCh<^iXC~+)Jr6%tDr)HO{@VEI&U#SW;3l
zs%M%$S>X0;8p_7v1~}0vv7}tDmQhMwZtS7bg+J+dv6cwuBC^`_@(fcH@A+~04h)bR
zgKG(o^^w2TatEbr2r;sVHNIVJ*BfjA$kr=|vaCI~(lB6x$lSDR*RGA^4QXj*F0FaTWp|M*(f{X*=JLK)-RqbcKC&^Rg<>h~5W@a9OUPm4xJ5CJX
zLb07VHRkAv0Z2eyNW*f++B5-x=_0rAs&-Q^v#hMF2VLi&U)J-It&8n|ToZk4L;%9{
zvRFSwxSPN3^rNsHvoO&!$?*vFQ`Adzt
zj5GYRLP%lP$D#3~Ia;=e!z}~u
z1h8mZS9_T@!_kzx?0dwE2hRa=;w}Rw|L`|K{>Dp`A^!;X#oY!F$JOQK<^3d6bgHnh
zu$uCHKz9D$fr3NA!%uQ@a%xITONU}2Z~3o3Ub~7x{|G=IrJODU@LvXvjhN^eA^(V-
j-;*!$>#-b5A-n$uQ{Yg=qU`;!00000NkvXXu0mjf^0T^m
literal 0
HcmV?d00001
diff --git a/Adamant/Assets/Assets.xcassets/Icons/error.imageset/error@2x.png b/Adamant/Assets/Assets.xcassets/Icons/error.imageset/error@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5c301a37ed512f31e52180a433e299a3bb77b56
GIT binary patch
literal 4057
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
literal 0
HcmV?d00001
diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings
index 40b09acb0..60e16067e 100644
--- a/Adamant/Assets/l18n/en.lproj/Localizable.strings
+++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings
@@ -131,6 +131,12 @@
/* Chat: 'Sent funds' buble 'Tap for details' tip */
"ChatScene.tapForDetails" = "Tap for details";
+/* Error messge title for support email */
+"Error.Mail.Title" = "I have error in my iOS app";
+
+/* Error messge body for support email */
+"Error.Mail.Body" = "Hello,\nI have this error:\n\n%@\n\nMy device info:\n%2";
+
/* Login: Notify user, that he disabled camera in settings, and need to authorize application. */
"LoginScene.Error.AuthorizeCamera" = "You need to authorize Adamant to use device's Camera";
@@ -332,6 +338,12 @@
/* Shared alert 'Generate QR' button. Used to generate QR codes with addresses and passphrases. Used with sharing and saving, anywhere. */
"Shared.GenerateQRCode" = "Generate QR code";
+/* Shared alert notification: title for no internet connection message. */
+"Shared.NoInternet.Title" = "No internet connection";
+
+/* Shared alert notification: body message for no internet connection. */
+"Shared.NoInternet.Body" = "Please check your internet connection and try again";
+
/* Shared alert 'Ok' button. Used anywhere */
"Shared.Ok" = "Ok";
diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings
index 0e3f9da069be95ce9b714a3dc724069dd6de9fde..f7c7c90e0f2064b81990c03658a446b214484320 100644
GIT binary patch
delta 755
zcmbVKyG{a85FN=FBNiBlf{(<_8XHu^4NH*60!u7>AThQyibzOsHG%~d9U-y2|6xq9
z@)K-~iG`igSy}i6o_i%h36;5-o!OalXU?6O$5+?ayX(%%mbd8&=K`Y9IhCo7Q-Ri~
zK~+di*=K!3ov!H;N8%g6lyT=6xEkyrZIGLE8e|Mn{wFF99e-> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
+ case 6: // RGB (24-bit)
+ (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
+ case 8: // ARGB (32-bit)
+ (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
+ default:
+ (a, r, g, b) = (255, 0, 0, 0)
+ }
+ self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
+ }
+}
diff --git a/Adamant/ServiceProtocols/DialogService.swift b/Adamant/ServiceProtocols/DialogService.swift
index e915d3896..169308fe4 100644
--- a/Adamant/ServiceProtocols/DialogService.swift
+++ b/Adamant/ServiceProtocols/DialogService.swift
@@ -88,6 +88,8 @@ protocol DialogService: class {
func dismissProgress()
func showSuccess(withMessage: String)
func showError(withMessage: String)
+ func showNoConnectionNotification()
+ func dissmisNoConnectionNotification()
// MARK: - Notifications
func showNotification(title: String?, message: String?, image: UIImage?, tapHandler: (() -> Void)?)
diff --git a/Adamant/Services/AdamantDialogService.swift b/Adamant/Services/AdamantDialogService.swift
index 4dbf20e25..5e80d5598 100644
--- a/Adamant/Services/AdamantDialogService.swift
+++ b/Adamant/Services/AdamantDialogService.swift
@@ -8,10 +8,14 @@
import UIKit
import FTIndicator
+import PMAlertController
+import MessageUI
class AdamantDialogService: DialogService {
// MARK: Dependencies
var router: Router!
+
+ var mailDelegate = MailDelegate()
// Configure notifications
init() {
@@ -88,8 +92,73 @@ extension AdamantDialogService {
}
func showError(withMessage message: String) {
- FTIndicator.showError(withMessage: message)
+// FTIndicator.showError(withMessage: message)
+
+ let alertVC = PMAlertController(title: String.adamantLocalized.alert.error, description: message, image: #imageLiteral(resourceName: "error"), style: .alert)
+
+ alertVC.gravityDismissAnimation = false
+ alertVC.alertTitle.textColor = UIColor.adamantPrimary
+ alertVC.alertDescription.textColor = .adamantSecondary
+ alertVC.alertTitle.font = UIFont.adamantPrimary(size: 20)
+ alertVC.alertDescription.font = UIFont.adamantPrimaryLight(size: 14)
+ alertVC.headerViewHeightConstraint.constant = 50
+
+ let supportBtn = PMAlertAction(title: String.adamantSupportEmail, style: .default, action: { () -> Void in
+ print("Support")
+
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
+ print("Send mail")
+
+ let mailVC = MFMailComposeViewController()
+ mailVC.mailComposeDelegate = self?.mailDelegate
+ mailVC.setToRecipients([String.adamantSupportEmail])
+ mailVC.setSubject(String.adamantLocalized.alert.emailErrorMessageTitle)
+
+ let systemVersion = UIDevice.current.systemVersion
+ let model = AdamantUtilities.deviceModelCode
+ let deviceInfo = "Model: \(model)\n" + "iOS: \(systemVersion)\n" + "App version: \(AdamantUtilities.applicationVersion)"
+
+ let body = String(format: String.adamantLocalized.alert.emailErrorMessageBody, message, deviceInfo)
+
+ mailVC.setMessageBody(body, isHTML: false)
+
+ self?.present(mailVC, animated: true, completion: nil)
+ }
+ })
+
+ supportBtn.titleLabel?.font = UIFont.adamantPrimary(size: 16)
+ supportBtn.setTitleColor(UIColor(hex: "#00B6FF"), for: .normal)
+ supportBtn.separator.isHidden = true
+
+ alertVC.addAction(supportBtn)
+
+ let okBtn = PMAlertAction(title: String.adamantLocalized.alert.ok, style: .default, action: { () in
+ print("Capture action OK")
+ })
+
+ okBtn.titleLabel?.font = UIFont.adamantPrimary(size: 16)
+ okBtn.setTitleColor(UIColor.white, for: .normal)
+ okBtn.backgroundColor = UIColor.adamantSecondary
+ alertVC.addAction(okBtn)
+
+ alertVC.alertActionStackView.axis = .vertical
+ alertVC.alertActionStackView.spacing = 0
+ alertVC.alertActionStackViewHeightConstraint.constant = 100
+
+ self.present(alertVC, animated: true, completion: nil)
}
+
+ func showNoConnectionNotification() {
+ FTIndicator.showNotification(with: #imageLiteral(resourceName: "error"), title: String.adamantLocalized.alert.noInternetNotificationTitle, message: String.adamantLocalized.alert.noInternetNotificationBoby, autoDismiss: false, tapHandler: {
+ //
+ }) {
+ //
+ }
+ }
+
+ func dissmisNoConnectionNotification() {
+ FTIndicator.dismissNotification()
+ }
}
@@ -191,3 +260,11 @@ extension AdamantDialogService {
}
}
}
+
+class MailDelegate: NSObject, MFMailComposeViewControllerDelegate {
+
+ func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
+ controller.dismiss(animated: true, completion: nil)
+ }
+
+}
diff --git a/Adamant/Utilities/AdamantUtilities.swift b/Adamant/Utilities/AdamantUtilities.swift
index f6b081682..650ade09d 100644
--- a/Adamant/Utilities/AdamantUtilities.swift
+++ b/Adamant/Utilities/AdamantUtilities.swift
@@ -19,6 +19,19 @@ class AdamantUtilities {
return ""
}()
+
+ // MARK: Device model
+ static var deviceModelCode: String {
+ var systemInfo = utsname()
+ uname(&systemInfo)
+ let modelCode = withUnsafePointer(to: &systemInfo.machine) {
+ $0.withMemoryRebound(to: CChar.self, capacity: 1) {
+ ptr in String.init(validatingUTF8: ptr)
+
+ }
+ }
+ return modelCode ?? "Unknown"
+ }
private init() { }
}
diff --git a/Podfile b/Podfile
index afb89baf9..8da19a00b 100644
--- a/Podfile
+++ b/Podfile
@@ -18,6 +18,7 @@ target 'Adamant' do
pod 'Eureka' # Forms
pod 'MessageKit' # Chat UI
pod 'MyLittlePinpad' # Pinpad
+ pod 'PMAlertController' # Custom alert controller
# QR
pod 'EFQRCode' # QR generator
From 79c976eb6bf0090f5646670b40a404850f864bef Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Thu, 31 May 2018 18:16:20 +0300
Subject: [PATCH 27/34] Project, pod and merge updates and fixes.
---
Adamant.xcodeproj/project.pbxproj | 14 ++++++++++----
.../xcschemes/Adamant.Dev.BackgroundFetch.xcscheme | 2 +-
.../xcshareddata/xcschemes/Adamant.Dev.xcscheme | 2 +-
.../xcschemes/Adamant.Release.xcscheme | 2 +-
.../xcshareddata/xcschemes/Adamant.Test.xcscheme | 2 +-
Adamant/AppDelegate.swift | 3 ++-
...able+Clamped.swift => Comparable+clamped.swift} | 2 +-
Adamant/Helpers/GlobalConstants.swift | 4 ----
.../{UIColor+adamant.swift => UIColor+hex.swift} | 2 +-
Adamant/Services/AdamantDialogService.swift | 4 ++--
Podfile.lock | 6 +++++-
11 files changed, 25 insertions(+), 18 deletions(-)
rename Adamant/Helpers/{Comparable+Clamped.swift => Comparable+clamped.swift} (91%)
rename Adamant/Helpers/{UIColor+adamant.swift => UIColor+hex.swift} (97%)
diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj
index 7b52b988c..308aa1b11 100644
--- a/Adamant.xcodeproj/project.pbxproj
+++ b/Adamant.xcodeproj/project.pbxproj
@@ -50,6 +50,7 @@
E9256F5F2034C21100DE86E9 /* String+localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9256F5E2034C21100DE86E9 /* String+localized.swift */; };
E9256F6D20357B1700DE86E9 /* LoginHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = E9256F6C20357B1700DE86E9 /* LoginHeader.xib */; };
E9256F762039A9A200DE86E9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E9256F752039A9A200DE86E9 /* LaunchScreen.storyboard */; };
+ E927171E20C04614002BB9A6 /* UIColor+hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = E927171D20C04613002BB9A6 /* UIColor+hex.swift */; };
E9393FA82055C92700EE6F30 /* Decimal+adamant.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9393FA72055C92700EE6F30 /* Decimal+adamant.swift */; };
E9393FAA2055D03300EE6F30 /* AdamantMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9393FA92055D03300EE6F30 /* AdamantMessage.swift */; };
E93B0D742028B21400126346 /* ChatsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93B0D732028B21400126346 /* ChatsProvider.swift */; };
@@ -159,7 +160,7 @@
E9EC341F200524CA00C0E546 /* Roboto_700_normal.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E91947A7200010DF001362F8 /* Roboto_700_normal.ttf */; };
E9EC342120052ABB00C0E546 /* TransferViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9EC342020052ABB00C0E546 /* TransferViewController.swift */; };
E9EC344720066D4A00C0E546 /* AddressValidationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9EC344620066D4A00C0E546 /* AddressValidationTests.swift */; };
- E9FAE5DA203DBFEF008D3A6B /* Comparable+Clamped.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FAE5D9203DBFEF008D3A6B /* Comparable+Clamped.swift */; };
+ E9FAE5DA203DBFEF008D3A6B /* Comparable+clamped.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FAE5D9203DBFEF008D3A6B /* Comparable+clamped.swift */; };
E9FAE5E2203ED1AE008D3A6B /* ShareQrViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FAE5E0203ED1AE008D3A6B /* ShareQrViewController.swift */; };
E9FAE5E3203ED1AE008D3A6B /* ShareQrViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E9FAE5E1203ED1AE008D3A6B /* ShareQrViewController.xib */; };
/* End PBXBuildFile section */
@@ -236,6 +237,7 @@
E9256F5E2034C21100DE86E9 /* String+localized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+localized.swift"; sourceTree = ""; };
E9256F6C20357B1700DE86E9 /* LoginHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoginHeader.xib; sourceTree = ""; };
E9256F752039A9A200DE86E9 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; };
+ E927171D20C04613002BB9A6 /* UIColor+hex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+hex.swift"; sourceTree = ""; };
E9393FA72055C92700EE6F30 /* Decimal+adamant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Decimal+adamant.swift"; sourceTree = ""; };
E9393FA92055D03300EE6F30 /* AdamantMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantMessage.swift; sourceTree = ""; };
E93B0D732028B21400126346 /* ChatsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsProvider.swift; sourceTree = ""; };
@@ -339,7 +341,7 @@
E9EC344420066D4A00C0E546 /* AdamantTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AdamantTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
E9EC344620066D4A00C0E546 /* AddressValidationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressValidationTests.swift; sourceTree = ""; };
E9EC344820066D4A00C0E546 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- E9FAE5D9203DBFEF008D3A6B /* Comparable+Clamped.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Comparable+Clamped.swift"; sourceTree = ""; };
+ E9FAE5D9203DBFEF008D3A6B /* Comparable+clamped.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Comparable+clamped.swift"; sourceTree = ""; };
E9FAE5E0203ED1AE008D3A6B /* ShareQrViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareQrViewController.swift; sourceTree = ""; };
E9FAE5E1203ED1AE008D3A6B /* ShareQrViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShareQrViewController.xib; sourceTree = ""; };
/* End PBXFileReference section */
@@ -483,7 +485,7 @@
children = (
E91947B12000246A001362F8 /* AdamantError.swift */,
E9061B96207501E40011F104 /* AdamantUserInfoKey.swift */,
- E9FAE5D9203DBFEF008D3A6B /* Comparable+Clamped.swift */,
+ E9FAE5D9203DBFEF008D3A6B /* Comparable+clamped.swift */,
E9393FA72055C92700EE6F30 /* Decimal+adamant.swift */,
E95F856420067A030070534A /* GlobalConstants.swift */,
E95F856A200789450070534A /* JSModels.swift */,
@@ -491,6 +493,7 @@
E9147B5E20500E9300145913 /* MyLittlePinpad+adamant.swift */,
E9147B6220505C7500145913 /* QRCodeReader+adamant.swift */,
E9256F5E2034C21100DE86E9 /* String+localized.swift */,
+ E927171D20C04613002BB9A6 /* UIColor+hex.swift */,
);
path = Helpers;
sourceTree = "";
@@ -908,6 +911,7 @@
"${BUILT_PRODUCTS_DIR}/KeychainAccess/KeychainAccess.framework",
"${BUILT_PRODUCTS_DIR}/MessageKit/MessageKit.framework",
"${BUILT_PRODUCTS_DIR}/MyLittlePinpad/MyLittlePinpad.framework",
+ "${BUILT_PRODUCTS_DIR}/PMAlertController/PMAlertController.framework",
"${BUILT_PRODUCTS_DIR}/QRCodeReader.swift/QRCodeReader.framework",
"${BUILT_PRODUCTS_DIR}/RNCryptor/RNCryptor.framework",
"${BUILT_PRODUCTS_DIR}/ReachabilitySwift/Reachability.framework",
@@ -925,6 +929,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KeychainAccess.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessageKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MyLittlePinpad.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PMAlertController.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/QRCodeReader.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNCryptor.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework",
@@ -1012,6 +1017,7 @@
E95F85802008C8D70070534A /* ChatsRoutes.swift in Sources */,
E9942B87203D9E5100C163AF /* EurekaQRRow.swift in Sources */,
E93EFE13200D1156000BB482 /* ChatViewController.swift in Sources */,
+ E927171E20C04614002BB9A6 /* UIColor+hex.swift in Sources */,
E9B3D39E201F99F40019EB36 /* DataProvider.swift in Sources */,
E9E7CDC02003AF6D00DFC4DB /* AdamantCellFactory.swift in Sources */,
E91A063A209F05AA0018A102 /* NotificationsViewController.swift in Sources */,
@@ -1038,7 +1044,7 @@
E965A53020B594120041A3EA /* AdamantApi+States.swift in Sources */,
E9150B982066DA210065A985 /* CoreDataAccount+CoreDataProperties.swift in Sources */,
E9B3D3A1201FA26B0019EB36 /* AdamantAccountsProvider.swift in Sources */,
- E9FAE5DA203DBFEF008D3A6B /* Comparable+Clamped.swift in Sources */,
+ E9FAE5DA203DBFEF008D3A6B /* Comparable+clamped.swift in Sources */,
E9150B972066DA210065A985 /* CoreDataAccount+CoreDataClass.swift in Sources */,
E95F857A2007F0260070534A /* ServerResponse.swift in Sources */,
E9A174B32057EC47003667CD /* BackgroundFetchService.swift in Sources */,
diff --git a/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Dev.BackgroundFetch.xcscheme b/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Dev.BackgroundFetch.xcscheme
index 15fd92192..ef6bec5a9 100644
--- a/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Dev.BackgroundFetch.xcscheme
+++ b/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Dev.BackgroundFetch.xcscheme
@@ -1,6 +1,6 @@
Void in
+ let supportBtn = PMAlertAction(title: AdamantResources.iosAppSupportEmail, style: .default, action: { () -> Void in
print("Support")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
@@ -111,7 +111,7 @@ extension AdamantDialogService {
let mailVC = MFMailComposeViewController()
mailVC.mailComposeDelegate = self?.mailDelegate
- mailVC.setToRecipients([String.adamantSupportEmail])
+ mailVC.setToRecipients([AdamantResources.iosAppSupportEmail])
mailVC.setSubject(String.adamantLocalized.alert.emailErrorMessageTitle)
let systemVersion = UIDevice.current.systemVersion
diff --git a/Podfile.lock b/Podfile.lock
index 2ae0fffe7..08bec33cd 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -15,6 +15,7 @@ PODS:
- KeychainAccess (3.1.1)
- MessageKit (0.13.5)
- MyLittlePinpad (0.2.4)
+ - PMAlertController (3.4.0)
- QRCodeReader.swift (8.2.0)
- ReachabilitySwift (4.1.0)
- RNCryptor (5.0.3)
@@ -31,6 +32,7 @@ DEPENDENCIES:
- KeychainAccess
- MessageKit
- MyLittlePinpad
+ - PMAlertController
- QRCodeReader.swift
- ReachabilitySwift
- RNCryptor
@@ -48,6 +50,7 @@ SPEC REPOS:
- KeychainAccess
- MessageKit
- MyLittlePinpad
+ - PMAlertController
- QRCodeReader.swift
- ReachabilitySwift
- RNCryptor
@@ -64,11 +67,12 @@ SPEC CHECKSUMS:
KeychainAccess: 7bd430028059754a3debab3cfc0bd1fc7fb85df3
MessageKit: c4bd50e285a0f0761bc23a2e982aa7840873c4f5
MyLittlePinpad: dc5f8a7fc13a4ad6fc9dc8d3359d91f1b5b1c7e8
+ PMAlertController: efb781925d741d50e0200018a00c53cecb8b4910
QRCodeReader.swift: 003eb32f18a5a675b936ec82ba0ff368cddbff45
ReachabilitySwift: 6849231cd4e06559f3b9ef4a97a0a0f96d41e09f
RNCryptor: c93d19029dcf7ff160aca0f24d6c9e7b0d82f664
Swinject: a1364b0f66c2736bb03c1c7cab54809e16df25da
-PODFILE CHECKSUM: 12f69d43b1b41b997e7c698aa56457f71d9ade99
+PODFILE CHECKSUM: d5efe36624e504f34934e8b20f4138c7db823908
COCOAPODS: 1.5.0
From 6bc2874054db473dbb397082cd325a5907d64468 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Fri, 1 Jun 2018 12:21:38 +0300
Subject: [PATCH 28/34] dialogService.showWarning dialogService not dismissing
progress fixed
---
Adamant/ServiceProtocols/DialogService.swift | 1 +
Adamant/Services/AdamantDialogService.swift | 30 ++++++++++---------
.../Account/TransferViewController.swift | 6 ++--
.../Stories/Chats/NewChatViewController.swift | 10 +++----
.../Login/LoginViewController+QR.swift | 6 ++--
.../Stories/Login/LoginViewController.swift | 2 +-
6 files changed, 29 insertions(+), 26 deletions(-)
diff --git a/Adamant/ServiceProtocols/DialogService.swift b/Adamant/ServiceProtocols/DialogService.swift
index 169308fe4..07c8b8dae 100644
--- a/Adamant/ServiceProtocols/DialogService.swift
+++ b/Adamant/ServiceProtocols/DialogService.swift
@@ -87,6 +87,7 @@ protocol DialogService: class {
func showProgress(withMessage: String?, userInteractionEnable: Bool)
func dismissProgress()
func showSuccess(withMessage: String)
+ func showWarning(withMessage: String)
func showError(withMessage: String)
func showNoConnectionNotification()
func dissmisNoConnectionNotification()
diff --git a/Adamant/Services/AdamantDialogService.swift b/Adamant/Services/AdamantDialogService.swift
index 5aa8261ee..2fa97a3f2 100644
--- a/Adamant/Services/AdamantDialogService.swift
+++ b/Adamant/Services/AdamantDialogService.swift
@@ -91,10 +91,20 @@ extension AdamantDialogService {
FTIndicator.showSuccess(withMessage: message)
}
+ func showWarning(withMessage message: String) {
+ FTIndicator.showError(withMessage: message)
+ }
+
func showError(withMessage message: String) {
-// FTIndicator.showError(withMessage: message)
-
- let alertVC = PMAlertController(title: String.adamantLocalized.alert.error, description: message, image: #imageLiteral(resourceName: "error"), style: .alert)
+ if Thread.isMainThread {
+ FTIndicator.dismissProgress()
+ } else {
+ DispatchQueue.main.sync {
+ FTIndicator.dismissProgress()
+ }
+ }
+
+ let alertVC = PMAlertController(title: String.adamantLocalized.alert.error, description: message, image: #imageLiteral(resourceName: "error"), style: .alert)
alertVC.gravityDismissAnimation = false
alertVC.alertTitle.textColor = UIColor.adamantPrimary
@@ -103,9 +113,7 @@ extension AdamantDialogService {
alertVC.alertDescription.font = UIFont.adamantPrimaryLight(size: 14)
alertVC.headerViewHeightConstraint.constant = 50
- let supportBtn = PMAlertAction(title: AdamantResources.iosAppSupportEmail, style: .default, action: { () -> Void in
- print("Support")
-
+ let supportBtn = PMAlertAction(title: AdamantResources.iosAppSupportEmail, style: .default) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
print("Send mail")
@@ -132,9 +140,7 @@ extension AdamantDialogService {
alertVC.addAction(supportBtn)
- let okBtn = PMAlertAction(title: String.adamantLocalized.alert.ok, style: .default, action: { () in
- print("Capture action OK")
- })
+ let okBtn = PMAlertAction(title: String.adamantLocalized.alert.ok, style: .default)
okBtn.titleLabel?.font = UIFont.adamantPrimary(size: 16)
okBtn.setTitleColor(UIColor.white, for: .normal)
@@ -149,11 +155,7 @@ extension AdamantDialogService {
}
func showNoConnectionNotification() {
- FTIndicator.showNotification(with: #imageLiteral(resourceName: "error"), title: String.adamantLocalized.alert.noInternetNotificationTitle, message: String.adamantLocalized.alert.noInternetNotificationBoby, autoDismiss: false, tapHandler: {
- //
- }) {
- //
- }
+ FTIndicator.showNotification(with: #imageLiteral(resourceName: "error"), title: String.adamantLocalized.alert.noInternetNotificationTitle, message: String.adamantLocalized.alert.noInternetNotificationBoby, autoDismiss: false, tapHandler: nil, completion: nil)
}
func dissmisNoConnectionNotification() {
diff --git a/Adamant/Stories/Account/TransferViewController.swift b/Adamant/Stories/Account/TransferViewController.swift
index d10661728..562f915fc 100644
--- a/Adamant/Stories/Account/TransferViewController.swift
+++ b/Adamant/Stories/Account/TransferViewController.swift
@@ -275,17 +275,17 @@ class TransferViewController: FormViewController {
let amount = Decimal(raw)
guard AdamantUtilities.validateAmount(amount: amount) else {
- dialogService.showError(withMessage: String.adamantLocalized.transfer.amountZeroError)
+ dialogService.showWarning(withMessage: String.adamantLocalized.transfer.amountZeroError)
return
}
guard AdamantUtilities.validateAdamantAddress(address: recipient) else {
- dialogService.showError(withMessage: String.adamantLocalized.transfer.addressValidationError)
+ dialogService.showWarning(withMessage: String.adamantLocalized.transfer.addressValidationError)
return
}
guard amount <= Decimal(maxToTransfer) else {
- dialogService.showError(withMessage: String.adamantLocalized.transfer.amountTooHigh)
+ dialogService.showWarning(withMessage: String.adamantLocalized.transfer.amountTooHigh)
return
}
diff --git a/Adamant/Stories/Chats/NewChatViewController.swift b/Adamant/Stories/Chats/NewChatViewController.swift
index fff27c196..bb4de5340 100644
--- a/Adamant/Stories/Chats/NewChatViewController.swift
+++ b/Adamant/Stories/Chats/NewChatViewController.swift
@@ -198,7 +198,7 @@ class NewChatViewController: FormViewController {
}
case .notFound:
- self.dialogService.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.newChat.addressNotFoundFormat, address))
+ self.dialogService.showWarning(withMessage: String.localizedStringWithFormat(String.adamantLocalized.newChat.addressNotFoundFormat, address))
case .serverError(let error):
self.dialogService.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.newChat.serverErrorFormat, String(describing: error)))
@@ -306,7 +306,7 @@ extension NewChatViewController {
extension NewChatViewController: QRCodeReaderViewControllerDelegate {
func reader(_ reader: QRCodeReaderViewController, didScanResult result: QRCodeReaderResult) {
guard let uri = AdamantUriTools.decode(uri: result.value) else {
- dialogService.showError(withMessage: String.adamantLocalized.newChat.wrongQrError)
+ dialogService.showWarning(withMessage: String.adamantLocalized.newChat.wrongQrError)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
reader.startScanning()
}
@@ -316,7 +316,7 @@ extension NewChatViewController: QRCodeReaderViewControllerDelegate {
if startNewChat(with: uri) {
dismiss(animated: true, completion: nil)
} else {
- dialogService.showError(withMessage: String.adamantLocalized.newChat.wrongQrError)
+ dialogService.showWarning(withMessage: String.adamantLocalized.newChat.wrongQrError)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
reader.startScanning()
}
@@ -347,9 +347,9 @@ extension NewChatViewController: UINavigationControllerDelegate, UIImagePickerCo
}
}
- dialogService.showError(withMessage: String.adamantLocalized.newChat.wrongQrError)
+ dialogService.showWarning(withMessage: String.adamantLocalized.newChat.wrongQrError)
} else {
- dialogService.showError(withMessage: String.adamantLocalized.login.noQrError)
+ dialogService.showWarning(withMessage: String.adamantLocalized.login.noQrError)
}
}
}
diff --git a/Adamant/Stories/Login/LoginViewController+QR.swift b/Adamant/Stories/Login/LoginViewController+QR.swift
index 3aab38912..3d573c353 100644
--- a/Adamant/Stories/Login/LoginViewController+QR.swift
+++ b/Adamant/Stories/Login/LoginViewController+QR.swift
@@ -84,7 +84,7 @@ extension LoginViewController {
extension LoginViewController: QRCodeReaderViewControllerDelegate {
func reader(_ reader: QRCodeReaderViewController, didScanResult result: QRCodeReaderResult) {
guard AdamantUtilities.validateAdamantPassphrase(passphrase: result.value) else {
- dialogService.showError(withMessage: String.adamantLocalized.login.wrongQrError)
+ dialogService.showWarning(withMessage: String.adamantLocalized.login.wrongQrError)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
reader.startScanning()
}
@@ -117,9 +117,9 @@ extension LoginViewController: UINavigationControllerDelegate, UIImagePickerCont
}
}
- dialogService.showError(withMessage: String.adamantLocalized.login.wrongQrError)
+ dialogService.showWarning(withMessage: String.adamantLocalized.login.wrongQrError)
} else {
- dialogService.showError(withMessage: String.adamantLocalized.login.noQrError)
+ dialogService.showWarning(withMessage: String.adamantLocalized.login.noQrError)
}
}
}
diff --git a/Adamant/Stories/Login/LoginViewController.swift b/Adamant/Stories/Login/LoginViewController.swift
index ec56a030b..825b2e7ad 100644
--- a/Adamant/Stories/Login/LoginViewController.swift
+++ b/Adamant/Stories/Login/LoginViewController.swift
@@ -306,7 +306,7 @@ class LoginViewController: FormViewController {
extension LoginViewController {
func loginWith(passphrase: String) {
guard AdamantUtilities.validateAdamantPassphrase(passphrase: passphrase) else {
- dialogService.showError(withMessage: AccountServiceError.wrongPassphrase.localized)
+ dialogService.showWarning(withMessage: AccountServiceError.wrongPassphrase.localized)
return
}
From ed1f75142366ccd572caa6347bfd82e25dade4a3 Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Fri, 1 Jun 2018 13:19:13 +0300
Subject: [PATCH 29/34] DialogServcie.showError(withMessage: , error: ) now
sends detailed error description in email. String(describing: ) error
refactored.
---
Adamant/AppDelegate.swift | 2 +-
.../Assets/l18n/en.lproj/Localizable.strings | 5 +++-
.../Assets/l18n/ru.lproj/Localizable.strings | Bin 45170 -> 45692 bytes
Adamant/Helpers/String+localized.swift | 4 +--
Adamant/ServiceProtocols/AccountService.swift | 2 +-
Adamant/ServiceProtocols/ApiService.swift | 2 +-
Adamant/ServiceProtocols/DialogService.swift | 2 +-
Adamant/Services/AdamantAccountService.swift | 2 +-
Adamant/Services/AdamantDialogService.swift | 27 ++++++++++++------
.../ApiService/AdamantApi+Transactions.swift | 1 -
.../Account/TransferViewController.swift | 6 ++--
.../Chats/ChatViewController+MessageKit.swift | 6 ++--
.../Stories/Chats/NewChatViewController.swift | 14 ++++++++-
.../Login/LoginViewController+Pinpad.swift | 2 +-
.../Stories/Login/LoginViewController.swift | 4 +--
.../Settings/QRGeneratorViewController.swift | 4 +--
.../SettingsViewController+StayIn.swift | 2 +-
17 files changed, 54 insertions(+), 31 deletions(-)
diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift
index c4f48d37b..8613c5ec7 100644
--- a/Adamant/AppDelegate.swift
+++ b/Adamant/AppDelegate.swift
@@ -245,7 +245,7 @@ extension AppDelegate {
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
if let service = container.resolve(DialogService.self) {
- service.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription))
+ service.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.notifications.registerRemotesError, error.localizedDescription), error: error)
}
}
}
diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings
index 60e16067e..c0e72c25d 100644
--- a/Adamant/Assets/l18n/en.lproj/Localizable.strings
+++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings
@@ -135,7 +135,10 @@
"Error.Mail.Title" = "I have error in my iOS app";
/* Error messge body for support email */
-"Error.Mail.Body" = "Hello,\nI have this error:\n\n%@\n\nMy device info:\n%2";
+"Error.Mail.Body" = "Hello,\nI have this error:\n\n%@\n\nMy device info:\n%@";
+
+/* Error messge body for support email, with detailed error description. Where first %@ - error's short message, second %@ - detailed description, third %@ - deviceInfo */
+"Error.Mail.Body.Detailed" = "Hello,\nI have this error:\n\n%@\n\n%@\n\nDevice:\n%@";
/* Login: Notify user, that he disabled camera in settings, and need to authorize application. */
"LoginScene.Error.AuthorizeCamera" = "You need to authorize Adamant to use device's Camera";
diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings
index f7c7c90e0f2064b81990c03658a446b214484320..c4169c9f823b4ac76bce3f307d04268d2f6bf2ad 100644
GIT binary patch
delta 339
zcmezLfa%W@rVUS=B$ZhlS)5oLSnOCV!7L*n%Zed}A#d_U0pZDZj#86VoOEP$7!(-F
z88R747&3sY6oynFD-p=g0kTpiPE@Ve0}6)&B~uxSfLMVc4JcW}Pz+S2z@WyR
z7$jT7kPpP_Ksf~<&H(d3s&av{#Xy+IkPc*lEG!1n$w2u$poPe$pji);OPPGtLR1%|
z2joDI&6qmMfa1wO6Fq_Er7`4B_IJ``*JE%2ij+)#C@Ly14-5ng79$o@U~qs;5@0c5
eF<`L)Vq2hy;^f&*o|A7l@d#rHu+7rW;kf`yBtiH9
delta 46
zcmezKgz3`*rVUS=7$qj(ml5YwV6b7aV=-m1napn{%5KVH#Sp`gH~FG~@Mb$#yIcTy
Ct_}nM
diff --git a/Adamant/Helpers/String+localized.swift b/Adamant/Helpers/String+localized.swift
index e4fd13557..931457f0e 100644
--- a/Adamant/Helpers/String+localized.swift
+++ b/Adamant/Helpers/String+localized.swift
@@ -34,8 +34,8 @@ extension String {
static let noInternetNotificationBoby = NSLocalizedString("Shared.NoInternet.Body", comment: "Shared alert notification: body message for no internet connection.")
static let emailErrorMessageTitle = NSLocalizedString("Error.Mail.Title", comment: "Error messge title for support email")
- static let emailErrorMessageBody = NSLocalizedString("Error.Mail.Body", comment: "SError messge body for support email")
-
+ static let emailErrorMessageBody = NSLocalizedString("Error.Mail.Body", comment: "Error messge body for support email")
+ static let emailErrorMessageBodyWithDescription = NSLocalizedString("Error.Mail.Body.Detailed", comment: "Error messge body for support email, with detailed error description. Where first %@ - error's short message, second %@ - detailed description, third %@ - deviceInfo")
}
private init() { }
diff --git a/Adamant/ServiceProtocols/AccountService.swift b/Adamant/ServiceProtocols/AccountService.swift
index 09e4a3c41..6d2421bb9 100644
--- a/Adamant/ServiceProtocols/AccountService.swift
+++ b/Adamant/ServiceProtocols/AccountService.swift
@@ -49,7 +49,7 @@ enum AccountServiceResult {
case failure(AccountServiceError)
}
-enum AccountServiceError {
+enum AccountServiceError: Error {
case userNotLogged
case invalidPassphrase
case wrongPassphrase
diff --git a/Adamant/ServiceProtocols/ApiService.swift b/Adamant/ServiceProtocols/ApiService.swift
index 962c42516..0e50b5545 100644
--- a/Adamant/ServiceProtocols/ApiService.swift
+++ b/Adamant/ServiceProtocols/ApiService.swift
@@ -36,7 +36,7 @@ enum ApiServiceError: Error {
if let apiError = error as? ApiServiceError {
message = apiError.localized
} else if let error = error {
- message = String(describing: error)
+ message = error.localizedDescription
} else {
message = msg
}
diff --git a/Adamant/ServiceProtocols/DialogService.swift b/Adamant/ServiceProtocols/DialogService.swift
index 07c8b8dae..a6708d9bb 100644
--- a/Adamant/ServiceProtocols/DialogService.swift
+++ b/Adamant/ServiceProtocols/DialogService.swift
@@ -88,7 +88,7 @@ protocol DialogService: class {
func dismissProgress()
func showSuccess(withMessage: String)
func showWarning(withMessage: String)
- func showError(withMessage: String)
+ func showError(withMessage: String, error: Error?)
func showNoConnectionNotification()
func dissmisNoConnectionNotification()
diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift
index a6b17e915..34da8a1cf 100644
--- a/Adamant/Services/AdamantAccountService.swift
+++ b/Adamant/Services/AdamantAccountService.swift
@@ -179,7 +179,7 @@ extension AdamantAccountService: AccountService {
self?.setState(.loggedIn)
case .failure(let error):
- print("Error update account: \(String(describing: error))")
+ print("Error update account: \(error.localized))")
}
}
}
diff --git a/Adamant/Services/AdamantDialogService.swift b/Adamant/Services/AdamantDialogService.swift
index 2fa97a3f2..abc9bf0cd 100644
--- a/Adamant/Services/AdamantDialogService.swift
+++ b/Adamant/Services/AdamantDialogService.swift
@@ -95,7 +95,7 @@ extension AdamantDialogService {
FTIndicator.showError(withMessage: message)
}
- func showError(withMessage message: String) {
+ func showError(withMessage message: String, error: Error? = nil) {
if Thread.isMainThread {
FTIndicator.dismissProgress()
} else {
@@ -115,8 +115,10 @@ extension AdamantDialogService {
let supportBtn = PMAlertAction(title: AdamantResources.iosAppSupportEmail, style: .default) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
- print("Send mail")
-
+ guard let presenter = self else {
+ return
+ }
+
let mailVC = MFMailComposeViewController()
mailVC.mailComposeDelegate = self?.mailDelegate
mailVC.setToRecipients([AdamantResources.iosAppSupportEmail])
@@ -125,14 +127,21 @@ extension AdamantDialogService {
let systemVersion = UIDevice.current.systemVersion
let model = AdamantUtilities.deviceModelCode
let deviceInfo = "Model: \(model)\n" + "iOS: \(systemVersion)\n" + "App version: \(AdamantUtilities.applicationVersion)"
-
- let body = String(format: String.adamantLocalized.alert.emailErrorMessageBody, message, deviceInfo)
-
+
+ let body: String
+
+ if let error = error {
+ let errorDescription = String(describing: error)
+ body = String(format: String.adamantLocalized.alert.emailErrorMessageBodyWithDescription, message, errorDescription, deviceInfo)
+ } else {
+ body = String(format: String.adamantLocalized.alert.emailErrorMessageBody, message, deviceInfo)
+ }
+
mailVC.setMessageBody(body, isHTML: false)
- self?.present(mailVC, animated: true, completion: nil)
+ presenter.present(mailVC, animated: true, completion: nil)
}
- })
+ }
supportBtn.titleLabel?.font = UIFont.adamantPrimary(size: 16)
supportBtn.setTitleColor(UIColor(hex: "#00B6FF"), for: .normal)
@@ -214,7 +223,7 @@ extension AdamantDialogService {
self?.present(vc, animated: true, completion: completion)
case .failure(error: let error):
- self?.showError(withMessage: String(describing: error))
+ self?.showError(withMessage: error.localizedDescription, error: error)
}
})
diff --git a/Adamant/Services/ApiService/AdamantApi+Transactions.swift b/Adamant/Services/ApiService/AdamantApi+Transactions.swift
index 107ae7c35..70dcd2d08 100644
--- a/Adamant/Services/ApiService/AdamantApi+Transactions.swift
+++ b/Adamant/Services/ApiService/AdamantApi+Transactions.swift
@@ -71,7 +71,6 @@ extension AdamantApiService {
switch serverResponse {
case .success(let response):
if let collection = response.collection {
- print("Recive \(collection.count) trantaction(s)")
completion(.success(collection))
} else {
let error = AdamantApiService.translateServerError(response.error)
diff --git a/Adamant/Stories/Account/TransferViewController.swift b/Adamant/Stories/Account/TransferViewController.swift
index 562f915fc..e5bfe9601 100644
--- a/Adamant/Stories/Account/TransferViewController.swift
+++ b/Adamant/Stories/Account/TransferViewController.swift
@@ -314,14 +314,14 @@ class TransferViewController: FormViewController {
}
case .failure(let error):
- dialogService.showError(withMessage: String(describing: error))
+ dialogService.showError(withMessage: error.localized, error: error)
}
}
- case .failure(_):
- dialogService.showError(withMessage: String.adamantLocalized.transfer.accountNotFound)
+ case .failure(let error):
+ dialogService.showError(withMessage: String.adamantLocalized.transfer.accountNotFound, error: error)
}
}
})
diff --git a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift
index ce1a17e05..9009acece 100644
--- a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift
+++ b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift
@@ -152,11 +152,11 @@ extension ChatViewController: MessageInputBarDelegate {
case .dependencyError(let error):
message = String.localizedStringWithFormat(String.adamantLocalized.chat.internalErrorFormat, error)
case .internalError(let error):
- message = String.localizedStringWithFormat(String.adamantLocalized.chat.internalErrorFormat, String(describing: error))
+ message = String.localizedStringWithFormat(String.adamantLocalized.chat.internalErrorFormat, error.localizedDescription)
case .notLogged:
message = String.localizedStringWithFormat(String.adamantLocalized.chat.internalErrorFormat, "User not logged")
case .serverError(let error):
- message = String.localizedStringWithFormat(String.adamantLocalized.chat.serverErrorFormat, String(describing: error))
+ message = String.localizedStringWithFormat(String.adamantLocalized.chat.serverErrorFormat, error.localizedDescription)
case .networkError:
message = String.adamantLocalized.chat.noNetwork
@@ -178,7 +178,7 @@ extension ChatViewController: MessageInputBarDelegate {
}
// TODO: Log this
- self.dialogService.showError(withMessage: message)
+ self.dialogService.showError(withMessage: message, error: error)
}
})
diff --git a/Adamant/Stories/Chats/NewChatViewController.swift b/Adamant/Stories/Chats/NewChatViewController.swift
index bb4de5340..83cdb0454 100644
--- a/Adamant/Stories/Chats/NewChatViewController.swift
+++ b/Adamant/Stories/Chats/NewChatViewController.swift
@@ -200,8 +200,20 @@ class NewChatViewController: FormViewController {
case .notFound:
self.dialogService.showWarning(withMessage: String.localizedStringWithFormat(String.adamantLocalized.newChat.addressNotFoundFormat, address))
+ case .serverError(let error as ApiServiceError):
+ let message: String
+ switch error {
+ case .networkError(let internalError):
+ message = internalError.localizedDescription
+
+ default:
+ message = error.localized
+ }
+
+ self.dialogService.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.newChat.serverErrorFormat, message), error: error)
+
case .serverError(let error):
- self.dialogService.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.newChat.serverErrorFormat, String(describing: error)))
+ self.dialogService.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.newChat.serverErrorFormat, error.localizedDescription), error: error)
}
}
}
diff --git a/Adamant/Stories/Login/LoginViewController+Pinpad.swift b/Adamant/Stories/Login/LoginViewController+Pinpad.swift
index 68fae3881..6c953b331 100644
--- a/Adamant/Stories/Login/LoginViewController+Pinpad.swift
+++ b/Adamant/Stories/Login/LoginViewController+Pinpad.swift
@@ -67,7 +67,7 @@ extension LoginViewController {
}
case .failure(let error):
- self?.dialogService.showError(withMessage: error.localized)
+ self?.dialogService.showError(withMessage: error.localized, error: error)
if let pinpad = self?.presentedViewController as? PinpadViewController {
pinpad.clearPin()
diff --git a/Adamant/Stories/Login/LoginViewController.swift b/Adamant/Stories/Login/LoginViewController.swift
index 825b2e7ad..6ae07051e 100644
--- a/Adamant/Stories/Login/LoginViewController.swift
+++ b/Adamant/Stories/Login/LoginViewController.swift
@@ -350,7 +350,7 @@ extension LoginViewController {
self?.loginIntoExistingAccount(passphrase: passphrase)
case .failure(let error):
- self?.dialogService.showError(withMessage: error.localized)
+ self?.dialogService.showError(withMessage: error.localized, error: error)
}
})
}
@@ -368,7 +368,7 @@ extension LoginViewController {
self?.dialogService.dismissProgress()
case .failure(let error):
- self?.dialogService.showError(withMessage: error.localized)
+ self?.dialogService.showError(withMessage: error.localized, error: error)
}
})
}
diff --git a/Adamant/Stories/Settings/QRGeneratorViewController.swift b/Adamant/Stories/Settings/QRGeneratorViewController.swift
index 99b5adab5..884692747 100644
--- a/Adamant/Stories/Settings/QRGeneratorViewController.swift
+++ b/Adamant/Stories/Settings/QRGeneratorViewController.swift
@@ -105,7 +105,7 @@ class QRGeneratorViewController: FormViewController {
if completed {
self?.dialogService.showToastMessage(String.adamantLocalized.alert.done)
} else if let error = error {
- self?.dialogService.showToastMessage(String(describing: error))
+ self?.dialogService.showToastMessage(error.localizedDescription)
}
}
self?.present(vc, animated: true, completion: nil)
@@ -187,7 +187,7 @@ extension QRGeneratorViewController {
setQr(image: qr)
case .failure(let error):
- dialogService.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.qrGenerator.internalError, String(describing: error)))
+ dialogService.showError(withMessage: String.localizedStringWithFormat(String.adamantLocalized.qrGenerator.internalError, error.localizedDescription), error: error)
}
}
diff --git a/Adamant/Stories/Settings/SettingsViewController+StayIn.swift b/Adamant/Stories/Settings/SettingsViewController+StayIn.swift
index 51c5f30dc..4c95bfd83 100644
--- a/Adamant/Stories/Settings/SettingsViewController+StayIn.swift
+++ b/Adamant/Stories/Settings/SettingsViewController+StayIn.swift
@@ -150,7 +150,7 @@ extension SettingsViewController: PinpadViewControllerDelegate {
}
case .failure(let error):
- self?.dialogService.showError(withMessage: error.localized)
+ self?.dialogService.showError(withMessage: error.localized, error: error)
}
}
From d14a559fb99b6808fdb822f85121d1ac14ae5bcf Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Fri, 1 Jun 2018 16:41:10 +0300
Subject: [PATCH 30/34] Transaction details export simplified.
---
.../TransactionDetailsViewController.swift | 33 +------------------
1 file changed, 1 insertion(+), 32 deletions(-)
diff --git a/Adamant/Stories/Transactions/TransactionDetailsViewController.swift b/Adamant/Stories/Transactions/TransactionDetailsViewController.swift
index 0a268ef08..2aab84408 100644
--- a/Adamant/Stories/Transactions/TransactionDetailsViewController.swift
+++ b/Adamant/Stories/Transactions/TransactionDetailsViewController.swift
@@ -139,43 +139,12 @@ extension TransactionDetailsViewController: UITableViewDataSource, UITableViewDe
}
guard let cell = tableView.cellForRow(at: indexPath),
- let row = Row(rawValue: indexPath.row),
let details = cell.detailTextLabel?.text else {
tableView.deselectRow(at: indexPath, animated: true)
return
}
- let payload: String
- switch row {
- case .amount:
- payload = "\(row.localized): \(details)"
-
- case .date:
- payload = "\(row.localized): \(details)"
-
- case .confirmations:
- payload = "\(row.localized): \(details)"
-
- case .fee:
- payload = "\(row.localized): \(details)"
-
- case .transactionNumber:
- payload = "\(row.localized): \(details)"
-
- case .from:
- payload = "\(row.localized): \(details)"
-
- case .to:
- payload = "\(row.localized): \(details)"
-
- case .block:
- payload = "\(row.localized): \(details)"
-
- case .openInExplorer:
- payload = ""
- }
-
- dialogService.presentShareAlertFor(string: payload,
+ dialogService.presentShareAlertFor(string: details,
types: [.copyToPasteboard, .share],
excludedActivityTypes: nil,
animated: true) {
From 3a619c0382faf11256b5a602edf47afaccf8ff3a Mon Sep 17 00:00:00 2001
From: Pavel Anokhov
Date: Fri, 1 Jun 2018 16:57:05 +0300
Subject: [PATCH 31/34] Transactions list and details scenes fixes ru
localization file encoding fixed
---
.../Assets/l18n/en.lproj/Localizable.strings | 6 ++++++
.../Assets/l18n/ru.lproj/Localizable.strings | Bin 45692 -> 28976 bytes
.../TransactionDetailsViewController.swift | 7 +++++++
.../TransactionsViewController.swift | 9 +++++++++
4 files changed, 22 insertions(+)
diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings
index c0e72c25d..f3ad99734 100644
--- a/Adamant/Assets/l18n/en.lproj/Localizable.strings
+++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings
@@ -374,6 +374,12 @@
/* Main tab bar: Settings page */
"Tabs.Settings" = "Settings";
+/* TransactionList: scene title */
+"TransactionListScene.Title" = "Transactions";
+
+/* Transaction details: scene title */
+"TransactionDetailsScene.Title" = "Details";
+
/* Transaction details: amount row. */
"TransactionDetailsScene.Row.Amount" = "Amount";
diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings
index c4169c9f823b4ac76bce3f307d04268d2f6bf2ad..bf66843430d34905cc5ecdc17dd593a81e521c5b 100644
GIT binary patch
literal 28976
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