From 8edf8aa1bbfcb39900adcec6c261d5619f60f3d5 Mon Sep 17 00:00:00 2001 From: adamant-al <33592982+adamant-al@users.noreply.github.com> Date: Tue, 4 Sep 2018 18:37:20 +0300 Subject: [PATCH 01/69] Readme update --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c1a0cd937..f06a86834 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,19 @@ -# Adamant-iOS +# ADAMANT Messenger for iOS -iOS native client for ADAMANT Messenger. Available at App Store *(via TestFlight)*. You can use this repository to build your own ADAMANT iOS application. +iOS native client for ADAMANT Messenger. Available at [App Store](https://itunes.apple.com/app/adamant-messenger/id1341473829). You can use this repository to build your own version of ADAMANT iOS application. ADAMANT is the most secure and anonymous messenger, encrypted with Blockchain. Highlights: -- The most secure and anonymous messenger (see comparison table) +- The most secure and anonymous messenger (see comparison table on Website) - PWA version is also available https://msg.adamant.im/ - Trusted. Open-source project. +- Brand new Fair dPoS blockchain concensus - The only one which is Blockchain-powered -- Integrated token transfers +- Integrated crypto transfers -Read more about ADAMANT at [adamant.im](https://adamant.im) +Read more about ADAMANT Project at [adamant.im](https://adamant.im) ## Build Setup From 4a4ce7dce2336ef7d9e8e576973844225e0f096d Mon Sep 17 00:00:00 2001 From: Pavel Anokhov
m>ZyW>DVz`i}CzXTINtE`A@E$lbQHC7NauvL{pvW~niUiF^tPaSn# j%n!f;@ZaByoQ(Yj*PI#MW{89o00000NkvXXu0mjfaw@d7 diff --git a/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/attachment@2x.png b/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/attachment@2x.png deleted file mode 100644 index 1dc44dbffbcd508879504db25983ce40ac61516f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1997 zcmV;;2Qv7HP)M2W9=-^O@#Y7GvQ$tqGT4imvX0%4L z$(vqzA(m#-3%$?_rl=e`Agxj6&=!&BiF6R6gQNqWFVDU1?&rFG|I;te EZm<)q1IxgAa2)(^s2ekV790V86@jhjj0a6Y zqo;eA;Q>%gVyVs=>^e8*44*DDu{hD~_!!vOlGLa5Tn@>FM3to0Z=)b3w-AKMrR{IX z=7DQKN6-pXg9pGnAk`1!50RY#8k97Fmq3_Q+P;RYLxCCIg8XO4$D{(br&Zhx?=L$O zQ=-oRz1?D{A4 ~8aC(Cgf*-SHhe-z12YqvGtmm3oZ$<59o`Vhna zjvV|JbkPQlw~gQ-xkn0mC{Cf55#x{IOv~3&alG0H9FlvueAo3Xtv9Hm{IpS9JX;H2 z$(;bxGa~CCADkh#MAjPIXes8wYljnEW5Sz?TU~9YWCWRtOb
A(kN%Yz`a$HKgEt3-yjs &T{Nz{}#|rY>|@FQ4r$9?L^C #8B`{21C7wU T3Ky$}d$Hz|~#EILz@RfWnRE?K! zK^!3T55d1NBZ{s=K{ss|Bhz)tqRU}g8;X4B-?rS6J0EAYAf24X2f;{Sk?wd|e@`qz z?ya5S$bJP@%$@Kr0=duyc+q9j3bY^o2q#LRzb6)A=UsB;Q@*X3nv3R?3+XB8X<32v zboG=i(py;sEIaY^R=wm#BGXr4+1A3>*OUwC?>K$uRv@hltq+T|p0utkJMpw0J<>Y< z9ay&8;8#0_J>V|@tsQwiwkiqjP*x!AXhZ5*{L9czs2$O=6Hhy-N23vFC$?<2!q*ON z(Rnb#&i*d_GcBc 5ezUGOFWEracbgq(XlK{Pw^ieMS13f^OWpJNq=ppa#Acu}4!7!~sHI$xZc)>SWRh z`f Fw2mcyj9NtxN_U>8*Ontwna0V>l50 z9+1Y r&)8o-AAOy<-cJz0I!tN0R+> zAL2BiwsemkLy+o+vEq3Z$Y5kDEP`&_?uM^7>ykdf*dX-vb_&shC j 3FheI82>qv3}_5{OfBOS-ejS zC%0LO4(|oSK1?L5U5s5b$LH1XwS!yss$5jT6iwEdQq1}b0lLz$d{~nUyhYfY5ga7# zwTusdpc}U}@Oy)j=m^@e6ZNZ0^3bG-){4%d)Q>fukDiy@NMr{cKilB<0wt4G@|e{? z0nfdWXRSBwcv_Pl-GJZ_upWx) t 6X*9(aFG zAj+3I;_e&ZO-7Z<&>} *Es|Lv|3fi9ja!^SksjbewhoTq_-fhYZCIDSk z(#e)OUWyO>Gu9}n1+$s~RArQsnCeXZp)?FM2 wdI3=*@4((Z%85OweO5x>4=~YxiOe 2V?YORz*{FWa1GcD@+7hncO@F11#Q6rX{XWa)u2*xYPt6~nn!^H z$}Xn2+d(afv)XP#`>X>YqiyKqlsYCdPO>Jki{8vh)!6p4l9Cw`tE3LIlB#dGHCj7@ zo(4wd2!0pbN5L|%4jc@+gA>32;I%Cf2ZDci8#FpkiTE`LPfe;^4)+r9PLO52sJI%u z08)CYUjh_ItGD*_;7;J_v|i7#KyAGCHi%P!NB?rKXruDpLSQqUoz<-T;Tk%kRm1BE zUmK{`7tktAyOvqN^W }G_lFL6;( zj>^*Mh@<-woeCXZ4X Iat`o}h6P*dkTm|o#Iz=)m5rZbahBpoN z4}y>|t6Tx^C{Qmu5n;>!Z|dc^C|w=BJ)G!7=%5zqT0_>`*5DxcY+5d;xAR)`-$CQY zjNs$WEQQyz7H2uyDrvFr_*xr>hO~DP)!#rIY}WN+c-l6WWq05xE0%3#1(C;3SIAm| zWCb1He+r)XW!h|<#WBR!8_NvD pbKn*A6l=w?1H@l!e63AKL)ei-@f?VQ%evMNXK!~#Jp!H%MwWmg#@SDxlNFcl z-vnCR`VhfNU ;CtYCzxgq6tz-tx74Y5)Jf<(wvs?R+ zN30$k?T0sj&^fET8(s%z5LY-gm2f@99@7w$8aEZc4!c{#-jCN!z)E7M(<`Ktd5?{Q z6xvy(#?^~Ff^fWtGMf1G#+7cT;&C&uIvncmfOoJn!f!Y=)p5=I9?NqmBmbq_>3G}% zLc*-_c6jZa5vI;3gCDI$wI=nL4rTNwH=(UnPWK2F%{c?Xmz@Fr99~;z0M|P;Ux#;r zQ}f;Mo^ WDgI1s3Y2!1I`rc?$^b zta2;7p&n5 !yRZezBrWyZNPR*C#d3P*F!~LgIcRoBV@=LXFRAwbH)P2(#nbx8+ zKxk)`FT(qvQ@1a?)ZRS!x*x80o9c9*rfI>fg>wkPD#xegb~Cg~(r(Gp3Ux2QJ9&4j z^gsQfJP$%!tGpLp=}V(gIMtTU3M|xJ1 @i% z=LmX>z74b?sM#(-!E?H!Ag^$G(-Wa5BXrCvbz=2ir|wDcHac}@!qXd)vi1;u3lyW) zruwzEYP*z{o@9zM#_2_4)qTK9VyIgKub)%*6nNfqggfCXQ7KcxmG}^k%0-|n$ZcPt zMJ$wctz?dNs%sY2Y?`GN-(LmqSf}o3@U}R0r^C}LoH8X-M~iVx;d%-8yQc)=`YSVp zf?y~$u3j8BG_JM2TL?m1t6TxEkD(P;4dR3M9N`ymJ%=(HS7!q&M(w(^`%2o?Ff5@^ zry2j#NwqzM!zi=^32m+NS$Ie8X_owV2#i;Or5EaMh1V9OOurm>$9D}~rfG%_0a)cu zc%L+t_El<6{8wP 77KK 2Y-Jz{jUgr^nqm{>B zoSK@|djQk)jaz}KPEUj;0Iv;%n9@|emQd=D$1a5A8Iq)Xtw#_}7vO!1T;sbIt}8*R z^O~&km}i!pkD$gakBx$)^=>Lo)sF=p(_8Tv@MtcA*VQBXd57Q)9-)Y%i6zx}t=%-E zdQ35DC+6w+S3QDo@+{+?gs(m&O$*#*zzTE$^`2ct>}+I0b1C&5;|9j{< M$YgV5G0HBo88a+_Y`Eg(x&-}>=x zwf%`V8oKh=Z-08TPvaBqL(H1-HS>Gya7bG6gtk_xC6ssN@ju@>H?&0cxL0dqX=rIr zG=#NwwAzNco$$0K^;l0xT8xLbR{0=2E%I~Q*J)u1PspvfufgDiw$`*C+ehDhrPG}s z0xPCar}kPqd#o3vXF+IdmD+ZCmvPN}- jP;e@MzD0=Uv7@ zt!)3|c9yf}DmTe>SGy3;Q<@b+)B9V|8|sYU7 SUL?cx^7)uK6wZ zos(d00&(nF*VEzE;wu@{cN2kpDyh)2gb(1}yDa-9+`1-HsTcvWzD?b~U8DxJQ;F+E zV8tKmroq#>Z{1Dfl(k0Gw_lgRyT+cKPJ1n-Lc*-_r|?QiX6V!7iNG!m#0Dv~9(_3x zb4c*b@Y<9e_tz4^X82LB@W2@FDf>YtgNzxokTdht?K3PVV>0 z4$ks7k;*u@O4keFbg)DUuKNgj-u?gNf_>L4r-?@sP^Co1_vXRrTC=ybz!#zDXO7r+ z*7c;cT+1-DGs+4&zOUE+lsbi|6QEn1aVVwH>2S!wTtLU#It~&p1h{XWC>We7dl&2lDqH4(Ue-M<+YtO_!7_; z*D9?eQO^LCo|3Zf>B~8zRc5h5(ARKp7brW4!QUhJKB7Wgt@_q*qF(_*C(9@`w(9jR zb2EXv@=P=Nm%#>5?}X~9)Vb#YVV&va1mHcF%#-}N=$;EwlO~impK0bE3H}JSgFK0g zxyxw#Euf!*TdfuS1Zc-J5DWsxgT6pZBR$QXfR<7^(bCT<`nxG5@lmh BcA>RN1002ovPDHLkV1j0;Ge!Ua diff --git a/Adamant/Assets/l18n/de.lproj/Localizable.strings b/Adamant/Assets/l18n/de.lproj/Localizable.strings index c75884239..16f3404c9 100755 --- a/Adamant/Assets/l18n/de.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/de.lproj/Localizable.strings @@ -652,11 +652,14 @@ /* Transfer: logged user balance. */ "TransferScene.Row.Balance" = "Kontostand"; -/* Transfer: maximum amount to transfer: available account money substracting transfer fee. */ +/* Transfer: maximum amount to transfer: available account money substracting transfer fee */ "TransferScene.Row.MaxToTransfer" = "Maximalbetrag für den Versand verfügbar"; /* Transfer: recipient address */ -"TransferScene.Row.Recipient" = "Adresse"; +"TransferScene.Row.RecipientAddress" = "Adresse"; + +/* Transfer: recipient name */ +"TransferScene.Row.RecipientName" = "Name"; /* Transfer: Send button */ "TransferScene.Row.Send" = "Senden"; @@ -679,6 +682,12 @@ /* Transfer: Confirm transfer alert: Send tokens button */ "TransferScene.Send" = "Senden"; +/* Transfer: Confirm transfer alert: 'Can't Undo' message */ +"TransferScene.CantUndo" = "You can't undo this action."; + +/* Tranfser: Confirm using maximum available for transfer tokens as amount to transfer. */ +"TransferScene.UseMaxToTransfer" = "Use all available tokens as amount to transfer?"; + /* Transfer: Confirm transfer %1$@ tokens to %2$@ message. Note two variables: at runtime %1$@ will be amount (with ADM suffix), and %2$@ will be recipient address. You can use address before amount with this so called 'position tokens'. */ "TransferScene.SendConfirmFormat" = "%1$@ zu %2$@? senden"; diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings index e8d2cb148..74d0dfc98 100755 --- a/Adamant/Assets/l18n/en.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings @@ -703,11 +703,14 @@ /* Transfer: logged user balance. */ "TransferScene.Row.Balance" = "Balance"; -/* Transfer: maximum amount to transfer: available account money substracting transfer fee. */ +/* Transfer: maximum amount to transfer: available account money substracting transfer fee */ "TransferScene.Row.MaxToTransfer" = "Max to transfer"; /* Transfer: recipient address */ -"TransferScene.Row.Recipient" = "Address"; +"TransferScene.Row.RecipientAddress" = "Address"; + +/* Transfer: recipient name */ +"TransferScene.Row.RecipientName" = "Name"; /* Transfer: Send button */ "TransferScene.Row.Send" = "Send"; @@ -734,7 +737,10 @@ "TransferScene.Send" = "Send"; /* Transfer: Confirm transfer alert: 'Can't Undo' message */ -"TransferScene.CantUndo" = "You can't undo this action."; +"TransferScene.CantUndo" = "You can't undo this action"; + +/* Tranfser: Confirm using maximum available for transfer tokens as amount to transfer. */ +"TransferScene.UseMaxToTransfer" = "Use all available tokens as amount to transfer?"; /* Transfer: Confirm transfer %1$@ tokens to %2$@ message. Note two variables: at runtime %1$@ will be amount (with ADM suffix), and %2$@ will be recipient address. You can use address before amount with this so called 'position tokens'. */ "TransferScene.SendConfirmFormat" = "Send %1$@ to %2$@?"; diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings index 821b06b01..29fec43f9 100644 --- a/Adamant/Assets/l18n/ru.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/ru.lproj/Localizable.strings @@ -703,11 +703,14 @@ /* Transfer: logged user balance. */ "TransferScene.Row.Balance" = "Баланс"; -/* Transfer: maximum amount to transfer: available account money substracting transfer fee. */ +/* Transfer: maximum amount to transfer: available account money substracting transfer fee */ "TransferScene.Row.MaxToTransfer" = "Максимум"; /* Transfer: recipient address */ -"TransferScene.Row.Recipient" = "Адрес"; +"TransferScene.Row.RecipientAddress" = "Адрес"; + +/* Transfer: recipient name */ +"TransferScene.Row.RecipientName" = "Имя"; /* Transfer: Send button */ "TransferScene.Row.Send" = "Отправить"; @@ -734,7 +737,10 @@ "TransferScene.Send" = "Отправить"; /* Transfer: Confirm transfer alert: 'Can't Undo' message */ -"TransferScene.CantUndo" = "Вы не сможете отменить это действие."; +"TransferScene.CantUndo" = "Вы не сможете отменить это действие"; + +/* Tranfser: Confirm using maximum available for transfer tokens as amount to transfer. */ +"TransferScene.UseMaxToTransfer" = "Использовать все доступные токены как количество для перевода?"; /* Transfer: Confirm transfer %1$@ tokens to %2$@ message. Note two variables: at runtime %1$@ will be amount (with ADM suffix), and %2$@ will be recipient address. You can use address before amount with this so called 'position tokens'. */ "TransferScene.SendConfirmFormat" = "Отправить %1$@ получателю %2$@?"; diff --git a/Adamant/Services/KeychainStore.swift b/Adamant/Services/KeychainStore.swift index a184d8379..dcc16d9c6 100644 --- a/Adamant/Services/KeychainStore.swift +++ b/Adamant/Services/KeychainStore.swift @@ -15,7 +15,7 @@ class KeychainStore: SecuredStore { // For AppStore builds, we use a real password. // See keychain-toAppstore.sh & keychain-toDebug.sh scripts. They runs automaticatlly for Release builds. - private let 🍩 = "debug" + private let 🍩 = "standard-berkeley-silt-excavate-sprain-platter-flatboat-jockey-sisal-catapult" func get(_ key: String) -> String? { if let rawData = keychain[key], diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 2f26d9a11..6bbee633a 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -91,11 +91,14 @@ class ChatViewController: MessagesViewController { private var prevFee: Decimal = 0 // MARK: Attachment button + static let attachmentButtonSize: CGFloat = 36.0 + lazy var attachmentButton: InputBarButtonItem = { return InputBarButtonItem() .configure { - $0.setSize(CGSize(width: 36, height: 36), animated: false) - $0.image = #imageLiteral(resourceName: "attachment") + $0.setSize(CGSize(width: ChatViewController.attachmentButtonSize, height: ChatViewController.attachmentButtonSize), animated: false) + $0.image = #imageLiteral(resourceName: "Attachment.png") + $0.tintColor = UIColor.adamant.primary }.onTouchUpInside { [weak self] _ in guard let vc = self?.router.get(scene: AdamantScene.Chats.complexTransfer) as? ComplexTransferViewController else { return @@ -106,7 +109,7 @@ class ChatViewController: MessagesViewController { let navigator = UINavigationController(rootViewController: vc) self?.present(navigator, animated: true, completion: nil) - } + } }() // MARK: RichTransaction status updates @@ -178,7 +181,6 @@ class ChatViewController: MessagesViewController { messageInputBar.textViewPadding.right = -buttonWidth messageInputBar.setRightStackViewWidthConstant(to: buttonWidth, animated: false) - messageInputBar.setLeftStackViewWidthConstant(to: 36, animated: false) // Make feeLabel let feeLabel = InputBarButtonItem() @@ -192,6 +194,12 @@ class ChatViewController: MessagesViewController { messageInputBar.setStackViewItems([feeLabel, .flexibleSpace], forStack: .bottom, animated: false) messageInputBar.setStackViewItems([attachmentButton], forStack: .left, animated: false) + // Add spacing between leftStackView (attachment button) and message input field + messageInputBar.leftStackView.alignment = .leading + messageInputBar.setLeftStackViewWidthConstant(to: ChatViewController.attachmentButtonSize + size*2, animated: false) + messageInputBar.leftStackView.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: size*2) + messageInputBar.leftStackView.isLayoutMarginsRelativeArrangement = true + messageInputBar.sendButton.configure { $0.layer.cornerRadius = size*2 $0.layer.borderWidth = 1 diff --git a/Adamant/Stories/Chats/ComplexTransferViewController.swift b/Adamant/Stories/Chats/ComplexTransferViewController.swift index e5c8a8a58..aa3304056 100644 --- a/Adamant/Stories/Chats/ComplexTransferViewController.swift +++ b/Adamant/Stories/Chats/ComplexTransferViewController.swift @@ -24,12 +24,22 @@ class ComplexTransferViewController: UIViewController { weak var transferDelegate: ComplexTransferViewControllerDelegate? var services: [WalletServiceWithSend]! - var partner: CoreDataAccount? + var partner: CoreDataAccount? { + didSet { + if let partner = partner { + navigationItem.title = partner.name ?? partner.address + } + } + } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor.white - navigationItem.title = partner?.address + + if let partner = partner { + navigationItem.title = partner.name ?? partner.address + } + navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancel)) // MARK: Services @@ -79,6 +89,7 @@ extension ComplexTransferViewController: PagingViewControllerDataSource { let vc = services[index].transferViewController() if let v = vc as? TransferViewControllerBase { if let address = partner?.address { + let name = partner?.name v.admReportRecipient = address v.recipientIsReadonly = true v.showProgressView(animated: false) @@ -87,7 +98,8 @@ extension ComplexTransferViewController: PagingViewControllerDataSource { switch result { case .success(let walletAddress): DispatchQueue.main.async { - v.recipient = walletAddress + v.recipientAddress = walletAddress + v.recipientName = name v.hideProgress(animated: true) } diff --git a/Adamant/Wallets/Adamant/AdmTransferViewController.swift b/Adamant/Wallets/Adamant/AdmTransferViewController.swift index 9476c574c..63de0c3a0 100644 --- a/Adamant/Wallets/Adamant/AdmTransferViewController.swift +++ b/Adamant/Wallets/Adamant/AdmTransferViewController.swift @@ -23,7 +23,7 @@ class AdmTransferViewController: TransferViewControllerBase { // MARK: Sending override func sendFunds() { - guard let service = service as? AdmWalletService, let recipient = recipient, let amount = amount else { + guard let service = service as? AdmWalletService, let recipient = recipientAddress, let amount = amount else { return } @@ -57,7 +57,7 @@ class AdmTransferViewController: TransferViewControllerBase { private var _recipient: String? - override var recipient: String? { + override var recipientAddress: String? { set { if let recipient = newValue, let first = recipient.first, first != "U" { _recipient = "U\(recipient)" @@ -81,7 +81,7 @@ class AdmTransferViewController: TransferViewControllerBase { $0.cell.textField.placeholder = String.adamantLocalized.newChat.addressPlaceholder $0.cell.textField.keyboardType = .numberPad - if let recipient = recipient { + if let recipient = recipientAddress { let trimmed = recipient.components(separatedBy: AdmTransferViewController.invalidCharactersSet).joined() $0.value = trimmed } @@ -163,4 +163,8 @@ class AdmTransferViewController: TransferViewControllerBase { return false } } + + override func defaultSceneTitle() -> String? { + return String.adamantLocalized.wallets.sendAdm + } } diff --git a/Adamant/Wallets/Ethereum/EthTransferViewController.swift b/Adamant/Wallets/Ethereum/EthTransferViewController.swift index fb8574d31..cd9add712 100644 --- a/Adamant/Wallets/Ethereum/EthTransferViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransferViewController.swift @@ -38,7 +38,7 @@ class EthTransferViewController: TransferViewControllerBase { override func sendFunds() { let comments = "" // TODO: - guard let service = service as? EthWalletService, let recipient = recipient, let amount = amount else { + guard let service = service as? EthWalletService, let recipient = recipientAddress, let amount = amount else { return } @@ -97,7 +97,7 @@ class EthTransferViewController: TransferViewControllerBase { private var _recipient: String? - override var recipient: String? { + override var recipientAddress: String? { set { if let recipient = newValue, let first = recipient.first, first != "0" { _recipient = "0x\(recipient)" @@ -142,7 +142,7 @@ class EthTransferViewController: TransferViewControllerBase { $0.cell.textField.placeholder = String.adamantLocalized.newChat.addressPlaceholder $0.cell.textField.keyboardType = UIKeyboardType.namePhonePad - if let recipient = recipient { + if let recipient = recipientAddress { let trimmed = recipient.components(separatedBy: EthTransferViewController.invalidCharacters).joined() $0.value = trimmed } @@ -227,4 +227,8 @@ class EthTransferViewController: TransferViewControllerBase { } } } + + override func defaultSceneTitle() -> String? { + return String.adamantLocalized.wallets.sendEth + } } diff --git a/Adamant/Wallets/TransferViewControllerBase.swift b/Adamant/Wallets/TransferViewControllerBase.swift index 708ccf6bf..cab14d4b7 100644 --- a/Adamant/Wallets/TransferViewControllerBase.swift +++ b/Adamant/Wallets/TransferViewControllerBase.swift @@ -36,6 +36,8 @@ extension String.adamantLocalized { static let cantUndo = NSLocalizedString("TransferScene.CantUndo", comment: "Transfer: Send button") + static let useMaxToTransfer = NSLocalizedString("TransferScene.UseMaxToTransfer", comment: "Tranfser: Confirm using maximum available for transfer tokens as amount to transfer.") + private init() { } } } @@ -59,6 +61,7 @@ class TransferViewControllerBase: FormViewController { case balance case amount case maxToTransfer + case name case address case fee case total @@ -70,6 +73,7 @@ class TransferViewControllerBase: FormViewController { case .balance: return "balance" case .amount: return "amount" case .maxToTransfer: return "max" + case .name: return "name" case .address: return "recipient" case .fee: return "fee" case .total: return "total" @@ -82,8 +86,9 @@ class TransferViewControllerBase: FormViewController { switch self { case .balance: return NSLocalizedString("TransferScene.Row.Balance", comment: "Transfer: logged user balance.") case .amount: return NSLocalizedString("TransferScene.Row.Amount", comment: "Transfer: amount of adamant to transfer.") - case .maxToTransfer: return NSLocalizedString("TransferScene.Row.MaxToTransfer", comment: "Transfer: maximum amount to transfer: available account money substracting transfer fee.") - case .address: return NSLocalizedString("TransferScene.Row.Recipient", comment: "Transfer: recipient address") + case .maxToTransfer: return NSLocalizedString("TransferScene.Row.MaxToTransfer", comment: "Transfer: maximum amount to transfer: available account money substracting transfer fee") + case .name: return NSLocalizedString("TransferScene.Row.RecipientName", comment: "Transfer: recipient name") + case .address: return NSLocalizedString("TransferScene.Row.RecipientAddress", comment: "Transfer: recipient address") case .fee: return NSLocalizedString("TransferScene.Row.TransactionFee", comment: "Transfer: transfer fee") case .total: return NSLocalizedString("TransferScene.Row.Total", comment: "Transfer: total amount of transaction: money to transfer adding fee") case .comments: return NSLocalizedString("TransferScene.Row.Comments", comment: "Transfer: transfer comment") @@ -155,14 +160,26 @@ class TransferViewControllerBase: FormViewController { weak var delegate: TransferViewControllerDelegate? - var recipient: String? = nil { + var recipientAddress: String? = nil { didSet { if let row: RowOf = form.rowBy(tag: BaseRows.address.tag) { - row.value = recipient + row.value = recipientAddress row.updateCell() } } } + + var recipientName: String? = nil { + didSet { + guard let row: RowOf = form.rowBy(tag: BaseRows.name.tag) else { + return + } + + row.value = recipientName + row.updateCell() + row.evaluateHidden() + } + } var admReportRecipient: String? = nil var amount: Decimal? = nil @@ -211,6 +228,7 @@ class TransferViewControllerBase: FormViewController { // MARK: UI navigationAccessoryView.tintColor = UIColor.adamant.primary + navigationItem.title = defaultSceneTitle() // MARK: Sections form.append(walletSection()) @@ -257,6 +275,11 @@ class TransferViewControllerBase: FormViewController { $0.tag = Sections.recipient.tag } + // Name row + let nameRow = defaultRowFor(baseRow: BaseRows.name) + section.append(nameRow) + + // Address row section.append(defaultRowFor(baseRow: BaseRows.address)) if !recipientIsReadonly, let stripe = recipientStripe() { @@ -372,14 +395,14 @@ class TransferViewControllerBase: FormViewController { if let row: TextRow = form.rowBy(tag: BaseRows.address.tag) { if let address = row.value, validateRecipient(address) { - recipient = address + recipientAddress = address markRow(row, valid: true) } else { - recipient = nil + recipientAddress = nil markRow(row, valid: false) } } else { - recipient = nil + recipientAddress = nil } if let row: DecimalRow = form.rowBy(tag: BaseRows.amount.tag) { @@ -416,12 +439,12 @@ class TransferViewControllerBase: FormViewController { func markRow(_ row: BaseRowType, valid: Bool) { row.baseCell.textLabel?.textColor = valid ? UIColor.black : UIColor.red } - + // MARK: - Send Actions private func confirmSendFunds() { - guard let recipient = recipient, let amount = amount else { + guard let recipient = recipientAddress, let amount = amount else { return } @@ -477,11 +500,11 @@ class TransferViewControllerBase: FormViewController { let total = withFee ? amount + service.transactionFee : amount - return balance > total + return balance >= total } func formIsValid() -> Bool { - if let recipient = recipient, validateRecipient(recipient), let amount = amount, validateAmount(amount) { + if let recipient = recipientAddress, validateRecipient(recipient), let amount = amount, validateAmount(amount) { return true } else { return false @@ -498,6 +521,13 @@ class TransferViewControllerBase: FormViewController { func reportTransferTo(admAddress: String, transferRecipient: String, amount: Decimal, comments: String, hash: String) { } + + func defaultSceneTitle() -> String? { + return WalletViewControllerBase.BaseRows.send.localized + } + + + /// MARK: - Abstract /// Send funds to recipient after validations /// You must override this method @@ -506,9 +536,6 @@ class TransferViewControllerBase: FormViewController { fatalError("You must implement sending logic") } - - /// MARK: - Abstract - /// User loaded address from QR (camera or library) /// You must override this method /// @@ -636,20 +663,60 @@ extension TransferViewControllerBase { } } + case .name: + return LabelRow() { [weak self] in + $0.title = BaseRows.name.localized + $0.tag = BaseRows.name.tag + $0.value = self?.recipientName + $0.hidden = Condition.function([], { form in + if let row: RowOf = form.rowBy(tag: BaseRows.name.tag), row.value != nil { + return false + } else { + return true + } + }) + } + case .address: return recipientRow() case .maxToTransfer: - return DecimalRow() { [weak self] in + let row = DecimalRow() { [weak self] in $0.title = BaseRows.maxToTransfer.localized $0.tag = BaseRows.maxToTransfer.tag $0.disabled = true $0.formatter = self?.balanceFormatter + $0.cell.selectionStyle = .gray if let maxToTransfer = self?.maxToTransfer { $0.value = maxToTransfer.doubleValue } - } + }.onCellSelection { [weak self] (cell, row) in + guard let value = row.value, value > 0, let presenter = self else { + row.deselect(animated: true) + return + } + + let alert = UIAlertController(title: String.adamantLocalized.transfer.useMaxToTransfer, message: nil, preferredStyle: .alert) + let cancelAction = UIAlertAction(title: String.adamantLocalized.alert.cancel , style: .cancel, handler: nil) + let confirmAction = UIAlertAction(title: String.adamantLocalized.alert.ok, style: .default) { [weak self] _ in + guard let amountRow: DecimalRow = self?.form.rowBy(tag: BaseRows.amount.tag) else { + return + } + amountRow.value = value + amountRow.updateCell() + self?.validateForm() + } + + alert.addAction(cancelAction) + alert.addAction(confirmAction) + + presenter.present(alert, animated: true, completion: { + row.deselect(animated: true) + }) + } + + return row case .amount: return DecimalRow { [weak self] in From e92dc7a411325e79ef8aa6c47bdcb9b0519584b2 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Wed, 17 Oct 2018 21:11:35 +0300 Subject: [PATCH 06/69] Transfer: Address validation fixed. --- .../Ethereum/EthTransferViewController.swift | 2 +- .../Wallets/TransferViewControllerBase.swift | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Adamant/Wallets/Ethereum/EthTransferViewController.swift b/Adamant/Wallets/Ethereum/EthTransferViewController.swift index cd9add712..b17217c0a 100644 --- a/Adamant/Wallets/Ethereum/EthTransferViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransferViewController.swift @@ -132,7 +132,7 @@ class EthTransferViewController: TransferViewControllerBase { return true case .invalid, .system: - return true + return false } } diff --git a/Adamant/Wallets/TransferViewControllerBase.swift b/Adamant/Wallets/TransferViewControllerBase.swift index cab14d4b7..adfffcfe0 100644 --- a/Adamant/Wallets/TransferViewControllerBase.swift +++ b/Adamant/Wallets/TransferViewControllerBase.swift @@ -169,6 +169,8 @@ class TransferViewControllerBase: FormViewController { } } + private var recipientAddressIsValid = false + var recipientName: String? = nil { didSet { guard let row: RowOf = form.rowBy(tag: BaseRows.name.tag) else { @@ -394,15 +396,17 @@ class TransferViewControllerBase: FormViewController { } if let row: TextRow = form.rowBy(tag: BaseRows.address.tag) { - if let address = row.value, validateRecipient(address) { - recipientAddress = address - markRow(row, valid: true) - } else { - recipientAddress = nil - markRow(row, valid: false) - } + if let address = row.value, validateRecipient(address) { + recipientAddress = address + markRow(row, valid: true) + recipientAddressIsValid = true + } else { + markRow(row, valid: false) + recipientAddressIsValid = false + } } else { recipientAddress = nil + recipientAddressIsValid = false } if let row: DecimalRow = form.rowBy(tag: BaseRows.amount.tag) { @@ -504,7 +508,7 @@ class TransferViewControllerBase: FormViewController { } func formIsValid() -> Bool { - if let recipient = recipientAddress, validateRecipient(recipient), let amount = amount, validateAmount(amount) { + if let recipient = recipientAddress, validateRecipient(recipient), let amount = amount, validateAmount(amount), recipientAddressIsValid { return true } else { return false From d8d9268890c36c64a106a7be0a9bc631820edb9b Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Wed, 17 Oct 2018 21:49:54 +0300 Subject: [PATCH 07/69] Chatroom.isForcedVisible ChatTransaction.showsChatroom ChatsProvider: hiding chatrooms with only transfers. --- .../ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents | 6 ++++-- Adamant/CoreData/ChatTransaction+CoreDataProperties.swift | 5 +++-- Adamant/CoreData/Chatroom+CoreDataProperties.swift | 5 +++-- Adamant/Services/DataProviders/AdamantChatsProvider.swift | 5 ++++- .../Services/DataProviders/AdamantTransfersProvider.swift | 2 ++ Adamant/Stories/Chats/NewChatViewController.swift | 4 +++- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents index 318bdb5e3..fc327245a 100644 --- a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents +++ b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents @@ -15,6 +15,7 @@ + @@ -26,6 +27,7 @@ + @@ -54,8 +56,8 @@ - - + + diff --git a/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift b/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift index 8b7a56808..46f05cac9 100644 --- a/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift +++ b/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift @@ -2,7 +2,7 @@ // ChatTransaction+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 24.09.2018. +// Created by Anokhov Pavel on 17.10.2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -17,10 +17,11 @@ extension ChatTransaction { return NSFetchRequest (entityName: "ChatTransaction") } + @NSManaged public var isConfirmed: Bool @NSManaged public var isUnread: Bool @NSManaged public var silentNotification: Bool @NSManaged public var status: Int16 - @NSManaged public var isConfirmed: Bool + @NSManaged public var showsChatroom: Bool @NSManaged public var chatroom: Chatroom? @NSManaged public var lastIn: Chatroom? diff --git a/Adamant/CoreData/Chatroom+CoreDataProperties.swift b/Adamant/CoreData/Chatroom+CoreDataProperties.swift index f5c7ab8f8..944afd0f8 100644 --- a/Adamant/CoreData/Chatroom+CoreDataProperties.swift +++ b/Adamant/CoreData/Chatroom+CoreDataProperties.swift @@ -2,7 +2,7 @@ // Chatroom+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 28.06.2018. +// Created by Anokhov Pavel on 17.10.2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -18,10 +18,11 @@ extension Chatroom { } @NSManaged public var hasUnreadMessages: Bool + @NSManaged public var isHidden: Bool @NSManaged public var isReadonly: Bool @NSManaged public var title: String? @NSManaged public var updatedAt: NSDate? - @NSManaged public var isHidden: Bool + @NSManaged public var isForcedVisible: Bool @NSManaged public var lastTransaction: ChatTransaction? @NSManaged public var partner: CoreDataAccount? @NSManaged public var transactions: NSSet? diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 66fc390d9..78296ec34 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -586,7 +586,10 @@ extension AdamantChatsProvider { NSSortDescriptor(key: "title", ascending: true)] request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [ NSPredicate(format: "partner!=nil"), - NSPredicate(format: "isHidden = false")]) + NSPredicate(format: "isForcedVisible = true OR isHidden = false"), + NSPredicate(format: "isForcedVisible = true OR ANY transactions.showsChatroom = true") + ]) + let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: stack.container.viewContext, sectionNameKeyPath: nil, cacheName: nil) return controller diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index 84806c037..626afa485 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -340,6 +340,7 @@ extension AdamantTransfersProvider { transaction.senderId = senderId transaction.type = Int16(TransactionType.send.rawValue) transaction.isOutgoing = true + transaction.showsChatroom = false transaction.transactionId = UUID().uuidString transaction.blockId = UUID().uuidString @@ -636,6 +637,7 @@ extension AdamantTransfersProvider { transfer.blockId = t.blockId transfer.confirmations = t.confirmations transfer.statusEnum = .delivered + transfer.showsChatroom = false transfer.isOutgoing = t.senderId == address let partnerId = transfer.isOutgoing ? t.recipientId : t.senderId diff --git a/Adamant/Stories/Chats/NewChatViewController.swift b/Adamant/Stories/Chats/NewChatViewController.swift index 5a0a2ef3f..c8246d63a 100644 --- a/Adamant/Stories/Chats/NewChatViewController.swift +++ b/Adamant/Stories/Chats/NewChatViewController.swift @@ -270,9 +270,11 @@ class NewChatViewController: FormViewController { account.name = name if let chatroom = account.chatroom, chatroom.title == nil { - account.chatroom?.title = name + chatroom.title = name } } + + account.chatroom?.isForcedVisible = true self.delegate?.newChatController(self, didSelectAccount: account) self.dialogService.dismissProgress() From c2dae3823955ef919583aab9f515599e87eac12c Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Wed, 17 Oct 2018 22:12:50 +0300 Subject: [PATCH 08/69] Transfer cell size fixed --- Adamant/SharedViews/TransferCollectionViewCell.xib | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Adamant/SharedViews/TransferCollectionViewCell.xib b/Adamant/SharedViews/TransferCollectionViewCell.xib index c5b859836..a7fd0a0e5 100644 --- a/Adamant/SharedViews/TransferCollectionViewCell.xib +++ b/Adamant/SharedViews/TransferCollectionViewCell.xib @@ -82,11 +82,13 @@ + + From 4a1ba9e7f90b64faa5833db4a653a4d945796281 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Thu, 18 Oct 2018 02:42:24 +0300 Subject: [PATCH 09/69] Empty message bubbles fixed. --- .../DataProviders/AdamantChatsProvider.swift | 76 +++++++++++++++---- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 78296ec34..18159af48 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -953,24 +953,70 @@ extension AdamantChatsProvider { let messageTransaction: ChatTransaction switch chat.type { case .message, .messageOld, .signal, .unknown: - let transaction = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) - transaction.message = decodedMessage - messageTransaction = transaction + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + messageTransaction = trs case .richMessage: - let transaction = RichMessageTransaction(entity: RichMessageTransaction.entity(), insertInto: context) - - if let decodedMessage = decodedMessage, - let data = decodedMessage.data(using: String.Encoding.utf8), - let json = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: String], - let type = json["type"] { - transaction.richType = type - transaction.richContent = json - - transaction.transactionStatus = richProviders[type] != nil ? .notInitiated : nil + if let decodedMessage = decodedMessage, let data = decodedMessage.data(using: String.Encoding.utf8), let jsonRaw = try? JSONSerialization.jsonObject(with: data, options: []) { + switch jsonRaw { + // MARK: Valid json + case let json as [String:String]: + // Supported rich message type + if let type = json[RichContentKeys.type] { + let trs = RichMessageTransaction(entity: RichMessageTransaction.entity(), insertInto: context) + trs.richContent = json + trs.richType = type + trs.transactionStatus = richProviders[type] != nil ? .notInitiated : nil + messageTransaction = trs + } + + // Not supported, show as text message + else { + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + messageTransaction = trs + } + + // MARK: Bad json, try to fix it + case let json as [String:Any]: + // Supported type but in wrong format + if let type = json[RichContentKeys.type] as? String { + var fixedJson = [String:String]() + + for (key, raw) in json { + if let value = raw as? String { + fixedJson[key] = value + } else if let value = raw as? NSNumber, let amount = AdamantBalanceFormat.currencyFormatterFull.string(from: value) { + fixedJson[key] = amount + } else { + fixedJson[key] = String(describing: raw) + } + } + + let trs = RichMessageTransaction(entity: RichMessageTransaction.entity(), insertInto: context) + trs.richContent = fixedJson + trs.richType = type + trs.transactionStatus = richProviders[type] != nil ? .notInitiated : nil + messageTransaction = trs + } + // Not supported, show as text message + else { + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + messageTransaction = trs + } + + default: + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + messageTransaction = trs + } + } else { + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + messageTransaction = trs } - - messageTransaction = transaction } messageTransaction.date = transaction.date as NSDate From 355bfd052a0d57daf7ef5e6c2c6a890139b8953a Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Tue, 23 Oct 2018 19:00:02 +0300 Subject: [PATCH 10/69] Showing transaction details after transaction sent --- .../DataProviders/TransfersProvider.swift | 7 ++++- Adamant/Services/AdamantDialogService.swift | 16 ++++++++-- .../AdamantTransfersProvider.swift | 16 ++++++++-- .../Stories/Chats/ChatViewController.swift | 22 ++++++++----- .../Chats/ComplexTransferViewController.swift | 10 +++--- .../Adamant/AdmTransferViewController.swift | 8 +++-- Adamant/Wallets/Adamant/AdmWalletRoutes.swift | 1 + .../Adamant/AdmWalletService+Send.swift | 6 ++-- .../Ethereum/EthTransferViewController.swift | 17 ++++++++-- .../Wallets/Ethereum/EthWalletRoutes.swift | 1 + ...EthWalletService+RichMessageProvider.swift | 1 - .../Wallets/TransferViewControllerBase.swift | 5 +-- Adamant/Wallets/WalletService.swift | 2 +- .../Wallets/WalletViewControllerBase.swift | 31 ++++++++++++------- 14 files changed, 102 insertions(+), 41 deletions(-) diff --git a/Adamant/ServiceProtocols/DataProviders/TransfersProvider.swift b/Adamant/ServiceProtocols/DataProviders/TransfersProvider.swift index 3e23ba622..25d9b6b63 100644 --- a/Adamant/ServiceProtocols/DataProviders/TransfersProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/TransfersProvider.swift @@ -24,6 +24,11 @@ enum TransfersProviderResult { case failure(TransfersProviderError) } +enum TransfersProviderTransferResult { + case success(transaction: TransactionDetails) + case failure(TransfersProviderError) +} + extension TransfersProviderError: RichError { var message: String { switch self { @@ -135,7 +140,7 @@ protocol TransfersProvider: DataProvider { func update(completion: ((TransfersProviderResult?) -> Void)?) // MARK: - Sending funds - func transferFunds(toAddress recipient: String, amount: Decimal, completion: @escaping (TransfersProviderResult) -> Void) + func transferFunds(toAddress recipient: String, amount: Decimal, completion: @escaping (TransfersProviderTransferResult) -> Void) // MARK: - Transactions func getTransfer(id: String) -> TransferTransaction? diff --git a/Adamant/Services/AdamantDialogService.swift b/Adamant/Services/AdamantDialogService.swift index 6c88215ab..d49d5e898 100644 --- a/Adamant/Services/AdamantDialogService.swift +++ b/Adamant/Services/AdamantDialogService.swift @@ -178,7 +178,13 @@ extension AdamantDialogService { alertVC.alertActionStackView.spacing = 0 alertVC.alertActionStackViewHeightConstraint.constant = 100 - present(alertVC, animated: true, completion: nil) + if Thread.isMainThread { + present(alertVC, animated: true, completion: nil) + } else { + DispatchQueue.main.async { [weak self] in + self?.present(alertVC, animated: true, completion: nil) + } + } } func showRichError(error: RichError) { @@ -412,7 +418,13 @@ extension AdamantDialogService { alertVC.alertActionStackViewHeightConstraint.constant = 50 } - self.present(alertVC, animated: true, completion: nil) + if Thread.isMainThread { + present(alertVC, animated: true, completion: nil) + } else { + DispatchQueue.main.async { [weak self] in + self?.present(alertVC, animated: true, completion: nil) + } + } } } diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index 626afa485..a7f0350da 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -285,14 +285,14 @@ extension AdamantTransfersProvider { // MARK: Sending Funds // Wrapper - func transferFunds(toAddress recipient: String, amount: Decimal, completion: @escaping (TransfersProviderResult) -> Void) { + func transferFunds(toAddress recipient: String, amount: Decimal, completion: @escaping (TransfersProviderTransferResult) -> Void) { // Go background sendingQueue.async { self.transferFundsInternal(toAddress: recipient, amount: amount, completion: completion) } } - private func transferFundsInternal(toAddress recipient: String, amount: Decimal, completion: @escaping (TransfersProviderResult) -> Void) { + private func transferFundsInternal(toAddress recipient: String, amount: Decimal, completion: @escaping (TransfersProviderTransferResult) -> Void) { // MARK: 0. Prepare guard let senderId = accountService.account?.address, let keypair = accountService.keypair else { completion(.failure(.notLogged)) @@ -383,8 +383,17 @@ extension AdamantTransfersProvider { } self.unconfirmedsSemaphore.signal() + do { + try context.save() + } catch { + completion(.failure(.internalError(message: "Failed to save data context", error: error))) + } - completion(.success) + if let trs = self.stack.container.viewContext.object(with: transaction.objectID) as? TransactionDetails { + completion(.success(transaction: trs)) + } else { + completion(.failure(.internalError(message: "Failed to get transaction in viewContext", error: nil))) + } case .failure(let error): completion(.failure(.serverError(error))) @@ -611,6 +620,7 @@ extension AdamantTransfersProvider { transaction.blockId = t.blockId transaction.confirmations = t.confirmations transaction.statusEnum = .delivered + transaction.fee = t.fee as NSDecimalNumber unconfirmedTransactions.removeValue(forKey: t.id) diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 6bbee633a..8b77af413 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -525,23 +525,31 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { } extension ChatViewController: TransferViewControllerDelegate, ComplexTransferViewControllerDelegate { - func transferViewControllerDidFinishTransfer(_ viewController: TransferViewControllerBase) { - dismissTransferViewController() + func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails, detailsViewController: UIViewController?) { + dismissTransferViewController(andPresent: detailsViewController) } - func complexTransferViewControllerDidFinish(_ viewController: ComplexTransferViewController) { - dismissTransferViewController() - } + func complexTransferViewController(_ viewController: ComplexTransferViewController, didFinishWithTransfer: TransactionDetails?, detailsViewController: UIViewController?) { + dismissTransferViewController(andPresent: detailsViewController) + } - private func dismissTransferViewController() { + private func dismissTransferViewController(andPresent viewController: UIViewController?) { fixKeyboardInsets = true if Thread.isMainThread { dismiss(animated: true, completion: nil) + + if let viewController = viewController, let nav = navigationController { + nav.pushViewController(viewController, animated: true) + } } else { DispatchQueue.main.async { [weak self] in self?.dismiss(animated: true, completion: nil) - } + + if let viewController = viewController, let nav = self?.navigationController { + nav.pushViewController(viewController, animated: true) + } + } } } } diff --git a/Adamant/Stories/Chats/ComplexTransferViewController.swift b/Adamant/Stories/Chats/ComplexTransferViewController.swift index aa3304056..e11eebc71 100644 --- a/Adamant/Stories/Chats/ComplexTransferViewController.swift +++ b/Adamant/Stories/Chats/ComplexTransferViewController.swift @@ -10,7 +10,7 @@ import UIKit import Parchment protocol ComplexTransferViewControllerDelegate: class { - func complexTransferViewControllerDidFinish(_ viewController: ComplexTransferViewController) + func complexTransferViewController(_ viewController: ComplexTransferViewController, didFinishWithTransfer: TransactionDetails?, detailsViewController: UIViewController?) } class ComplexTransferViewController: UIViewController { @@ -72,7 +72,7 @@ class ComplexTransferViewController: UIViewController { @objc func cancel() { - transferDelegate?.complexTransferViewControllerDidFinish(self) + transferDelegate?.complexTransferViewController(self, didFinishWithTransfer: nil, detailsViewController: nil) } } @@ -132,7 +132,7 @@ extension ComplexTransferViewController: PagingViewControllerDataSource { } extension ComplexTransferViewController: TransferViewControllerDelegate { - func transferViewControllerDidFinishTransfer(_ viewController: TransferViewControllerBase) { - transferDelegate?.complexTransferViewControllerDidFinish(self) - } + func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails, detailsViewController: UIViewController?) { + transferDelegate?.complexTransferViewController(self, didFinishWithTransfer: transfer, detailsViewController: detailsViewController) + } } diff --git a/Adamant/Wallets/Adamant/AdmTransferViewController.swift b/Adamant/Wallets/Adamant/AdmTransferViewController.swift index 63de0c3a0..f42d2b37e 100644 --- a/Adamant/Wallets/Adamant/AdmTransferViewController.swift +++ b/Adamant/Wallets/Adamant/AdmTransferViewController.swift @@ -31,7 +31,7 @@ class AdmTransferViewController: TransferViewControllerBase { service.sendMoney(recipient: recipient, amount: amount, comments: "") { [weak self] result in switch result { - case .success: + case .success(let result): service.update() guard let vc = self else { @@ -39,7 +39,11 @@ class AdmTransferViewController: TransferViewControllerBase { } vc.dialogService?.showSuccess(withMessage: String.adamantLocalized.transfer.transferSuccess) - vc.delegate?.transferViewControllerDidFinishTransfer(vc) + + let detailsVC = self?.router.get(scene: AdamantScene.Wallets.Adamant.transactionDetails) as? AdmTransactionDetailsViewController + detailsVC?.transaction = result + + vc.delegate?.transferViewController(vc, didFinishWithTransfer: result, detailsViewController: detailsVC) case .failure(let error): guard let dialogService = self?.dialogService else { diff --git a/Adamant/Wallets/Adamant/AdmWalletRoutes.swift b/Adamant/Wallets/Adamant/AdmWalletRoutes.swift index 575cc136f..3b11f90fc 100644 --- a/Adamant/Wallets/Adamant/AdmWalletRoutes.swift +++ b/Adamant/Wallets/Adamant/AdmWalletRoutes.swift @@ -22,6 +22,7 @@ extension AdamantScene.Wallets { let c = AdmTransferViewController() c.dialogService = r.resolve(DialogService.self) c.accountService = r.resolve(AccountService.self) + c.router = r.resolve(Router.self) return c } diff --git a/Adamant/Wallets/Adamant/AdmWalletService+Send.swift b/Adamant/Wallets/Adamant/AdmWalletService+Send.swift index ac5117fe0..2b70653c5 100644 --- a/Adamant/Wallets/Adamant/AdmWalletService+Send.swift +++ b/Adamant/Wallets/Adamant/AdmWalletService+Send.swift @@ -23,11 +23,11 @@ extension AdmWalletService: WalletServiceSimpleSend { /// Comments not implemented - func sendMoney(recipient: String, amount: Decimal, comments: String, completion: @escaping (WalletServiceSimpleResult) -> Void) { + func sendMoney(recipient: String, amount: Decimal, comments: String, completion: @escaping (WalletServiceResult ) -> Void) { transfersProvider.transferFunds(toAddress: recipient, amount: amount) { result in switch result { - case .success: - completion(.success) + case .success(let transaction): + completion(.success(result: transaction)) case .failure(let error): completion(.failure(error: error.asWalletServiceError())) diff --git a/Adamant/Wallets/Ethereum/EthTransferViewController.swift b/Adamant/Wallets/Ethereum/EthTransferViewController.swift index b17217c0a..0ce015ca0 100644 --- a/Adamant/Wallets/Ethereum/EthTransferViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransferViewController.swift @@ -66,15 +66,26 @@ class EthTransferViewController: TransferViewControllerBase { // MARK: 2. Send eth transaction service.sendTransaction(transaction) { result in switch result { - case .success(_): + case .success(let hash): service.update() guard let vc = self else { break } - vc.dialogService.showSuccess(withMessage: String.adamantLocalized.transfer.transferSuccess) - vc.delegate?.transferViewControllerDidFinishTransfer(vc) + service.getTransaction(by: hash) { result in + switch result { + case .success(let transaction): + let detailsVc = self?.router.get(scene: AdamantScene.Wallets.Ethereum.transactionDetails) as? EthTransactionDetailsViewController + detailsVc?.transaction = transaction + + vc.dialogService.showSuccess(withMessage: String.adamantLocalized.transfer.transferSuccess) + vc.delegate?.transferViewController(vc, didFinishWithTransfer: transaction, detailsViewController: detailsVc) + + case .failure(let error): + vc.dialogService.showRichError(error: error) + } + } case .failure(let error): self?.dialogService.showRichError(error: error) diff --git a/Adamant/Wallets/Ethereum/EthWalletRoutes.swift b/Adamant/Wallets/Ethereum/EthWalletRoutes.swift index aa35c96ba..dcf74a119 100644 --- a/Adamant/Wallets/Ethereum/EthWalletRoutes.swift +++ b/Adamant/Wallets/Ethereum/EthWalletRoutes.swift @@ -24,6 +24,7 @@ extension AdamantScene.Wallets { c.dialogService = r.resolve(DialogService.self) c.chatsProvider = r.resolve(ChatsProvider.self) c.accountService = r.resolve(AccountService.self) + c.router = r.resolve(Router.self) return c } diff --git a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift index 9838257e5..13314e158 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift @@ -42,7 +42,6 @@ extension EthWalletService: RichMessageProvider { self?.dialogService.showRichError(error: error) } } - } // MARK: Cells diff --git a/Adamant/Wallets/TransferViewControllerBase.swift b/Adamant/Wallets/TransferViewControllerBase.swift index adfffcfe0..8f90efac3 100644 --- a/Adamant/Wallets/TransferViewControllerBase.swift +++ b/Adamant/Wallets/TransferViewControllerBase.swift @@ -14,7 +14,7 @@ import QRCodeReader // MARK: - Transfer Delegate Protocol protocol TransferViewControllerDelegate: class { - func transferViewControllerDidFinishTransfer(_ viewController: TransferViewControllerBase) + func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails, detailsViewController: UIViewController?) } @@ -126,7 +126,8 @@ class TransferViewControllerBase: FormViewController { var accountService: AccountService! var dialogService: DialogService! - + var router: Router! + // MARK: - Properties diff --git a/Adamant/Wallets/WalletService.swift b/Adamant/Wallets/WalletService.swift index 427440b38..702a69117 100644 --- a/Adamant/Wallets/WalletService.swift +++ b/Adamant/Wallets/WalletService.swift @@ -199,7 +199,7 @@ protocol WalletServiceWithSend: WalletService { } protocol WalletServiceSimpleSend: WalletServiceWithSend { - func sendMoney(recipient: String, amount: Decimal, comments: String, completion: @escaping (WalletServiceSimpleResult) -> Void) + func sendMoney(recipient: String, amount: Decimal, comments: String, completion: @escaping (WalletServiceResult ) -> Void) } protocol WalletServiceTwoStepSend: WalletServiceWithSend { diff --git a/Adamant/Wallets/WalletViewControllerBase.swift b/Adamant/Wallets/WalletViewControllerBase.swift index 4b0626d81..c9bfecd70 100644 --- a/Adamant/Wallets/WalletViewControllerBase.swift +++ b/Adamant/Wallets/WalletViewControllerBase.swift @@ -229,15 +229,24 @@ class WalletViewControllerBase: FormViewController, WalletViewController { extension WalletViewControllerBase: TransferViewControllerDelegate { - func transferViewControllerDidFinishTransfer(_ viewController: TransferViewControllerBase) { - if let nav = navigationController, nav.topViewController == viewController { - DispatchQueue.main.async { - nav.popViewController(animated: true) - } - } else if presentedViewController == viewController { - DispatchQueue.main.async { [weak self] in - self?.dismiss(animated: true, completion: nil) - } - } - } + func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails, detailsViewController: UIViewController?) { + if let nav = navigationController, nav.topViewController == viewController { + DispatchQueue.main.async { + if let detailsViewController = detailsViewController { + nav.popViewController(animated: false) + nav.pushViewController(detailsViewController, animated: true) + } else { + nav.popViewController(animated: true) + } + } + } else if presentedViewController == viewController { + DispatchQueue.main.async { [weak self] in + self?.dismiss(animated: true, completion: nil) + + if let detailsViewController = detailsViewController { + self?.present(detailsViewController, animated: true, completion: nil) + } + } + } + } } From ec9696bfa88bd7d29a7f8ecc27b18f68fa53f4d4 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Wed, 24 Oct 2018 21:23:36 +0300 Subject: [PATCH 11/69] BaseTransaction: id and blockId are now optionals, chat messages now requires unique ChatTransaction.chatMessageId TransactionDetails base vc: fixed memory leak. Fixed ADM transaction details: refreshing, fee, block id, transaction id. --- .../BaseTransaction+CoreDataProperties.swift | 3 +- .../BaseTransaction+TransactionDetails.swift | 18 ++-- .../ChatModels.xcdatamodel/contents | 9 +- .../ChatTransaction+CoreDataProperties.swift | 6 +- Adamant/Models/EthTransaction.swift | 4 +- Adamant/Services/AdamantDialogService.swift | 28 +++--- .../DataProviders/AdamantChatsProvider.swift | 5 +- .../AdamantTransfersProvider.swift | 10 +- .../Chats/ChatViewController+MessageKit.swift | 10 +- .../Utilities/AdamantFormattingTools.swift | 4 +- .../AdmTransactionDetailsViewController.swift | 1 + Adamant/Wallets/TransactionDetails.swift | 4 +- ...TransactionDetailsViewControllerBase.swift | 94 ++++++++++++++----- 13 files changed, 124 insertions(+), 72 deletions(-) diff --git a/Adamant/CoreData/BaseTransaction+CoreDataProperties.swift b/Adamant/CoreData/BaseTransaction+CoreDataProperties.swift index 7f092a6ed..6d6e2f7bd 100644 --- a/Adamant/CoreData/BaseTransaction+CoreDataProperties.swift +++ b/Adamant/CoreData/BaseTransaction+CoreDataProperties.swift @@ -2,7 +2,7 @@ // BaseTransaction+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 24.03.2018. +// Created by Anokhov Pavel on 24.10.2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -28,5 +28,6 @@ extension BaseTransaction { @NSManaged public var senderId: String? @NSManaged public var transactionId: String? @NSManaged public var type: Int16 + @NSManaged public var isConfirmed: Bool } diff --git a/Adamant/CoreData/BaseTransaction+TransactionDetails.swift b/Adamant/CoreData/BaseTransaction+TransactionDetails.swift index 25c01bbf5..fd86a5209 100644 --- a/Adamant/CoreData/BaseTransaction+TransactionDetails.swift +++ b/Adamant/CoreData/BaseTransaction+TransactionDetails.swift @@ -9,12 +9,14 @@ import Foundation extension BaseTransaction: TransactionDetails { - var id: String { return transactionId ?? "" } + var id: String? { return transactionId } var senderAddress: String { return senderId ?? "" } - var recipientAddress: String { return self.recipientId ?? "" } - var blockValue: String? { return self.blockId } - var confirmationsValue: String? { return String(confirmations) } + var recipientAddress: String { return recipientId ?? "" } var dateValue: Date? { return date as Date? } + var feeValue: Decimal? { return fee?.decimalValue } + + var confirmationsValue: String? { return isConfirmed ? String(confirmations) : nil } + var blockValue: String? { return isConfirmed ? blockId : nil } var amountValue: Decimal { if let amount = self.amount { @@ -24,14 +26,6 @@ extension BaseTransaction: TransactionDetails { } } - var feeValue: Decimal { - if let fee = self.fee { - return fee.decimalValue - } else { - return 0 - } - } - var block: UInt { if let raw = blockId, let id = UInt(raw) { return id diff --git a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents index fc327245a..0c9750d82 100644 --- a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents +++ b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents @@ -2,15 +2,16 @@ - + + - + @@ -25,7 +26,7 @@ - + @@ -55,7 +56,7 @@ - + diff --git a/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift b/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift index 46f05cac9..62cf21a5d 100644 --- a/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift +++ b/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift @@ -2,7 +2,7 @@ // ChatTransaction+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 17.10.2018. +// Created by Anokhov Pavel on 24.10.2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -17,11 +17,11 @@ extension ChatTransaction { return NSFetchRequest (entityName: "ChatTransaction") } - @NSManaged public var isConfirmed: Bool @NSManaged public var isUnread: Bool + @NSManaged public var showsChatroom: Bool @NSManaged public var silentNotification: Bool @NSManaged public var status: Int16 - @NSManaged public var showsChatroom: Bool + @NSManaged public var chatMessageId: String? @NSManaged public var chatroom: Chatroom? @NSManaged public var lastIn: Chatroom? diff --git a/Adamant/Models/EthTransaction.swift b/Adamant/Models/EthTransaction.swift index 4f73d99e2..3a74bf7af 100644 --- a/Adamant/Models/EthTransaction.swift +++ b/Adamant/Models/EthTransaction.swift @@ -134,7 +134,7 @@ extension EthTransaction: Decodable { // MARK: - TransactionDetails extension EthTransaction: TransactionDetails { - var id: String { return hash } + var id: String? { return hash } var senderAddress: String { return from } var recipientAddress: String { return to } var dateValue: Date? { return date } @@ -142,7 +142,7 @@ extension EthTransaction: TransactionDetails { var confirmationsValue: String? { return confirmations } var blockValue: String? { return blockNumber} - var feeValue: Decimal { + var feeValue: Decimal? { return gasPrice * gasUsed } diff --git a/Adamant/Services/AdamantDialogService.swift b/Adamant/Services/AdamantDialogService.swift index d49d5e898..c02d22902 100644 --- a/Adamant/Services/AdamantDialogService.swift +++ b/Adamant/Services/AdamantDialogService.swift @@ -101,14 +101,18 @@ extension AdamantDialogService { FTIndicator.showError(withMessage: message) } - func showError(withMessage message: String, error: Error? = nil) { - if Thread.isMainThread { - FTIndicator.dismissProgress() - } else { - DispatchQueue.main.sync { - FTIndicator.dismissProgress() - } - } + func showError(withMessage message: String, error: Error? = nil) { + if Thread.isMainThread { + internalShowError(withMessage: message, error: error) + } else { + DispatchQueue.main.async { [weak self] in + self?.internalShowError(withMessage: message, error: error) + } + } + } + + private func internalShowError(withMessage message: String, error: Error? = nil) { + FTIndicator.dismissProgress() let alertVC = PMAlertController(title: String.adamantLocalized.alert.error, description: message, image: #imageLiteral(resourceName: "error"), style: .alert) @@ -178,13 +182,7 @@ extension AdamantDialogService { alertVC.alertActionStackView.spacing = 0 alertVC.alertActionStackViewHeightConstraint.constant = 100 - if Thread.isMainThread { - present(alertVC, animated: true, completion: nil) - } else { - DispatchQueue.main.async { [weak self] in - self?.present(alertVC, animated: true, completion: nil) - } - } + present(alertVC, animated: true, completion: nil) } func showRichError(error: RichError) { diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 18159af48..3d169f1ac 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -302,6 +302,7 @@ extension AdamantChatsProvider { transaction.senderId = senderId transaction.type = Int16(type.rawValue) transaction.isOutgoing = true + transaction.chatMessageId = UUID().uuidString transaction.message = text @@ -320,6 +321,7 @@ extension AdamantChatsProvider { transaction.senderId = senderId transaction.type = Int16(type.rawValue) transaction.isOutgoing = true + transaction.chatMessageId = UUID().uuidString transaction.richContent = richContent transaction.richType = richType @@ -374,8 +376,6 @@ extension AdamantChatsProvider { } // MARK: 3. Prepare transaction - transaction.transactionId = UUID().uuidString - transaction.blockId = UUID().uuidString transaction.statusEnum = MessageStatus.pending chatroom.addToTransactions(transaction) @@ -1029,6 +1029,7 @@ extension AdamantChatsProvider { messageTransaction.isOutgoing = isOutgoing messageTransaction.blockId = transaction.blockId messageTransaction.confirmations = transaction.confirmations + messageTransaction.chatMessageId = UUID().uuidString messageTransaction.statusEnum = MessageStatus.delivered diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index a7f0350da..f8e766c04 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -18,7 +18,7 @@ class AdamantTransfersProvider: TransfersProvider { var securedStore: SecuredStore! // MARK: Properties - var transferFee: Decimal = Decimal(sign: .plus, exponent: -1, significand: 5) + let transferFee: Decimal = Decimal(sign: .plus, exponent: -1, significand: 5) private(set) var state: State = .empty private(set) var isInitiallySynced: Bool = false @@ -341,9 +341,11 @@ extension AdamantTransfersProvider { transaction.type = Int16(TransactionType.send.rawValue) transaction.isOutgoing = true transaction.showsChatroom = false + transaction.fee = transferFee as NSDecimalNumber - transaction.transactionId = UUID().uuidString - transaction.blockId = UUID().uuidString + transaction.transactionId = nil + transaction.blockId = nil + transaction.chatMessageId = UUID().uuidString transaction.statusEnum = MessageStatus.pending // MARK: 3. Chatroom @@ -648,6 +650,8 @@ extension AdamantTransfersProvider { transfer.confirmations = t.confirmations transfer.statusEnum = .delivered transfer.showsChatroom = false + transfer.isConfirmed = true + transfer.chatMessageId = UUID().uuidString transfer.isOutgoing = t.senderId == address let partnerId = transfer.isOutgoing ? t.recipientId : t.senderId diff --git a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift index 426e36f21..44231db49 100644 --- a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift +++ b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift @@ -480,11 +480,11 @@ extension MessageTransaction: MessageType { } public var messageId: String { - return self.transactionId! + return chatMessageId! } public var sentDate: Date { - return self.date! as Date + return date! as Date } public var kind: MessageKind { @@ -513,11 +513,11 @@ extension RichMessageTransaction: MessageType { } public var messageId: String { - return self.transactionId! + return chatMessageId! } public var sentDate: Date { - return self.date! as Date + return date! as Date } } @@ -529,7 +529,7 @@ extension TransferTransaction: MessageType { } public var messageId: String { - return transactionId! + return chatMessageId! } public var sentDate: Date { diff --git a/Adamant/Utilities/AdamantFormattingTools.swift b/Adamant/Utilities/AdamantFormattingTools.swift index a2e350fec..2dd7292d6 100644 --- a/Adamant/Utilities/AdamantFormattingTools.swift +++ b/Adamant/Utilities/AdamantFormattingTools.swift @@ -27,12 +27,12 @@ class AdamantFormattingTools { } static func summaryFor(transaction: TransactionDetails, url: URL?) -> String { - return summaryFor(id: transaction.id, + return summaryFor(id: transaction.id ?? "", sender: transaction.senderAddress, recipient: transaction.recipientAddress, date: transaction.dateValue, amount: transaction.amountValue, - fee: transaction.feeValue, + fee: transaction.feeValue ?? 0, confirmations: transaction.confirmationsValue, blockId: transaction.blockValue, url: url) diff --git a/Adamant/Wallets/Adamant/AdmTransactionDetailsViewController.swift b/Adamant/Wallets/Adamant/AdmTransactionDetailsViewController.swift index 71d6c0b76..c1b4ae8dc 100644 --- a/Adamant/Wallets/Adamant/AdmTransactionDetailsViewController.swift +++ b/Adamant/Wallets/Adamant/AdmTransactionDetailsViewController.swift @@ -115,6 +115,7 @@ class AdmTransactionDetailsViewController: TransactionDetailsViewControllerBase case .success: DispatchQueue.main.async { self?.tableView.reloadData() +// self?.form. } case .failure: diff --git a/Adamant/Wallets/TransactionDetails.swift b/Adamant/Wallets/TransactionDetails.swift index 9b01abde3..3406e4a43 100644 --- a/Adamant/Wallets/TransactionDetails.swift +++ b/Adamant/Wallets/TransactionDetails.swift @@ -13,7 +13,7 @@ import BigInt /// A standard protocol representing a Transaction details. protocol TransactionDetails { /// The identifier of the transaction. - var id: String { get } + var id: String? { get } /// The sender of the transaction. var senderAddress: String { get } @@ -28,7 +28,7 @@ protocol TransactionDetails { var amountValue: Decimal { get } /// The amount of fee that taken for transaction process. - var feeValue: Decimal { get } + var feeValue: Decimal? { get } /// The confirmations of the transaction. var confirmationsValue: String? { get } diff --git a/Adamant/Wallets/TransactionDetailsViewControllerBase.swift b/Adamant/Wallets/TransactionDetailsViewControllerBase.swift index 031387704..19fb151ed 100644 --- a/Adamant/Wallets/TransactionDetailsViewControllerBase.swift +++ b/Adamant/Wallets/TransactionDetailsViewControllerBase.swift @@ -92,6 +92,12 @@ class TransactionDetailsViewControllerBase: FormViewController { } } + private static let awaitingValueString = "⏱" + + private lazy var currencyFormatter: NumberFormatter = { + return AdamantBalanceFormat.currencyFormatter(for: .full, currencySymbol: currencySymbol) + }() + // MARK: - Lifecycle override func viewDidLoad() { @@ -113,15 +119,26 @@ class TransactionDetailsViewControllerBase: FormViewController { $0.disabled = true $0.tag = Rows.transactionNumber.tag $0.title = Rows.transactionNumber.localized - $0.value = transaction?.id + + if let value = transaction?.id { + $0.value = value + } else { + $0.value = TransactionDetailsViewControllerBase.awaitingValueString + } }.cellSetup { (cell, _) in cell.selectionStyle = .gray - }.onCellSelection { (_, row) in + }.onCellSelection { [weak self] (_, row) in if let text = row.value { - self.shareValue(text) + self?.shareValue(text) } - }.cellUpdate { (cell, _) in + }.cellUpdate { [weak self] (cell, row) in cell.textLabel?.textColor = .black + + if let value = self?.transaction?.id { + row.value = value + } else { + row.value = TransactionDetailsViewControllerBase.awaitingValueString + } } section.append(idRow) @@ -150,7 +167,7 @@ class TransactionDetailsViewControllerBase: FormViewController { return DoubleDetailsTableViewCell.compactHeight } } - }.onCellSelection { (_, row) in + }.onCellSelection { [weak self] (_, row) in guard let value = row.value else { return } @@ -162,7 +179,7 @@ class TransactionDetailsViewControllerBase: FormViewController { text = value.first } - self.shareValue(text) + self?.shareValue(text) }.cellUpdate { (cell, _) in cell.textLabel?.textColor = .black } @@ -193,7 +210,7 @@ class TransactionDetailsViewControllerBase: FormViewController { return DoubleDetailsTableViewCell.compactHeight } } - }.onCellSelection { (_, row) in + }.onCellSelection { [weak self] (_, row) in guard let value = row.value else { return } @@ -205,7 +222,7 @@ class TransactionDetailsViewControllerBase: FormViewController { text = value.first } - self.shareValue(text) + self?.shareValue(text) }.cellUpdate { (cell, _) in cell.textLabel?.textColor = .black } @@ -225,8 +242,9 @@ class TransactionDetailsViewControllerBase: FormViewController { let text = value.humanizedDateTimeFull() self?.shareValue(text) } - }.cellUpdate { (cell, _) in + }.cellUpdate { [weak self] (cell, row) in cell.textLabel?.textColor = .black + row.value = self?.transaction?.dateValue } section.append(dateRow) @@ -245,28 +263,38 @@ class TransactionDetailsViewControllerBase: FormViewController { let text = AdamantBalanceFormat.full.format(value, withCurrencySymbol: self?.currencySymbol ?? nil) self?.shareValue(text) } - }.cellUpdate { (cell, _) in + }.cellUpdate { [weak self] (cell, row) in cell.textLabel?.textColor = .black + row.value = self?.transaction?.amountValue.doubleValue } section.append(amountRow) // MARK: Fee - let feeRow = DecimalRow() { + let feeRow = LabelRow() { $0.disabled = true $0.tag = Rows.fee.tag $0.title = Rows.fee.localized - $0.formatter = AdamantBalanceFormat.currencyFormatter(for: .full, currencySymbol: currencySymbol) - $0.value = transaction?.feeValue.doubleValue + + if let value = transaction?.feeValue { + $0.value = currencyFormatter.string(fromDecimal: value) + } else { + $0.value = TransactionDetailsViewControllerBase.awaitingValueString + } }.cellSetup { (cell, _) in cell.selectionStyle = .gray }.onCellSelection { [weak self] (_, row) in if let value = row.value { - let text = AdamantBalanceFormat.full.format(value, withCurrencySymbol: self?.currencySymbol ?? nil) - self?.shareValue(text) + self?.shareValue(value) } - }.cellUpdate { (cell, _) in + }.cellUpdate { [weak self] (cell, row) in cell.textLabel?.textColor = .black + + if let value = self?.transaction?.feeValue, let formatter = self?.currencyFormatter { + row.value = formatter.string(fromDecimal: value) + } else { + row.value = TransactionDetailsViewControllerBase.awaitingValueString + } } section.append(feeRow) @@ -276,15 +304,26 @@ class TransactionDetailsViewControllerBase: FormViewController { $0.disabled = true $0.tag = Rows.confirmations.tag $0.title = Rows.confirmations.localized - $0.value = transaction?.confirmationsValue + + if let value = transaction?.confirmationsValue { + $0.value = value + } else { + $0.value = TransactionDetailsViewControllerBase.awaitingValueString + } }.cellSetup { (cell, _) in cell.selectionStyle = .gray }.onCellSelection { [weak self] (_, row) in if let text = row.value { self?.shareValue(text) } - }.cellUpdate { (cell, _) in + }.cellUpdate { [weak self] (cell, row) in cell.textLabel?.textColor = .black + + if let value = self?.transaction?.confirmationsValue { + row.value = value + } else { + row.value = TransactionDetailsViewControllerBase.awaitingValueString + } } section.append(confirmationsRow) @@ -294,15 +333,26 @@ class TransactionDetailsViewControllerBase: FormViewController { $0.disabled = true $0.tag = Rows.block.tag $0.title = Rows.block.localized - $0.value = transaction?.blockValue + + if let value = transaction?.blockValue { + $0.value = value + } else { + $0.value = TransactionDetailsViewControllerBase.awaitingValueString + } }.cellSetup { (cell, _) in cell.selectionStyle = .gray }.onCellSelection { [weak self] (_, row) in if let text = row.value { self?.shareValue(text) } - }.cellUpdate { (cell, _) in + }.cellUpdate { [weak self] (cell, row) in cell.textLabel?.textColor = .black + + if let value = self?.transaction?.blockValue { + row.value = value + } else { + row.value = TransactionDetailsViewControllerBase.awaitingValueString + } } section.append(blockRow) @@ -319,8 +369,10 @@ class TransactionDetailsViewControllerBase: FormViewController { if let text = row.value { self?.shareValue(text) } - }.cellUpdate { (cell, _) in + }.cellUpdate { [weak self] (cell, row) in cell.textLabel?.textColor = .black + + row.value = self?.transaction?.transactionStatus?.localized } section.append(statusRow) From 3fc851913673ba8ceec35a12eb36027a36d497fc Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Wed, 24 Oct 2018 21:59:58 +0300 Subject: [PATCH 12/69] Localisation --- Adamant/Assets/l18n/de.lproj/Localizable.strings | 11 +++++++++++ Adamant/Assets/l18n/en.lproj/Localizable.strings | 12 ++++++++++++ Adamant/Assets/l18n/ru.lproj/Localizable.strings | 12 ++++++++++++ .../DataProviders/AccountsProvider.swift | 2 +- Adamant/Wallets/WalletService.swift | 6 +++--- 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Adamant/Assets/l18n/de.lproj/Localizable.strings b/Adamant/Assets/l18n/de.lproj/Localizable.strings index 16f3404c9..4851dd89a 100755 --- a/Adamant/Assets/l18n/de.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/de.lproj/Localizable.strings @@ -697,3 +697,14 @@ /* Transfer: Tokens transfered successfully message */ "TransferScene.TransferSuccessMessage" = "Fertig!"; +/* Wallet Services: Shared error, user do not have enought money. */ +"WalletServices.SharedErrors.NotEnoughtMoney" = "Not enought money"; + +/* Wallet Services: Shared error, user has not yet initiated a specific wallet. */ +"WalletServices.SharedErrors.WalletNotInitiated" = "Wallet not yet initiated"; + +/* Wallet Services: Shared error, invalid amount format. %@ for amount */ +"WalletServices.SharedErrors.InvalidAmountFormat" = "Invalid amount to send: %@"; + +/* Wallet Services: Shared error, transaction not found */ +"WalletServices.SharedErrors.TransactionNotFound" = "Transaction not found"; diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings index 74d0dfc98..b2e71166c 100755 --- a/Adamant/Assets/l18n/en.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings @@ -750,3 +750,15 @@ /* Transfer: Tokens transfered successfully message */ "TransferScene.TransferSuccessMessage" = "Done!"; + +/* Wallet Services: Shared error, user do not have enought money. */ +"WalletServices.SharedErrors.NotEnoughtMoney" = "Not enought money"; + +/* Wallet Services: Shared error, user has not yet initiated a specific wallet. */ +"WalletServices.SharedErrors.WalletNotInitiated" = "Wallet not yet initiated"; + +/* Wallet Services: Shared error, invalid amount format. %@ for amount */ +"WalletServices.SharedErrors.InvalidAmountFormat" = "Invalid amount to send: %@"; + +/* Wallet Services: Shared error, transaction not found */ +"WalletServices.SharedErrors.TransactionNotFound" = "Transaction not found"; diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings index 29fec43f9..0b8f71e76 100644 --- a/Adamant/Assets/l18n/ru.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/ru.lproj/Localizable.strings @@ -750,3 +750,15 @@ /* Transfer: Tokens transfered successfully message */ "TransferScene.TransferSuccessMessage" = "Готово!"; + +/* Wallet Services: Shared error, user do not have enought money. */ +"WalletServices.SharedErrors.NotEnoughtMoney" = "Недостаточно средств"; + +/* Wallet Services: Shared error, user has not yet initiated a specific wallet. */ +"WalletServices.SharedErrors.WalletNotInitiated" = "Кошелёк ещё не создан"; + +/* Wallet Services: Shared error, invalid amount format. %@ for amount */ +"WalletServices.SharedErrors.InvalidAmountFormat" = "Неверное количество для отправки: %@"; + +/* Wallet Services: Shared error, transaction not found */ +"WalletServices.SharedErrors.TransactionNotFound" = "Транзакция не найдена"; diff --git a/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift b/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift index 7e168dfd6..6557bb80c 100644 --- a/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/AccountsProvider.swift @@ -22,7 +22,7 @@ enum AccountsProviderResult { return "" case .notFound(let address): - return String.localizedStringWithFormat(String.adamantLocalized.sharedErrors.accountNotFound, address) + return String.localizedStringWithFormat(String.adamantLocalized.sharedErrors.accountNotFound, address) case .invalidAddress(let address): return String.localizedStringWithFormat(NSLocalizedString("AccountsProvider.Error.AddressNotValidFormat", comment: "AccountsProvider: Address not valid error, %@ for address"), address) diff --git a/Adamant/Wallets/WalletService.swift b/Adamant/Wallets/WalletService.swift index 702a69117..35a0dcf87 100644 --- a/Adamant/Wallets/WalletService.swift +++ b/Adamant/Wallets/WalletService.swift @@ -55,7 +55,7 @@ extension WalletServiceError: RichError { return String.adamantLocalized.transfer.accountNotFound case .walletNotInitiated: - return "Кошелёк ещё не создан для этого аккаунта" + return NSLocalizedString("WalletServices.SharedErrors.WalletNotInitiated", comment: "Wallet Services: Shared error, user has not yet initiated a specific wallet.") case .remoteServiceError(let message): return String.adamantLocalized.sharedErrors.remoteServerError(message: message) @@ -67,10 +67,10 @@ extension WalletServiceError: RichError { return String.adamantLocalized.sharedErrors.internalError(message: message) case .invalidAmount(let amount): - return "Неверное количество для перевода: \(amount)" + return String.localizedStringWithFormat(NSLocalizedString("WalletServices.SharedErrors.InvalidAmountFormat", comment: "Wallet Services: Shared error, invalid amount format. %@ for amount"), AdamantBalanceFormat.full.format(amount)) case .transactionNotFound: - return "Не удалось найти транзакцию" + return NSLocalizedString("WalletServices.SharedErrors.TransactionNotFound", comment: "Wallet Services: Shared error, transaction not found") } } From 816d24df93120dd9c43d514c3f0ca3782ddc82b8 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Thu, 25 Oct 2018 01:14:35 +0300 Subject: [PATCH 13/69] Fixed saving ETH into KVS. Improved balance check. --- .../ChatTransaction+CoreDataProperties.swift | 2 +- ...essageTransaction+CoreDataProperties.swift | 2 +- Adamant/ServiceProtocols/AccountService.swift | 4 +- .../Account/AccountViewController.swift | 35 ++-- .../Account/WalletCollectionViewCell.swift | 14 +- .../Stories/Account/WalletPagingItem.swift | 2 +- .../Wallets/Ethereum/EthWalletService.swift | 180 ++++++++++++------ .../Wallets/WalletViewControllerBase.swift | 33 +++- Adamant/Wallets/WalletViewControllerBase.xib | 11 +- 9 files changed, 202 insertions(+), 81 deletions(-) diff --git a/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift b/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift index 62cf21a5d..4a187d05c 100644 --- a/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift +++ b/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift @@ -2,7 +2,7 @@ // ChatTransaction+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 24.10.2018. +// Created by Anokhov Pavel on 25.10.2018. // Copyright © 2018 Adamant. All rights reserved. // // diff --git a/Adamant/CoreData/MessageTransaction+CoreDataProperties.swift b/Adamant/CoreData/MessageTransaction+CoreDataProperties.swift index 31252778a..039c08ee7 100644 --- a/Adamant/CoreData/MessageTransaction+CoreDataProperties.swift +++ b/Adamant/CoreData/MessageTransaction+CoreDataProperties.swift @@ -2,7 +2,7 @@ // MessageTransaction+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 24.09.2018. +// Created by Anokhov Pavel on 25.10.2018. // Copyright © 2018 Adamant. All rights reserved. // // diff --git a/Adamant/ServiceProtocols/AccountService.swift b/Adamant/ServiceProtocols/AccountService.swift index 9cc434827..b501649bf 100644 --- a/Adamant/ServiceProtocols/AccountService.swift +++ b/Adamant/ServiceProtocols/AccountService.swift @@ -33,8 +33,8 @@ extension Notification.Name { /// Raised when wallets collection updated /// /// UserInfo: - /// - Adamant.AccountService.updatedWallet: wallet object - /// - Adamant.AccountService.updatedWalletIndex: wallet index in AccountService.wallets collection + /// - AdamantUserInfoKey.AccountService.updatedWallet: wallet object + /// - AdamantUserInfoKey.AccountService.updatedWalletIndex: wallet index in AccountService.wallets collection static let walletUpdated = Notification.Name("adamant.accountService.walletUpdated") private init() {} diff --git a/Adamant/Stories/Account/AccountViewController.swift b/Adamant/Stories/Account/AccountViewController.swift index 9afd54dba..a216acc8c 100644 --- a/Adamant/Stories/Account/AccountViewController.swift +++ b/Adamant/Stories/Account/AccountViewController.swift @@ -23,6 +23,8 @@ extension String.adamantLocalized { static let getFreeTokensUrlFormat = NSLocalizedString("AccountTab.FreeTokens.UrlFormat", comment: "Account tab: A full 'Get free tokens' link, with %@ as address") static let buyTokensUrlFormat = NSLocalizedString("AccountTab.BuyTokens.UrlFormat", comment: "Account tab: A full 'Buy tokens' link, with %@ as address") + static let updatingBalance = "…" + private init() { } } } @@ -450,23 +452,30 @@ extension AccountViewController: PagingViewControllerDataSource, PagingViewContr } func pagingViewController (_ pagingViewController: PagingViewController , viewControllerForIndex index: Int) -> UIViewController { - return accountService.wallets[index].walletViewController.viewController + let service = accountService.wallets[index] + let viewController = service.walletViewController.viewController + + if let vcBase = viewController as? WalletViewControllerBase { + vcBase.isInitiated = service.wallet != nil + } + + return viewController } func pagingViewController (_ pagingViewController: PagingViewController , pagingItemForIndex index: Int) -> T { let service = accountService.wallets[index] - - guard let wallet = service.wallet else { - return WalletPagingItem(index: index, currencySymbol: "", currencyImage: #imageLiteral(resourceName: "wallet_adm")) as! T - } - - let serviceType = type(of: service) - - let item = WalletPagingItem(index: index, currencySymbol: serviceType.currencySymbol, currencyImage: serviceType.currencyLogo) - item.balance = wallet.balance - item.notifications = wallet.notifications - - return item as! T + let serviceType = type(of: service) + + let item = WalletPagingItem(index: index, currencySymbol: serviceType.currencySymbol, currencyImage: serviceType.currencyLogo) + + if let wallet = service.wallet { + item.balance = wallet.balance + item.notifications = wallet.notifications + } else { + item.balance = nil + } + + return item as! T } func pagingViewController (_ pagingViewController: PagingViewController , didScrollToItem pagingItem: T, startingViewController: UIViewController?, destinationViewController: UIViewController, transitionSuccessful: Bool) { diff --git a/Adamant/Stories/Account/WalletCollectionViewCell.swift b/Adamant/Stories/Account/WalletCollectionViewCell.swift index 6dd9ca2f3..2eb9d6692 100644 --- a/Adamant/Stories/Account/WalletCollectionViewCell.swift +++ b/Adamant/Stories/Account/WalletCollectionViewCell.swift @@ -24,11 +24,15 @@ class WalletCollectionViewCell: PagingCell { currencyImageView.image = item.currencyImage currencySymbolLabel.text = item.currencySymbol - if item.balance < 1 { - balanceLabel.text = AdamantBalanceFormat.compact.format(item.balance) - } else { - balanceLabel.text = AdamantBalanceFormat.short.format(item.balance) - } + if let balance = item.balance { + if balance < 1 { + balanceLabel.text = AdamantBalanceFormat.compact.format(balance) + } else { + balanceLabel.text = AdamantBalanceFormat.short.format(balance) + } + } else { + balanceLabel.text = String.adamantLocalized.account.updatingBalance + } accessoryContainerView.accessoriesBackgroundColor = options.indicatorColor diff --git a/Adamant/Stories/Account/WalletPagingItem.swift b/Adamant/Stories/Account/WalletPagingItem.swift index d5a669ad1..06a8f585d 100644 --- a/Adamant/Stories/Account/WalletPagingItem.swift +++ b/Adamant/Stories/Account/WalletPagingItem.swift @@ -14,7 +14,7 @@ class WalletPagingItem: PagingItem, Hashable, Comparable { let currencySymbol: String let currencyImage: UIImage - var balance: Decimal = 0 + var balance: Decimal? = 0 var notifications: Int = 0 init(index: Int, currencySymbol symbol: String, currencyImage image: UIImage) { diff --git a/Adamant/Wallets/Ethereum/EthWalletService.swift b/Adamant/Wallets/Ethereum/EthWalletService.swift index 5a61881f4..4350414a2 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService.swift @@ -89,6 +89,8 @@ class EthWalletService: WalletService { vc.service = self return vc } + + private var initialBalanceCheck = false // MARK: - State private (set) var state: WalletServiceState = .notInitiated @@ -96,7 +98,9 @@ class EthWalletService: WalletService { var wallet: WalletAccount? { return ethWallet } - + // MARK: - Delayed KVS save + private var balanceObserver: NSObjectProtocol? = nil + // MARK: - Logic init(apiUrl: String) throws { // Init network @@ -118,6 +122,11 @@ class EthWalletService: WalletService { NotificationCenter.default.addObserver(forName: Notification.Name.AdamantAccountService.userLoggedOut, object: nil, queue: nil) { [weak self] _ in self?.ethWallet = nil + self?.initialBalanceCheck = false + if let balanceObserver = self?.balanceObserver { + NotificationCenter.default.removeObserver(balanceObserver) + self?.balanceObserver = nil + } } } @@ -139,22 +148,36 @@ class EthWalletService: WalletService { state = .updating - getBalance(forAddress: wallet.ethAddress) { result in - defer { - self.stateSemaphore.signal() - } - self.stateSemaphore.wait() - self.state = .updated - + getBalance(forAddress: wallet.ethAddress) { [weak self] result in + if let stateSemaphore = self?.stateSemaphore { + defer { + stateSemaphore.signal() + } + stateSemaphore.wait() + self?.state = .updated + } + switch result { case .success(let balance): + let notification: Notification.Name? + if wallet.balance != balance { wallet.balance = balance - NotificationCenter.default.post(name: self.walletUpdatedNotification, object: self, userInfo: [AdamantUserInfoKey.WalletService.wallet: wallet]) - } + notification = self?.walletUpdatedNotification + self?.initialBalanceCheck = false + } else if let initialBalanceCheck = self?.initialBalanceCheck, initialBalanceCheck { + self?.initialBalanceCheck = false + notification = self?.walletUpdatedNotification + } else { + notification = nil + } + + if let notification = notification { + NotificationCenter.default.post(name: notification, object: self, userInfo: [AdamantUserInfoKey.WalletService.wallet: wallet]) + } case .failure(let error): - self.dialogService.showRichError(error: error) + self?.dialogService.showRichError(error: error) } } @@ -230,6 +253,11 @@ class EthWalletService: WalletService { // MARK: - WalletInitiatedWithPassphrase extension EthWalletService: InitiatedWithPassphraseService { func initWallet(withPassphrase passphrase: String, completion: @escaping (WalletServiceResult ) -> Void) { + guard let adamant = accountService.account else { + completion(.failure(error: .notLogged)) + return + } + // MARK: 1. Prepare stateSemaphore.wait() @@ -265,7 +293,8 @@ extension EthWalletService: InitiatedWithPassphraseService { } // MARK: 3. Update - ethWallet = EthWallet(address: ethAddress.address, ethAddress: ethAddress, keystore: keystore) + let eWallet = EthWallet(address: ethAddress.address, ethAddress: ethAddress, keystore: keystore) + ethWallet = eWallet state = .initiated if !enabled { @@ -276,19 +305,76 @@ extension EthWalletService: InitiatedWithPassphraseService { stateSemaphore.signal() // MARK: 4. Save into KVS - save(ethAddress: ethAddress.address) { [weak self] result in - switch result { - case .success: - break - - case .failure(let error): - self?.dialogService.showRichError(error: error) - } - } - - // MARK: 5. Initiate update - update() + getWalletAddress(byAdamantAddress: adamant.address) { [weak self] result in + switch result { + case .success(let address): + // ETH already saved + if address != ethAddress.address { + self?.save(ethAddress: ethAddress.address) { result in + self?.kvsSaveCompletionRecursion(ethAddress: ethAddress.address, result: result) + } + } + + self?.initialBalanceCheck = true + self?.update() + + completion(.success(result: eWallet)) + + case .failure(let error): + switch error { + case .walletNotInitiated: + // Show '0' without waiting for balance update + if let notification = self?.walletUpdatedNotification, let wallet = self?.ethWallet { + NotificationCenter.default.post(name: notification, object: self, userInfo: [AdamantUserInfoKey.WalletService.wallet: wallet]) + } + + self?.save(ethAddress: ethAddress.address) { result in + self?.kvsSaveCompletionRecursion(ethAddress: ethAddress.address, result: result) + } + + completion(.success(result: eWallet)) + + default: + completion(.failure(error: error)) + } + } + } } + + + /// New accounts doesn't have enought money to save KVS. We need to wait for balance update, and then - retry save + private func kvsSaveCompletionRecursion(ethAddress: String, result: WalletServiceSimpleResult) { + if let observer = balanceObserver { + NotificationCenter.default.removeObserver(observer) + balanceObserver = nil + } + + switch result { + case .success: + break + + case .failure(let error): + switch error { + case .notEnoughtMoney: // Possibly new account, we need to wait for dropship + // Register observer + let observer = NotificationCenter.default.addObserver(forName: NSNotification.Name.AdamantAccountService.accountDataUpdated, object: nil, queue: nil) { [weak self] _ in + guard let balance = self?.accountService.account?.balance, balance > AdamantApiService.KvsFee else { + return + } + + self?.save(ethAddress: ethAddress) { result in + self?.kvsSaveCompletionRecursion(ethAddress: ethAddress, result: result) + } + } + + // Save referense to unregister it later + balanceObserver = observer + + default: + dialogService.showRichError(error: error) + } + } + } } @@ -349,42 +435,26 @@ extension EthWalletService { /// - ethAddress: Ethereum address to save into KVS /// - adamantAddress: Owner of Ethereum address /// - completion: success - private func save(ethAddress: String, completion: @escaping (WalletServiceSimpleResult) -> Void) { + private func save(ethAddress: String, completion: @escaping (WalletServiceSimpleResult) -> Void) { guard let adamant = accountService.account, let keypair = accountService.keypair else { completion(.failure(error: .notLogged)) return } - let api = apiService - - getWalletAddress(byAdamantAddress: adamant.address) { result in - switch result { - case .success(let address): - guard address == ethAddress else { - // ETH already saved - completion(.success) - return - } - - guard adamant.balance >= AdamantApiService.KvsFee else { - completion(.failure(error: .notEnoughtMoney)) - return - } - - api?.store(key: EthWalletService.kvsAddress, value: ethAddress, type: .keyValue, sender: adamant.address, keypair: keypair) { result in - switch result { - case .success: - completion(.success) - - case .failure(let error): - completion(.failure(error: .apiError(error))) - } - } - - case .failure(let error): - completion(.failure(error: error)) - } - } + guard adamant.balance >= AdamantApiService.KvsFee else { + completion(.failure(error: .notEnoughtMoney)) + return + } + + apiService.store(key: EthWalletService.kvsAddress, value: ethAddress, type: .keyValue, sender: adamant.address, keypair: keypair) { result in + switch result { + case .success: + completion(.success) + + case .failure(let error): + completion(.failure(error: .apiError(error))) + } + } } } diff --git a/Adamant/Wallets/WalletViewControllerBase.swift b/Adamant/Wallets/WalletViewControllerBase.swift index c9bfecd70..987ddc91a 100644 --- a/Adamant/Wallets/WalletViewControllerBase.swift +++ b/Adamant/Wallets/WalletViewControllerBase.swift @@ -53,15 +53,41 @@ class WalletViewControllerBase: FormViewController, WalletViewController { var service: WalletService? + var isInitiated: Bool = true { + didSet { + guard viewDidInitialLoad else { + return + } + + if isInitiated { + initiatingActivityIndicator.stopAnimating() + tableView.isHidden = false + } else { + initiatingActivityIndicator.startAnimating() + tableView.isHidden = true + } + } + } + + // If we try to set isInitiated before view did load from nib, we will get a nil in IBOutlets. + private var viewDidInitialLoad = false + // MARK: - IBOutlets @IBOutlet weak var walletTitleLabel: UILabel! - + @IBOutlet weak var initiatingActivityIndicator: UIActivityIndicatorView! + // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() + + viewDidInitialLoad = true + if !isInitiated { + initiatingActivityIndicator.startAnimating() + tableView.isHidden = true + } let section = Section() @@ -175,8 +201,13 @@ class WalletViewControllerBase: FormViewController, WalletViewController { if let service = service { let callback = { [weak self] (notification: Notification) in guard let wallet = notification.userInfo?[AdamantUserInfoKey.WalletService.wallet] as? WalletAccount else { + self?.isInitiated = false return } + + if let isInitiated = self?.isInitiated, !isInitiated { + self?.isInitiated = true + } if let row: AlertLabelRow = self?.form.rowBy(tag: BaseRows.balance.tag) { let symbol = type(of: service).currencySymbol diff --git a/Adamant/Wallets/WalletViewControllerBase.xib b/Adamant/Wallets/WalletViewControllerBase.xib index 546122305..8df23e908 100644 --- a/Adamant/Wallets/WalletViewControllerBase.xib +++ b/Adamant/Wallets/WalletViewControllerBase.xib @@ -1,17 +1,18 @@ - + From a2db0d181b5b382a71c1eb2c7bf103b52cac6426 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov - + + @@ -32,14 +33,20 @@ + + + + + + From 58b19150f5c22c4ff6c7940ce200e33d89fbfc05 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Thu, 25 Oct 2018 14:48:23 +0300 Subject: [PATCH 14/69] New 'Send money' button. --- .../Buttons/SendMoney.imageset/Contents.json | 26 ++++++++++++++++++ .../Buttons/SendMoney.imageset/SendMoney.png | Bin 0 -> 703 bytes .../SendMoney.imageset/SendMoney@2x.png | Bin 0 -> 1820 bytes .../SendMoney.imageset/SendMoney@3x.png | Bin 0 -> 2919 bytes .../Stories/Chats/ChatViewController.swift | 2 +- 5 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/Contents.json create mode 100644 Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney.png create mode 100644 Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney@2x.png create mode 100644 Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney@3x.png diff --git a/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/Contents.json b/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/Contents.json new file mode 100644 index 000000000..46829e511 --- /dev/null +++ b/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "SendMoney.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SendMoney@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SendMoney@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney.png b/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney.png new file mode 100644 index 0000000000000000000000000000000000000000..7dda95dc50776d058dde91531e36dda2320bb05f GIT binary patch literal 703 zcmV;w0zmzVP) Px%c}YY;R9Fekmp^C}K@^8C5>P}C6e0-XRf?@z7!8p?kk|>frV?ulF@=Suv9k** zh$z8E(85^QSR`Ph_@@sf3NZ>IVxeH=pYJ<%LUOx%`_}Vf9(=cR@BQZOyV;q!*}g{1 z`j8!iQ*a1&XY_OZ3UA>xJcMUZ+q^+1+f490T!0Vo2)vQ)k6 CcnXeC zEpsI1z-RxjY%7Ug!o_Gp=Y)?VIud8mO2l>{T7VN(VpoR~Gdd>@qWzI!J7U`r4O9sy z5V$%b=7_xmm#Nr}jv~@>b1(S>ktMnb-4n2Y=kOwH>oXRyy=9^y0&mKKZT?BVTf}GB zlVMxmBt*>3?p%@Z8qUi_fi~YszW>Y^T@|yP{A#M~27+IZD`pnwNJZ}^@h{*nFWR8= z8B^1QPBukQZLXMkoclWT+(OJhG&|3LbE@6+8B?RyKH50wD`QPD-w b(zplRA&)G5VsS^%?UQ)_F8(^jbM*={y3R ztVz&`Xvc0R>#Z!0x$eNq4_oi$>po%;cOgv;nvKQ~L^rf6XiZYIp&5(Vbwty!rU*Ma zG=H r|0E_A6!DxW|Zg!OiBMZ@bbyR#s?(k8u zRS6#(;_#e`aby-DdUQ bkPMmIz%< l>+;H3RYb3v?002ovPDHLkV1khwOH%*< literal 0 HcmV?d00001 diff --git a/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney@2x.png b/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1dad6d686603fc6440463ac02c1efb69b08ec656 GIT binary patch literal 1820 zcmV+%2jlpOP) Px*)=5M`RA>e5n@em}MHqlV@P#y@AP*(vhD7lJ254f4skCeeEYJlQ-N;5m*pQHb z3kV4;2&{;&gjE;a$Vw9oB1A-!f`G<~I|M_zU?C8ZCY6eakMC>F)ZuVw?_*B65dY+3 z=A1MCKmYu9?#w^uma${XlL?q^gI1W6F`cp{<8R8#8GV#zp(o=c unTm;?Q%2RZdr>1VJF*9-R*o- fBV z{_h#qQ_DIG8jy=HErs!rftqKemPawz3py;;y_Z_rFxUaEEe%Nr+B$k2QYT#=<~*c@ zV1&|pK5I$|2er-;yxWG&5QKc`v!;`FJkrqtNPRYOk#>R32O?bi%r;FW`|w5wJ+-Ek z3dSx$7`)~AjQE~yD$o>S()A~}Wlc`#(F54(fq3AqPwg4j<7c*2C#B5C*vqvVT#tf| z?X6XDg!ZmQ@qn%SW-W;I{PWc7ms`7sz?y>6ojJCWGL^iEq5NP!DlqQ7d^>ehi8Dm| zRR_hl<2H8sHuBmJ^<{sL5A6&>y*O8EWxg#d|)B+p4RL6fzGZ!FE+aS(+()lcINQ zdd?11&xt1xVvyq>liP1>F= zusdu}x-x|nhplY|oxjVyYQ?tsplqvF`4;V-EU;-Gw+xEEb=cq~XhgyZIUdka4A$~) z!nW$fHav9~C2f8c`g3h#y=v-(LFvtmRw(P03W}E?cEaTAai~?)Q8%`w?F!`OHEzW1 z>Z#uplwUHV^(JH{Qaw4x+_BoBUQt&Y@c`|^zVh-8H{y2n)Ncri7aF-Q-&2*>CQ^AP z;^eDJGdDG>HfbA=W;m4Q{ix-7Fer_#;Euvh^OE8><02bHec5+W+q_RBT{|J(uu+Py z%y-y{KMkjQ2h=MaDCWO1_EGeeDcYH0U#hL|!~Wp6q?d|H6{uG#DBh`ys$>5YhL(&} z+wL-qx<$sH$k$xgy6vhH+t8-A^O9oQwI7o|e}5X1E^0nVk(h1GY{dh5k9*aMZDU>Q z+H6E#Ksj$ST>JX+hG5vyrc7a%7Y&MwTk6!k(FL;=pF#lQ@*ww%)}%2@Glg#{Q^>6j zch3!D+-GGS?!xgu4#wy7R~QU^oB-~@uIhK=@&9?o6$j%G*AK$5(#=zqRt?*#wueP8 zh(0bb;l0>l>nUFlIs1bBFQ|_g4B8C-<)BJ?y+|mX*b2^s^6kjJ^>9Cw>ZFusFsAJ< zp{iim`Bb=DUot<%*uDanhML4o%}DO3No(ZB6`B+!ZRfckW`(6~Dv(-QFj&{$j4Vb` z#|EH`AFA?6TO16ZEr+I)aB58bJ$9g5Ar3-Z_gPcQ61>vU>tJe}Qao7~C2ihIaq3R# z>kYQ{aAi<*G_~ZDwq80&@n${mXB#&H~waxgG-}=q%*9zq_RT?IrGV z9T)n35vIit8mYU$xzPEmd>MaIW#W}G`NG_wEaKm5U_e$3-bwh+hRkq3<-CM;%Q7_! z!{@+Y>@KU5uB?Mz%Bf{_lEAKO#aS>Io?>fx2SfAf#dihAz>W+`GtLxqrk^q&Pf*sC zgrQfp{Qsxj1)*}Lbb&L)g|>{{2eUQFT3Y+sLZ9|S3yiwB(1sM;JSBDVR?UEB)5vwg zK*;+r4sM&cC5Jr4jJ6X}pM8o3H<6oLU;qrn1xWe+26Od`$y>;1bxtQeB~OOb;_kK` zn)bh##RK;2a|FWlH7W8X(@JRMb>t-US7)}aQU?2ns4s@nq|rCp!LfEK8(|svTdU$) z(*sB06r2VVClfvKK8=#o*mXhH-sG^e7d!i2H`NQ zfhjO*l4^z&IqWODHfV)88Ph4<9>wkZaz-DenWi&JPQn!!b@>;LZ1bQM3wWLY0000< KMNUMnLSTX_ZhA%l literal 0 HcmV?d00001 diff --git a/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney@3x.png b/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f0bf131fec1e06624c31ed1ac980f55c48b8a11f GIT binary patch literal 2919 zcmV-t3z+nYP) Px=AxT6*RCodHoqvoSRTal+OIv~|yS8Zc54sB>l8|m%wuC D-=tA3ThDje3y6P z+1r_U^XARWo%gmU`R=`Q@11kL=iZsQ_uQG+&N;7HG9Nlu1h|ajA+X5dWAew_IN|bd zk)LYg2x;HSoXE`QVt*Xm04Bh7;8UOsc?TsLqk6FU!p zG0=Fa^NzWQ-q--vgDW$7<`8v%0Do>n9eNC$cF>(R9AGKOE5KD?WxI|fQQn~RVek<6 zAhE?PC|rz>TY%<-q`Tv PV&E=AED;0bUNB-z#-dmmT?W{qgB*a~!|hn;eY zvYkNlN*#CYLhRfM9tH1%kdN-jO+a_`K+77uI}E}O_!DJ%lwAm7iEQ5k4uO!L?%vzL zz{v<6y#PXo4^Xxi%mq!6^C5C4col?vAE#^y7zkO77vVgjCF{CA@a`@+z6bmOz02|K zeZW1H;sR>60q@jD9Pa=_pkG9H$Rj|(tM^5I%maNT7sK{t;2n9J<7@iLi skFGKquzy?Qi&Twp3v?TVcFFn3y!<`cL`;8Q6)6)iL zFL`bEM3Ze;HZq#%S4U$K-|#I^_z$rDP~Sum4T6zS8hiEiO*jZKWfR7&Pqb>gscteO z7(DJsC{Bqft1$l0Hk6MgT1r0;Y;d%U)*M`$_zg*|M22g#e6}II*J%Ok4CPf8OF}+R z*F{8I_tpuTS7L6nXjj@_B*sf+F?X!>;mD*z dv_VKK*Es_z_u`c~tq&(+t#}kb8jmpjH+4c- zZpX2MPpFkI`q0iCTwlKWTpATPYA?=sgz2}a6T)&k*10i*Pt2b;qCaJcoH6H4k&p3- zQ5dutA4{Kf&MI%^VcMcjx?;(T82(~e#Xf6voSGnCw`|6s*$-1WsVF-Xm8mXlZ^xU%6KXo%*XGpo;9T8S zB!~2dat^3dIt7tm&mf~Z8;CHC6@2=4S3M2Rcbs#(GCHA(s!^0GKny$8j(?cLsM=Wj zFqQ7n96HKu@@tfN9)$ uKqRHP2Isp@$&agM zjQ+)-PJ-=ZR?coG`r$7+HRMY{D6wI^UEwhYjFPV3@=Gz*F$-pAoBbk^mjT{y7z#zPMo4H z>&>Tv*I#9vpQg7+`HaE&movJ6eA!ZF5LuL4ufgYMG5i-$q_ev{XL@k5RwhL|Av{mS z(xlGK(kxq99F2R{`PQb4bP-CvCq-31$`)ed6_9r2?4d5^qfFeyF-`O++YQqAmtFoF zY+WQWEoXF81r^9BI @m znr~q$F5;9WbuE|0?YCgYhm~~(n0j`G9c0Iv^b{19CDJ9Graw%lpW_+E>IyK$>vv$r zporIFz({XUL4~3Tv?*IMS(ABPTQcf*&2fnzqc?36^JgVo7}8-ETbQ<}TU>&TEETV2 z*!O& @OgcxpL^@e zzu1|qi#>KO09jYp9_sT1pPlx@WJ9*<5(SBVFf6aRC- X#Du9Hi{xZZ%!)6zLI?wUq2ucb@h))2L|h=@&2IpyUZ6^YH7oP5AlJYbf|k zw@TDro0&&}IX0ofn&p1%nf)+5MO{+x*(r#W&KR;Aqvpfw Kg&$9Ht6n>%Q~Nd;5Gz{CXM`DO?52IbER_s<39!60!x1N0>h8_$VLN+i{CJ z>53)#eCdxs?}17Ye+8~DMPv6$#yLfSpmlPXD) lAz{05zUrs&iw@ys`=CUBGc*&aN%nKo|P5UP&9jFx5E&D_ii)96tAR@&*mF zBGc1uaMnfSHi~Tph<2f*r7?oYDBZHvb;dj7hoZ3f#Av4v1r *P|6^)?TFQ1>P%u+tZ<1|wM89Rs!w~zE5sOz2Md@iTA zT1}NbI+SiqZ(2)@0?61w >>YHe?0Lw1} zyGryZxjq`3c!qDp^asHDVK4cG@#uVFS Ob^@}Wl=p(>Cy5y7mm 9ak%UPm8X`4M2~wE2m46{~V%NA08@360~&H6|FnZFqgdIC6A>XuK-tpmF+r` zM0tZ!{a)rFpiRkh((Zv(q}ze6Sbqgqnb!b60LDP$rOrDhsWWut>#gJjxB)1*)7(^s zXsJF0_JL=>e$Y1(dtkzjnoq$M0WRZs2xuNKC Date: Thu, 25 Oct 2018 21:45:27 +0300 Subject: [PATCH 15/69] doh! --- Adamant/Services/KeychainStore.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Adamant/Services/KeychainStore.swift b/Adamant/Services/KeychainStore.swift index dcc16d9c6..a184d8379 100644 --- a/Adamant/Services/KeychainStore.swift +++ b/Adamant/Services/KeychainStore.swift @@ -15,7 +15,7 @@ class KeychainStore: SecuredStore { // For AppStore builds, we use a real password. // See keychain-toAppstore.sh & keychain-toDebug.sh scripts. They runs automaticatlly for Release builds. - private let 🍩 = "standard-berkeley-silt-excavate-sprain-platter-flatboat-jockey-sisal-catapult" + private let 🍩 = "debug" func get(_ key: String) -> String? { if let rawData = keychain[key], From 2dffcd434c9d52b5964dc69afd426505db689d3a Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Thu, 25 Oct 2018 23:55:55 +0300 Subject: [PATCH 16/69] Improved WalletServiceBase for service state Better V12 notification --- .../Assets/l18n/de.lproj/Localizable.strings | 5 +- .../Assets/l18n/en.lproj/Localizable.strings | 5 +- .../Assets/l18n/ru.lproj/Localizable.strings | 5 +- Adamant/ServiceProtocols/AccountService.swift | 1 + Adamant/Services/AdamantAccountService.swift | 26 ++++- .../Account/AccountViewController.swift | 9 +- .../Wallets/Adamant/AdmWalletService.swift | 3 +- .../Wallets/Ethereum/EthWalletService.swift | 65 ++++++++--- Adamant/Wallets/Lisk/LskWalletService.swift | 1 + Adamant/Wallets/WalletService.swift | 11 +- .../Wallets/WalletViewControllerBase.swift | 104 ++++++++++++------ Adamant/Wallets/WalletViewControllerBase.xib | 26 +++++ 12 files changed, 190 insertions(+), 71 deletions(-) diff --git a/Adamant/Assets/l18n/de.lproj/Localizable.strings b/Adamant/Assets/l18n/de.lproj/Localizable.strings index 4851dd89a..d2bac6522 100755 --- a/Adamant/Assets/l18n/de.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/de.lproj/Localizable.strings @@ -80,7 +80,10 @@ "AccountService.update.v12.title" = "Version 1.2 update: Cryptowallets"; /* AccountService: Alert message. Changes in version 1.2, notify user that he needs to relogin to initiate eth & lsk wallets */ -"AccountService.update.v12.message" = "You need to relogin to initiate Ethereum and Lisk wallets"; +"AccountService.update.v12.message" = "You need to relogin to initiate Ethereum wallet"; + +/* AccountService: User must relogin into app to initiate wallets */ +"AccountService.reloginToInitiateWallets" = "Relogin to initiate wallet"; /* Login: user typed in invalid passphrase */ "AccountServiceError.InvalidPassphrase" = "Falsche Passphrase"; diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings index b2e71166c..617b65205 100755 --- a/Adamant/Assets/l18n/en.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings @@ -80,7 +80,10 @@ "AccountService.update.v12.title" = "Version 1.2 update: Cryptowallets"; /* AccountService: Alert message. Changes in version 1.2, notify user that he needs to relogin to initiate eth & lsk wallets */ -"AccountService.update.v12.message" = "You need to relogin to initiate Ethereum and Lisk wallets"; +"AccountService.update.v12.message" = "You need to relogin to initiate Ethereum wallet"; + +/* AccountService: User must relogin into app to initiate wallets */ +"AccountService.reloginToInitiateWallets" = "Relogin to initiate wallet"; /* Login: user typed in invalid passphrase */ "AccountServiceError.InvalidPassphrase" = "Wrong passphrase"; diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings index 0b8f71e76..8a5f47a1e 100644 --- a/Adamant/Assets/l18n/ru.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/ru.lproj/Localizable.strings @@ -80,7 +80,10 @@ "AccountService.update.v12.title" = "Новое в версии 1.2: Криптокошельки"; /* AccountService: Alert message. Changes in version 1.2, notify user that he needs to relogin to initiate eth & lsk wallets */ -"AccountService.update.v12.message" = "Необходимо снова зайти в приложение чтобы создать Ethereum и Lisk кошельки"; +"AccountService.update.v12.message" = "Необходимо снова зайти в приложение чтобы создать Ethereum кошелёк"; + +/* AccountService: User must relogin into app to initiate wallets */ +"AccountService.reloginToInitiateWallets" = "Необходимо перезайти в приложение чтобы создать кошелёк"; /* Login: user typed in invalid passphrase */ "AccountServiceError.InvalidPassphrase" = "Неправильный пароль"; diff --git a/Adamant/ServiceProtocols/AccountService.swift b/Adamant/ServiceProtocols/AccountService.swift index b501649bf..f3141639b 100644 --- a/Adamant/ServiceProtocols/AccountService.swift +++ b/Adamant/ServiceProtocols/AccountService.swift @@ -47,6 +47,7 @@ extension String.adamantLocalized { struct accountService { static let updateAlertTitleV12 = NSLocalizedString("AccountService.update.v12.title", comment: "AccountService: Alert title. Changes in version 1.2") static let updateAlertMessageV12 = NSLocalizedString("AccountService.update.v12.message", comment: "AccountService: Alert message. Changes in version 1.2, notify user that he needs to relogin to initiate eth & lsk wallets") + static let reloginToInitiateWallets = NSLocalizedString("AccountService.reloginToInitiateWallets", comment: "AccountService: User must relogin into app to initiate wallets") } } diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift index d964de469..c6704d711 100644 --- a/Adamant/Services/AdamantAccountService.swift +++ b/Adamant/Services/AdamantAccountService.swift @@ -155,6 +155,7 @@ extension AdamantAccountService { securedStore.remove(.privateKey) securedStore.remove(.useBiometry) securedStore.remove(.passphrase) + securedStore.remove(.showedV12) hasStayInAccount = false NotificationCenter.default.post(name: Notification.Name.AdamantAccountService.stayInChanged, object: self, userInfo: [AdamantUserInfoKey.AccountService.newStayInState : false]) notificationsService.setNotificationsMode(.disabled, completion: nil) @@ -326,12 +327,26 @@ extension AdamantAccountService { } if let keypair = getSavedKeypair() { - loginWith(keypair: keypair) { result in + loginWith(keypair: keypair) { [weak self] result in switch result { case .success(let account, _): - completion(.success(account: account, - alert: (title: String.adamantLocalized.accountService.updateAlertTitleV12, - message: String.adamantLocalized.accountService.updateAlertMessageV12))) + + let alert: (title: String, message: String)? + if self?.securedStore.get(.showedV12) != nil { + alert = nil + } else { + self?.securedStore.set("1", for: .showedV12) + alert = (title: String.adamantLocalized.accountService.updateAlertTitleV12, + message: String.adamantLocalized.accountService.updateAlertMessageV12) + } + + completion(.success(account: account, alert: alert)) + + if let wallets = self?.wallets { + for case let wallet as InitiatedWithPassphraseService in wallets { + wallet.setInitiationFailed(reason: String.adamantLocalized.accountService.reloginToInitiateWallets) + } + } default: completion(result) @@ -434,6 +449,7 @@ extension StoreKey { static let pin = "accountService.pin" static let useBiometry = "accountService.useBiometry" static let passphrase = "accountService.passphrase" + static let showedV12 = "accountService.showedV12" private init() {} } @@ -445,6 +461,7 @@ fileprivate enum Key { case pin case useBiometry case passphrase + case showedV12 var stringValue: String { switch self { @@ -453,6 +470,7 @@ fileprivate enum Key { case .pin: return StoreKey.accountService.pin case .useBiometry: return StoreKey.accountService.useBiometry case .passphrase: return StoreKey.accountService.passphrase + case .showedV12: return StoreKey.accountService.showedV12 } } } diff --git a/Adamant/Stories/Account/AccountViewController.swift b/Adamant/Stories/Account/AccountViewController.swift index a216acc8c..b953c7614 100644 --- a/Adamant/Stories/Account/AccountViewController.swift +++ b/Adamant/Stories/Account/AccountViewController.swift @@ -452,14 +452,7 @@ extension AccountViewController: PagingViewControllerDataSource, PagingViewContr } func pagingViewController (_ pagingViewController: PagingViewController , viewControllerForIndex index: Int) -> UIViewController { - let service = accountService.wallets[index] - let viewController = service.walletViewController.viewController - - if let vcBase = viewController as? WalletViewControllerBase { - vcBase.isInitiated = service.wallet != nil - } - - return viewController + return accountService.wallets[index].walletViewController.viewController } func pagingViewController (_ pagingViewController: PagingViewController , pagingItemForIndex index: Int) -> T { diff --git a/Adamant/Wallets/Adamant/AdmWalletService.swift b/Adamant/Wallets/Adamant/AdmWalletService.swift index 25bb70c70..7ddd3a3c2 100644 --- a/Adamant/Wallets/Adamant/AdmWalletService.swift +++ b/Adamant/Wallets/Adamant/AdmWalletService.swift @@ -31,6 +31,7 @@ class AdmWalletService: NSObject, WalletService { let walletUpdatedNotification = Notification.Name("adamant.admWallet.updated") let serviceEnabledChanged = Notification.Name("adamant.admWallet.enabledChanged") let transactionFeeUpdated = Notification.Name("adamant.admWallet.feeUpdated") + let serviceStateChanged = Notification.Name("adamant.admWallet.stateChanged") // MARK: RichMessageProvider properties static let richMessageType = "adm_transaction" // not used @@ -53,7 +54,7 @@ class AdmWalletService: NSObject, WalletService { private var transfersController: NSFetchedResultsController ? // MARK: - State - private (set) var state: WalletServiceState = .notInitiated + private (set) var state: WalletServiceState = .upToDate private (set) var wallet: WalletAccount? = nil diff --git a/Adamant/Wallets/Ethereum/EthWalletService.swift b/Adamant/Wallets/Ethereum/EthWalletService.swift index 4350414a2..059f10056 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService.swift @@ -63,6 +63,7 @@ class EthWalletService: WalletService { let walletUpdatedNotification = Notification.Name("adamant.ethWallet.walletUpdated") let serviceEnabledChanged = Notification.Name("adamant.ethWallet.enabledChanged") let transactionFeeUpdated = Notification.Name("adamant.ethWallet.feeUpdated") + let serviceStateChanged = Notification.Name("adamant.ethWallet.stateChanged") // MARK: RichMessageProvider properties @@ -93,7 +94,22 @@ class EthWalletService: WalletService { private var initialBalanceCheck = false // MARK: - State - private (set) var state: WalletServiceState = .notInitiated + private (set) var state: WalletServiceState = .notInitiated + + private func setState(_ newState: WalletServiceState, silent: Bool = false) { + guard newState != state else { + return + } + + state = newState + + if !silent { + NotificationCenter.default.post(name: serviceStateChanged, + object: self, + userInfo: [AdamantUserInfoKey.WalletService.walletState: state]) + } + } + private (set) var ethWallet: EthWallet? = nil var wallet: WalletAccount? { return ethWallet } @@ -139,14 +155,14 @@ class EthWalletService: WalletService { stateSemaphore.wait() switch state { - case .notInitiated, .updating: + case .notInitiated, .updating, .initiationFailed(_): return - case .initiated, .updated: + case .upToDate: break } - state = .updating + setState(.updating) getBalance(forAddress: wallet.ethAddress) { [weak self] result in if let stateSemaphore = self?.stateSemaphore { @@ -154,7 +170,6 @@ class EthWalletService: WalletService { stateSemaphore.signal() } stateSemaphore.wait() - self?.state = .updated } switch result { @@ -179,6 +194,8 @@ class EthWalletService: WalletService { case .failure(let error): self?.dialogService.showRichError(error: error) } + + self?.setState(.upToDate) } getGasPrices { [weak self] result in @@ -252,7 +269,7 @@ class EthWalletService: WalletService { // MARK: - WalletInitiatedWithPassphrase extension EthWalletService: InitiatedWithPassphraseService { - func initWallet(withPassphrase passphrase: String, completion: @escaping (WalletServiceResult ) -> Void) { + func initWallet(withPassphrase passphrase: String, completion: @escaping (WalletServiceResult ) -> Void) { guard let adamant = accountService.account else { completion(.failure(error: .notLogged)) return @@ -260,8 +277,8 @@ extension EthWalletService: InitiatedWithPassphraseService { // MARK: 1. Prepare stateSemaphore.wait() - - state = .notInitiated + + setState(.notInitiated) if enabled { enabled = false @@ -295,7 +312,6 @@ extension EthWalletService: InitiatedWithPassphraseService { // MARK: 3. Update let eWallet = EthWallet(address: ethAddress.address, ethAddress: ethAddress, keystore: keystore) ethWallet = eWallet - state = .initiated if !enabled { enabled = true @@ -306,17 +322,22 @@ extension EthWalletService: InitiatedWithPassphraseService { // MARK: 4. Save into KVS getWalletAddress(byAdamantAddress: adamant.address) { [weak self] result in + guard let service = self else { + return + } + switch result { case .success(let address): // ETH already saved if address != ethAddress.address { - self?.save(ethAddress: ethAddress.address) { result in - self?.kvsSaveCompletionRecursion(ethAddress: ethAddress.address, result: result) + service.save(ethAddress: ethAddress.address) { result in + service.kvsSaveCompletionRecursion(ethAddress: ethAddress.address, result: result) } } - self?.initialBalanceCheck = true - self?.update() + service.initialBalanceCheck = true + service.setState(.upToDate, silent: true) + service.update() completion(.success(result: eWallet)) @@ -324,23 +345,31 @@ extension EthWalletService: InitiatedWithPassphraseService { switch error { case .walletNotInitiated: // Show '0' without waiting for balance update - if let notification = self?.walletUpdatedNotification, let wallet = self?.ethWallet { - NotificationCenter.default.post(name: notification, object: self, userInfo: [AdamantUserInfoKey.WalletService.wallet: wallet]) + if let wallet = service.ethWallet { + NotificationCenter.default.post(name: service.walletUpdatedNotification, object: service, userInfo: [AdamantUserInfoKey.WalletService.wallet: wallet]) } - self?.save(ethAddress: ethAddress.address) { result in - self?.kvsSaveCompletionRecursion(ethAddress: ethAddress.address, result: result) + service.save(ethAddress: ethAddress.address) { result in + service.kvsSaveCompletionRecursion(ethAddress: ethAddress.address, result: result) } - + service.setState(.upToDate) completion(.success(result: eWallet)) default: + service.setState(.upToDate) completion(.failure(error: error)) } } } } + func setInitiationFailed(reason: String) { + stateSemaphore.wait() + setState(.initiationFailed(reason: reason)) + ethWallet = nil + stateSemaphore.signal() + } + /// New accounts doesn't have enought money to save KVS. We need to wait for balance update, and then - retry save private func kvsSaveCompletionRecursion(ethAddress: String, result: WalletServiceSimpleResult) { diff --git a/Adamant/Wallets/Lisk/LskWalletService.swift b/Adamant/Wallets/Lisk/LskWalletService.swift index 1947b9658..c3794b962 100644 --- a/Adamant/Wallets/Lisk/LskWalletService.swift +++ b/Adamant/Wallets/Lisk/LskWalletService.swift @@ -14,6 +14,7 @@ class LskWalletService: WalletService { let walletUpdatedNotification = Notification.Name("lsk.update") let serviceEnabledChanged = Notification.Name("lsk.enabledChanged") + let serviceStateChanged = Notification.Name("lsk.stateChanged") // MARK: - Constants let addressRegex = try! NSRegularExpression(pattern: "^([0-9]{2,22})L$", options: []) diff --git a/Adamant/Wallets/WalletService.swift b/Adamant/Wallets/WalletService.swift index 35a0dcf87..2a878981b 100644 --- a/Adamant/Wallets/WalletService.swift +++ b/Adamant/Wallets/WalletService.swift @@ -10,8 +10,8 @@ import Foundation import UIKit import Swinject -enum WalletServiceState { - case notInitiated, initiated, updated, updating +enum WalletServiceState: Equatable { + case notInitiated, updating, upToDate, initiationFailed(reason: String) } enum WalletServiceSimpleResult { @@ -124,7 +124,8 @@ extension ApiServiceError { extension AdamantUserInfoKey { struct WalletService { static let wallet = "Adamant.WalletService.wallet" - + static let walletState = "Adamant.WalletService.walletState" + private init() {} } } @@ -161,6 +162,9 @@ protocol WalletService: class { /// Enabled state changed var serviceEnabledChanged: Notification.Name { get } + /// State changed + var serviceStateChanged: Notification.Name { get } + // MARK: State var wallet: WalletAccount? { get } var state: WalletServiceState { get } @@ -183,6 +187,7 @@ protocol SwinjectDependentService: WalletService { protocol InitiatedWithPassphraseService: WalletService { func initWallet(withPassphrase: String, completion: @escaping (WalletServiceResult ) -> Void) + func setInitiationFailed(reason: String) } protocol WalletServiceWithTransfers: WalletService { diff --git a/Adamant/Wallets/WalletViewControllerBase.swift b/Adamant/Wallets/WalletViewControllerBase.swift index 987ddc91a..2373a2af6 100644 --- a/Adamant/Wallets/WalletViewControllerBase.swift +++ b/Adamant/Wallets/WalletViewControllerBase.swift @@ -53,43 +53,23 @@ class WalletViewControllerBase: FormViewController, WalletViewController { var service: WalletService? - var isInitiated: Bool = true { - didSet { - guard viewDidInitialLoad else { - return - } - - if isInitiated { - initiatingActivityIndicator.stopAnimating() - tableView.isHidden = false - } else { - initiatingActivityIndicator.startAnimating() - tableView.isHidden = true - } - } - } - - // If we try to set isInitiated before view did load from nib, we will get a nil in IBOutlets. - private var viewDidInitialLoad = false - // MARK: - IBOutlets @IBOutlet weak var walletTitleLabel: UILabel! @IBOutlet weak var initiatingActivityIndicator: UIActivityIndicatorView! - + // MARK: Error view + + @IBOutlet weak var errorView: UIView! + @IBOutlet weak var errorImageView: UIImageView! + @IBOutlet weak var errorLabel: UILabel! + // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() - viewDidInitialLoad = true - if !isInitiated { - initiatingActivityIndicator.startAnimating() - tableView.isHidden = true - } - - let section = Section() + let section = Section() // MARK: Address let addressRow = LabelRow() { @@ -199,16 +179,12 @@ class WalletViewControllerBase: FormViewController, WalletViewController { // MARK: Notification if let service = service { - let callback = { [weak self] (notification: Notification) in + // MARK: Wallet updated + let walletUpdatedCallback = { [weak self] (notification: Notification) in guard let wallet = notification.userInfo?[AdamantUserInfoKey.WalletService.wallet] as? WalletAccount else { - self?.isInitiated = false return } - if let isInitiated = self?.isInitiated, !isInitiated { - self?.isInitiated = true - } - if let row: AlertLabelRow = self?.form.rowBy(tag: BaseRows.balance.tag) { let symbol = type(of: service).currencySymbol row.value = AdamantBalanceFormat.full.format(wallet.balance, withCurrencySymbol: symbol) @@ -230,8 +206,34 @@ class WalletViewControllerBase: FormViewController, WalletViewController { NotificationCenter.default.addObserver(forName: service.walletUpdatedNotification, object: service, queue: OperationQueue.main, - using: callback) + using: walletUpdatedCallback) + + // MARK: Wallet state updated + let stateUpdatedCallback = { [weak self] (notification: Notification) in + guard let newState = notification.userInfo?[AdamantUserInfoKey.WalletService.walletState] as? WalletServiceState else { + return + } + + self?.setUiToWalletServiceState(newState) + } + + NotificationCenter.default.addObserver(forName: service.serviceStateChanged, + object: service, + queue: OperationQueue.main, + using: stateUpdatedCallback) } + + if let state = service?.state { + switch state { + case .updating: + setUiToWalletServiceState(.notInitiated) + + default: + setUiToWalletServiceState(state) + } + } else { + setUiToWalletServiceState(.notInitiated) + } } override func viewWillAppear(_ animated: Bool) { @@ -256,6 +258,40 @@ class WalletViewControllerBase: FormViewController, WalletViewController { func sendRowLocalizedLabel() -> String { return BaseRows.send.localized } + + + // MARK: - Other + + private var currentUiState: WalletServiceState = .upToDate + + func setUiToWalletServiceState(_ state: WalletServiceState) { + guard currentUiState != state else { + return + } + + switch state { + case .updating: + break + + case .upToDate: + initiatingActivityIndicator.stopAnimating() + tableView.isHidden = false + errorView.isHidden = true + + case .notInitiated: + initiatingActivityIndicator.startAnimating() + tableView.isHidden = true + errorView.isHidden = true + + case .initiationFailed(let reason): + initiatingActivityIndicator.stopAnimating() + tableView.isHidden = true + errorView.isHidden = false + errorLabel.text = reason + } + + currentUiState = state + } } diff --git a/Adamant/Wallets/WalletViewControllerBase.xib b/Adamant/Wallets/WalletViewControllerBase.xib index 8df23e908..3f143b8a1 100644 --- a/Adamant/Wallets/WalletViewControllerBase.xib +++ b/Adamant/Wallets/WalletViewControllerBase.xib @@ -12,6 +12,9 @@ + + + + @@ -33,6 +36,23 @@ + + + + ++ + ++ + ++ @@ -40,11 +60,14 @@ @@ -53,4 +76,7 @@ + + + + + Date: Sat, 27 Oct 2018 22:21:34 +0300 Subject: [PATCH 17/69] Transfer ETH fixed & improved. --- Adamant.xcodeproj/project.pbxproj | 4 + Adamant/Models/EthTransaction.swift | 10 ++- Adamant/Models/RichMessage.swift | 2 +- Adamant/Models/SimpleTransactionDetails.swift | 33 ++++++++ .../TransferCollectionViewCell.swift | 11 ++- .../Chats/ChatViewController+MessageKit.swift | 75 ++++++++++++++++++- .../Stories/Chats/ChatViewController.swift | 14 +++- .../Chats/ComplexTransferViewController.swift | 2 +- Adamant/Stories/Chats/CustomCellDeleage.swift | 12 +++ .../AdmTransactionDetailsViewController.swift | 6 +- .../Ethereum/EthTransferViewController.swift | 26 +++---- ...EthWalletService+RichMessageProvider.swift | 45 ++++++++--- ...e+RichMessageProviderWithStatusCheck.swift | 30 +++++++- .../Wallets/Ethereum/EthWalletService.swift | 36 +++++++-- .../Wallets/TransferViewControllerBase.swift | 2 +- .../Wallets/WalletViewControllerBase.swift | 2 +- 16 files changed, 257 insertions(+), 53 deletions(-) create mode 100644 Adamant/Models/SimpleTransactionDetails.swift diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index a42f3c531..d8f29005d 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -249,6 +249,7 @@ 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 */; }; + E9FCA1E6218334C00005E83D /* SimpleTransactionDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FCA1E5218334C00005E83D /* SimpleTransactionDetails.swift */; }; E9FEECA421413659007DD7C8 /* RichMessageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FEECA321413659007DD7C8 /* RichMessageProvider.swift */; }; E9FEECA62143C300007DD7C8 /* EthWalletService+RichMessageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FEECA52143C300007DD7C8 /* EthWalletService+RichMessageProvider.swift */; }; E9FEECA92143C371007DD7C8 /* TransferCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FEECA72143C371007DD7C8 /* TransferCollectionViewCell.swift */; }; @@ -524,6 +525,7 @@ 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 = " "; }; + E9FCA1E5218334C00005E83D /* SimpleTransactionDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleTransactionDetails.swift; sourceTree = " "; }; E9FEECA321413659007DD7C8 /* RichMessageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RichMessageProvider.swift; sourceTree = " "; }; E9FEECA52143C300007DD7C8 /* EthWalletService+RichMessageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EthWalletService+RichMessageProvider.swift"; sourceTree = " "; }; E9FEECA72143C371007DD7C8 /* TransferCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransferCollectionViewCell.swift; sourceTree = " "; }; @@ -701,6 +703,7 @@ E940086A2114A70600CD2D67 /* LskAccount.swift */, E993302321369B8A00CD5200 /* RichMessage.swift */, E971591921681D6900A5F904 /* TransactionStatus.swift */, + E9FCA1E5218334C00005E83D /* SimpleTransactionDetails.swift */, ); path = Models; sourceTree = " "; @@ -1386,6 +1389,7 @@ E9150B9D2066DA210065A985 /* Chatroom+CoreDataClass.swift in Sources */, E9942B80203C058C00C163AF /* QRGeneratorViewController.swift in Sources */, E921597520611A6A0000CA5C /* AdamantReachability.swift in Sources */, + E9FCA1E6218334C00005E83D /* SimpleTransactionDetails.swift in Sources */, 64A223DA20F7A14B005157CB /* AdamantLskApiService.swift in Sources */, E9150BA22066DA210065A985 /* ChatTransaction+CoreDataProperties.swift in Sources */, 6455E9F321075D8000B2E94C /* AdamantAddressBookService.swift in Sources */, diff --git a/Adamant/Models/EthTransaction.swift b/Adamant/Models/EthTransaction.swift index 3a74bf7af..663cae5fe 100644 --- a/Adamant/Models/EthTransaction.swift +++ b/Adamant/Models/EthTransaction.swift @@ -47,7 +47,7 @@ struct EthTransaction { let value: Decimal let from: String let to: String - let gasUsed: Decimal + let gasUsed: Decimal? let gasPrice: Decimal let confirmations: String? let isError: Bool @@ -143,6 +143,10 @@ extension EthTransaction: TransactionDetails { var blockValue: String? { return blockNumber} var feeValue: Decimal? { + guard let gasUsed = gasUsed else { + return nil + } + return gasPrice * gasUsed } @@ -153,13 +157,13 @@ extension EthTransaction: TransactionDetails { // MARK: - From EthereumTransaction extension EthereumTransaction { - func asEthTransaction(date: Date?, gasUsed: BigUInt, blockNumber: String?, confirmations: String?, receiptStatus: TransactionReceipt.TXStatus, isOutgoing: Bool) -> EthTransaction { + func asEthTransaction(date: Date?, gasUsed: BigUInt?, blockNumber: String?, confirmations: String?, receiptStatus: TransactionReceipt.TXStatus, isOutgoing: Bool) -> EthTransaction { return EthTransaction(date: date, hash: txhash ?? "", value: value.asDecimal(exponent: EthWalletService.currencyExponent), from: sender?.address ?? "", to: to.address, - gasUsed: gasUsed.asDecimal(exponent: 0), + gasUsed: gasUsed?.asDecimal(exponent: 0), gasPrice: gasPrice.asDecimal(exponent: EthWalletService.currencyExponent), confirmations: confirmations, isError: receiptStatus != .failed, diff --git a/Adamant/Models/RichMessage.swift b/Adamant/Models/RichMessage.swift index 2eb53ff0e..e8a2a83b3 100644 --- a/Adamant/Models/RichMessage.swift +++ b/Adamant/Models/RichMessage.swift @@ -130,7 +130,7 @@ extension RichMessageTransfer { f.roundingMode = .floor f.decimalSeparator = "." f.minimumFractionDigits = 0 - f.maximumFractionDigits = 18 + f.maximumFractionDigits = 12 // 18 is too low, 0.007 for example will serialize as 0.007000000000000001 return f }() diff --git a/Adamant/Models/SimpleTransactionDetails.swift b/Adamant/Models/SimpleTransactionDetails.swift new file mode 100644 index 000000000..e8cf5ef82 --- /dev/null +++ b/Adamant/Models/SimpleTransactionDetails.swift @@ -0,0 +1,33 @@ +// +// SimpleTransactionDetails.swift +// Adamant +// +// Created by Anokhov Pavel on 26.10.2018. +// Copyright © 2018 Adamant. All rights reserved. +// + +import Foundation + +struct SimpleTransactionDetails: TransactionDetails { + var id: String? + + var senderAddress: String + + var recipientAddress: String + + var dateValue: Date? + + var amountValue: Decimal + + var feeValue: Decimal? + + var confirmationsValue: String? + + var blockValue: String? + + var isOutgoing: Bool + + var transactionStatus: TransactionStatus? + + +} diff --git a/Adamant/SharedViews/TransferCollectionViewCell.swift b/Adamant/SharedViews/TransferCollectionViewCell.swift index 89cdaf4fa..4bfb8cce5 100644 --- a/Adamant/SharedViews/TransferCollectionViewCell.swift +++ b/Adamant/SharedViews/TransferCollectionViewCell.swift @@ -9,7 +9,7 @@ import UIKit //import MessageKit -class TransferCollectionViewCell: UICollectionViewCell, ChatCell, TapRecognizerCustomCell { +class TransferCollectionViewCell: UICollectionViewCell, ChatCell, TapRecognizerTransferCell { @IBOutlet weak var sentLabel: UILabel! @IBOutlet weak var amountLabel: UILabel! @IBOutlet weak var currencySymbolLabel: UILabel! @@ -28,7 +28,7 @@ class TransferCollectionViewCell: UICollectionViewCell, ChatCell, TapRecognizerC @IBOutlet weak var statusLeadingConstraint: NSLayoutConstraint? @IBOutlet weak var statusTrailingConstraint: NSLayoutConstraint? - weak var delegate: CustomCellDelegate? = nil + weak var delegate: TransferCellDelegate? = nil override func awakeFromNib() { super.awakeFromNib() @@ -36,12 +36,17 @@ class TransferCollectionViewCell: UICollectionViewCell, ChatCell, TapRecognizerC bubbleView.layer.cornerRadius = 16.0 bubbleView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTap))) + statusView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapStatus))) } // MARK: - Tap @objc func didTap(sender: UITapGestureRecognizer) { - delegate?.didTapCustomCell(self) + delegate?.didTapTransferCell(self) + } + + @objc func didTapStatus(sender: UITapGestureRecognizer) { + delegate?.didTapTransferCellStatus(self) } // MARK: - Status diff --git a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift index 44231db49..b80b5a802 100644 --- a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift +++ b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift @@ -143,14 +143,34 @@ extension ChatViewController: MessagesDataSource { chatCell.bubbleBackgroundColor = bgColor } - if let customCell = cell as? TapRecognizerCustomCell { - customCell.delegate = self + // MARK: Delegates + switch cell { + case let tapCell as TapRecognizerCustomCell: + tapCell.delegate = self + + case let transferCell as TapRecognizerTransferCell: + transferCell.delegate = self + + default: + break } + // MARK: Rich transfer statuses if let richTransaction = message as? RichMessageTransaction, (richTransaction.transactionStatus == nil || richTransaction.transactionStatus == .notInitiated), let updater = provider as? RichMessageProviderWithStatusCheck { - updateStatus(for: richTransaction, provider: updater) + + /* + Сообщения-отчёты об отправленных средствах создаются раньше, чем на эфирных нодах появляется сама транзакция перевода (по ТЗ). + Проблема - как только сообщение появляется в чате, мы запрашиваем у эфирной ноды статус транзакции которую ещё не отправили - нода возвращает ошибку. + Решение - если сообщение появилось только что - обновим статус этой транзакции с 'некоторой' задержкой. + 🤷🏻♂️ + */ + if let date = richTransaction.date, date.timeIntervalSinceNow > -2.0 { + updateStatus(for: richTransaction, provider: updater, delay: 5.0) + } else { + updateStatus(for: richTransaction, provider: updater) + } } return cell @@ -344,6 +364,55 @@ extension ChatViewController: CustomCellDelegate { } } +// MARK: - TransferCollectionViewCellDelegate +extension ChatViewController: TransferCellDelegate { + func didTapTransferCell(_ cell: TapRecognizerTransferCell) { + guard let c = cell as? UICollectionViewCell, + let indexPath = messagesCollectionView.indexPath(for: c), + let transaction = chatController?.object(at: IndexPath(row: indexPath.section, section: 0)) else { + return + } + + switch transaction { + case let transfer as TransferTransaction: + guard let provider = richMessageProviders[AdmWalletService.richMessageType] as? AdmWalletService else { + break + } + + provider.richMessageTapped(for: transfer, at: indexPath, in: self) + + case let richTransaction as RichMessageTransaction: + guard let type = richTransaction.richType, let provider = richMessageProviders[type] else { + break + } + + provider.richMessageTapped(for: richTransaction, at: indexPath, in: self) + + default: + return + } + } + + func didTapTransferCellStatus(_ cell: TapRecognizerTransferCell) { + guard let c = cell as? UICollectionViewCell, + let indexPath = messagesCollectionView.indexPath(for: c), + let transaction = chatController?.object(at: IndexPath(row: indexPath.section, section: 0)) as? RichMessageTransaction else { + return + } + + guard transaction.transactionStatus != TransactionStatus.updating else { + return + } + + guard let type = transaction.richType, + let provider = richMessageProviders[type] as? RichMessageProviderWithStatusCheck else { + return + } + + updateStatus(for: transaction, provider: provider, delay: 1) + } +} + // MARK: - MessagesLayoutDelegate extension ChatViewController: MessagesLayoutDelegate { func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat { diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 2f85e08ed..7017074d5 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -97,7 +97,7 @@ class ChatViewController: MessagesViewController { return InputBarButtonItem() .configure { $0.setSize(CGSize(width: ChatViewController.attachmentButtonSize, height: ChatViewController.attachmentButtonSize), animated: false) - $0.image = #imageLiteral(resourceName: "SendMoney") + $0.image = #imageLiteral(resourceName: "Attachment") $0.tintColor = UIColor.adamant.primary }.onTouchUpInside { [weak self] _ in guard let vc = self?.router.get(scene: AdamantScene.Chats.complexTransfer) as? ComplexTransferViewController else { @@ -525,7 +525,7 @@ extension ChatViewController: NSFetchedResultsControllerDelegate { } extension ChatViewController: TransferViewControllerDelegate, ComplexTransferViewControllerDelegate { - func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails, detailsViewController: UIViewController?) { + func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails?, detailsViewController: UIViewController?) { dismissTransferViewController(andPresent: detailsViewController) } @@ -556,14 +556,20 @@ extension ChatViewController: TransferViewControllerDelegate, ComplexTransferVie // MARK: - RichTransfers status update extension ChatViewController { - func updateStatus(for transaction: RichMessageTransaction, provider: RichMessageProviderWithStatusCheck) { + func updateStatus(for transaction: RichMessageTransaction, provider: RichMessageProviderWithStatusCheck, delay: TimeInterval? = nil) { transaction.transactionStatus = .updating let operation = StatusUpdateProcedure(parentContext: stack.container.viewContext, objectId: transaction.objectID, provider: provider) - richStatusOperationQueue.addOperation(operation) + if let delay = delay { + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in + self?.richStatusOperationQueue.addOperation(operation) + } + } else { + richStatusOperationQueue.addOperation(operation) + } } } diff --git a/Adamant/Stories/Chats/ComplexTransferViewController.swift b/Adamant/Stories/Chats/ComplexTransferViewController.swift index e11eebc71..47cfd065a 100644 --- a/Adamant/Stories/Chats/ComplexTransferViewController.swift +++ b/Adamant/Stories/Chats/ComplexTransferViewController.swift @@ -132,7 +132,7 @@ extension ComplexTransferViewController: PagingViewControllerDataSource { } extension ComplexTransferViewController: TransferViewControllerDelegate { - func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails, detailsViewController: UIViewController?) { + func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails?, detailsViewController: UIViewController?) { transferDelegate?.complexTransferViewController(self, didFinishWithTransfer: transfer, detailsViewController: detailsViewController) } } diff --git a/Adamant/Stories/Chats/CustomCellDeleage.swift b/Adamant/Stories/Chats/CustomCellDeleage.swift index c3950455f..302ef454c 100644 --- a/Adamant/Stories/Chats/CustomCellDeleage.swift +++ b/Adamant/Stories/Chats/CustomCellDeleage.swift @@ -8,6 +8,7 @@ import Foundation +// MARK: - Custom cell protocol TapRecognizerCustomCell: class { /// Must be a weak reference var delegate: CustomCellDelegate? { get set } @@ -16,3 +17,14 @@ protocol TapRecognizerCustomCell: class { protocol CustomCellDelegate: class { func didTapCustomCell(_ cell: TapRecognizerCustomCell) } + +// MARK: - Transfer cell +protocol TapRecognizerTransferCell: class { + // Must be a weak reference + var delegate: TransferCellDelegate? { get set } +} + +protocol TransferCellDelegate: class { + func didTapTransferCell(_ cell: TapRecognizerTransferCell) + func didTapTransferCellStatus(_ cell: TapRecognizerTransferCell) +} diff --git a/Adamant/Wallets/Adamant/AdmTransactionDetailsViewController.swift b/Adamant/Wallets/Adamant/AdmTransactionDetailsViewController.swift index c1b4ae8dc..eb9c8fd4e 100644 --- a/Adamant/Wallets/Adamant/AdmTransactionDetailsViewController.swift +++ b/Adamant/Wallets/Adamant/AdmTransactionDetailsViewController.swift @@ -67,7 +67,11 @@ class AdmTransactionDetailsViewController: TransactionDetailsViewControllerBase // MARK: - Overrides override func explorerUrl(for transaction: TransactionDetails) -> URL? { - return URL(string: "\(AdamantResources.adamantExplorerAddress)\(transaction.id)") + guard let id = transaction.id else { + return nil + } + + return URL(string: "\(AdamantResources.adamantExplorerAddress)\(id)") } func goToChat() { diff --git a/Adamant/Wallets/Ethereum/EthTransferViewController.swift b/Adamant/Wallets/Ethereum/EthTransferViewController.swift index 0ce015ca0..6d8a7ef00 100644 --- a/Adamant/Wallets/Ethereum/EthTransferViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransferViewController.swift @@ -49,16 +49,22 @@ class EthTransferViewController: TransferViewControllerBase { dialogService.showProgress(withMessage: String.adamantLocalized.transfer.transferProcessingMessage, userInteractionEnable: false) service.createTransaction(recipient: recipient, amount: amount, comments: comments) { [weak self] result in + guard let vc = self else { + dialogService.dismissProgress() + dialogService.showError(withMessage: String.adamantLocalized.sharedErrors.unknownError, error: nil) + return + } + switch result { case .success(let transaction): // MARK: 1. Send adm report - if let reportRecipient = self?.admReportRecipient, let hash = transaction.txhash { + if let reportRecipient = vc.admReportRecipient, let hash = transaction.txhash { let payload = RichMessageTransfer(type: EthWalletService.richMessageType, amount: amount, hash: hash, comments: comments) let message = AdamantMessage.richMessage(payload: payload) - self?.chatsProvider.sendMessage(message, recipientId: reportRecipient) { result in + vc.chatsProvider.sendMessage(message, recipientId: reportRecipient) { result in if case .failure(let error) = result { - self?.dialogService.showRichError(error: error) + vc.dialogService.showRichError(error: error) } } } @@ -68,15 +74,10 @@ class EthTransferViewController: TransferViewControllerBase { switch result { case .success(let hash): service.update() - - guard let vc = self else { - break - } - service.getTransaction(by: hash) { result in switch result { case .success(let transaction): - let detailsVc = self?.router.get(scene: AdamantScene.Wallets.Ethereum.transactionDetails) as? EthTransactionDetailsViewController + let detailsVc = vc.router.get(scene: AdamantScene.Wallets.Ethereum.transactionDetails) as? EthTransactionDetailsViewController detailsVc?.transaction = transaction vc.dialogService.showSuccess(withMessage: String.adamantLocalized.transfer.transferSuccess) @@ -84,19 +85,16 @@ class EthTransferViewController: TransferViewControllerBase { case .failure(let error): vc.dialogService.showRichError(error: error) + vc.delegate?.transferViewController(vc, didFinishWithTransfer: nil, detailsViewController: nil) } } case .failure(let error): - self?.dialogService.showRichError(error: error) + vc.dialogService.showRichError(error: error) } } case .failure(let error): - guard let dialogService = self?.dialogService else { - break - } - dialogService.dismissProgress() dialogService.showRichError(error: error) } diff --git a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift index 13314e158..d8b86cd4a 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift @@ -26,20 +26,45 @@ extension EthWalletService: RichMessageProvider { getTransaction(by: hash) { [weak self] result in dialogService.dismissProgress() + guard let vc = self?.router.get(scene: AdamantScene.Wallets.Ethereum.transactionDetails) as? EthTransactionDetailsViewController else { + return + } switch result { - case .success(let transaction): - guard let vc = self?.router.get(scene: AdamantScene.Wallets.Ethereum.transactionDetails) as? EthTransactionDetailsViewController else { - return - } - - vc.transaction = transaction - DispatchQueue.main.async { - chat.navigationController?.pushViewController(vc, animated: true) - } + case .success(let ethTransaction): + vc.transaction = ethTransaction case .failure(let error): - self?.dialogService.showRichError(error: error) + switch error { + case .remoteServiceError: + let amount: Decimal + if let amountRaw = transaction.richContent?[RichContentKeys.transfer.amount], let decimal = Decimal(string: amountRaw) { + amount = decimal + } else { + amount = 0 + } + + let failedTransaction = SimpleTransactionDetails(id: hash, + senderAddress: transaction.senderAddress, + recipientAddress: transaction.recipientAddress, + dateValue: nil, + amountValue: amount, + feeValue: nil, + confirmationsValue: nil, + blockValue: nil, + isOutgoing: transaction.isOutgoing, + transactionStatus: TransactionStatus.failed) + + vc.transaction = failedTransaction + + default: + self?.dialogService.showRichError(error: error) + return + } + } + + DispatchQueue.main.async { + chat.navigationController?.pushViewController(vc, animated: true) } } } diff --git a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProviderWithStatusCheck.swift b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProviderWithStatusCheck.swift index 017c66591..325dc42b6 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProviderWithStatusCheck.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProviderWithStatusCheck.swift @@ -11,12 +11,34 @@ import web3swift extension EthWalletService: RichMessageProviderWithStatusCheck { func statusForTransactionBy(hash: String, completion: @escaping (WalletServiceResult ) -> Void) { - switch web3.eth.getTransactionReceipt(hash) { - case .success(let receipt): + do { + _ = try web3.eth.getTransactionDetailsPromise(hash).wait() + } catch let error as Web3Error { + completion(.failure(error: error.asWalletServiceError())) + return + } catch { + completion(.failure(error: WalletServiceError.internalError(message: "Failed to get transaction", error: error))) + return + } + + do { + let receipt = try web3.eth.getTransactionReceiptPromise(hash).wait() completion(.success(result: receipt.status.asTransactionStatus())) + } catch let error as Web3Error { + let result: WalletServiceResult - case .failure(let error): - completion(.failure(error: error.asWalletServiceError())) + switch error { + // Transaction not delivired yet + case .inputError, .nodeError: + result = .success(result: .pending) + + default: + result = .failure(error: error.asWalletServiceError()) + } + + completion(result) + } catch { + completion(.failure(error: WalletServiceError.internalError(message: "Failed to get transaction", error: error))) } } } diff --git a/Adamant/Wallets/Ethereum/EthWalletService.swift b/Adamant/Wallets/Ethereum/EthWalletService.swift index 059f10056..cf7b51f29 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService.swift @@ -542,18 +542,29 @@ extension EthWalletService { let eth = web3.eth DispatchQueue.global(qos: .utility).async { + let isOutgoing: Bool + let details: web3swift.TransactionDetails + + // MARK: 1. Transaction details do { - // MARK: 1. Transaction's details and receipt - let details = try eth.getTransactionDetailsPromise(hash).wait() - let receipt = try eth.getTransactionReceiptPromise(hash).wait() + details = try eth.getTransactionDetailsPromise(hash).wait() - // MARK: 2. Determine if transaction is outcome or income - let isOutgoing: Bool if let sender = sender { isOutgoing = details.transaction.to.address != sender } else { isOutgoing = false } + } catch let error as Web3Error { + completion(.failure(error: error.asWalletServiceError())) + return + } catch { + completion(.failure(error: WalletServiceError.internalError(message: "Failed to get transaction", error: error))) + return + } + + // MARK: 2. Transaction receipt + do { + let receipt = try eth.getTransactionReceiptPromise(hash).wait() // MARK: 3. Check if transaction is delivered guard receipt.status == .ok, let blockNumber = details.blockNumber else { @@ -570,9 +581,20 @@ extension EthWalletService { let transaction = details.transaction.asEthTransaction(date: block.timestamp, gasUsed: receipt.gasUsed, blockNumber: String(blockNumber), confirmations: String(confirmations), receiptStatus: receipt.status, isOutgoing: isOutgoing) completion(.success(result: transaction)) - } catch let error as Web3Error { - completion(.failure(error: error.asWalletServiceError())) + let result: WalletServiceResult + + switch error { + // Transaction not delivired yet + case .inputError, .nodeError: + let transaction = details.transaction.asEthTransaction(date: nil, gasUsed: nil, blockNumber: nil, confirmations: nil, receiptStatus: TransactionReceipt.TXStatus.notYetProcessed, isOutgoing: isOutgoing) + result = .success(result: transaction) + + default: + result = .failure(error: error.asWalletServiceError()) + } + + completion(result) } catch { completion(.failure(error: WalletServiceError.internalError(message: "Failed to get transaction", error: error))) } diff --git a/Adamant/Wallets/TransferViewControllerBase.swift b/Adamant/Wallets/TransferViewControllerBase.swift index 8f90efac3..829971d39 100644 --- a/Adamant/Wallets/TransferViewControllerBase.swift +++ b/Adamant/Wallets/TransferViewControllerBase.swift @@ -14,7 +14,7 @@ import QRCodeReader // MARK: - Transfer Delegate Protocol protocol TransferViewControllerDelegate: class { - func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails, detailsViewController: UIViewController?) + func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails?, detailsViewController: UIViewController?) } diff --git a/Adamant/Wallets/WalletViewControllerBase.swift b/Adamant/Wallets/WalletViewControllerBase.swift index 2373a2af6..9e7b44dd0 100644 --- a/Adamant/Wallets/WalletViewControllerBase.swift +++ b/Adamant/Wallets/WalletViewControllerBase.swift @@ -296,7 +296,7 @@ class WalletViewControllerBase: FormViewController, WalletViewController { extension WalletViewControllerBase: TransferViewControllerDelegate { - func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails, detailsViewController: UIViewController?) { + func transferViewController(_ viewController: TransferViewControllerBase, didFinishWithTransfer transfer: TransactionDetails?, detailsViewController: UIViewController?) { if let nav = navigationController, nav.topViewController == viewController { DispatchQueue.main.async { if let detailsViewController = detailsViewController { From 868b1338b58588305e8a09dd4ae7f818876ca305 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Sat, 27 Oct 2018 22:25:51 +0300 Subject: [PATCH 18/69] Fixed ETH url --- .../Ethereum/EthTransactionDetailsViewController.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Adamant/Wallets/Ethereum/EthTransactionDetailsViewController.swift b/Adamant/Wallets/Ethereum/EthTransactionDetailsViewController.swift index 92f828fea..1f1498be5 100644 --- a/Adamant/Wallets/Ethereum/EthTransactionDetailsViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransactionDetailsViewController.swift @@ -18,6 +18,10 @@ class EthTransactionDetailsViewController: TransactionDetailsViewControllerBase } override func explorerUrl(for transaction: TransactionDetails) -> URL? { - return URL(string: "\(AdamantResources.ethereumExplorerAddress)\(transaction.id)") + guard let id = transaction.id else { + return nil + } + + return URL(string: "\(AdamantResources.ethereumExplorerAddress)\(id)") } } From a70d5837317ae601bdcda1c641eb59fd3f5f5307 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Sat, 27 Oct 2018 22:55:41 +0300 Subject: [PATCH 19/69] Improved ETH transaction details --- .../EthTransactionDetailsViewController.swift | 42 +++++++++++++++++++ .../EthTransactionsViewController.swift | 1 + .../Ethereum/EthTransferViewController.swift | 12 ++++-- ...EthWalletService+RichMessageProvider.swift | 2 + ...TransactionDetailsViewControllerBase.swift | 6 +-- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/Adamant/Wallets/Ethereum/EthTransactionDetailsViewController.swift b/Adamant/Wallets/Ethereum/EthTransactionDetailsViewController.swift index 1f1498be5..de3bfd498 100644 --- a/Adamant/Wallets/Ethereum/EthTransactionDetailsViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransactionDetailsViewController.swift @@ -9,12 +9,28 @@ import UIKit class EthTransactionDetailsViewController: TransactionDetailsViewControllerBase { + // MARK: - Dependencies + + weak var service: EthWalletService? + + // MARK: - Properties + + private lazy var refreshControl: UIRefreshControl = { + let control = UIRefreshControl() + control.addTarget(self, action: #selector(refresh), for: UIControl.Event.valueChanged) + return control + }() + // MARK: - Overrides override func viewDidLoad() { currencySymbol = EthWalletService.currencySymbol super.viewDidLoad() + + if service != nil { + tableView.refreshControl = refreshControl + } } override func explorerUrl(for transaction: TransactionDetails) -> URL? { @@ -24,4 +40,30 @@ class EthTransactionDetailsViewController: TransactionDetailsViewControllerBase return URL(string: "\(AdamantResources.ethereumExplorerAddress)\(id)") } + + @objc func refresh() { + guard let id = transaction?.id, let service = service else { + refreshControl.endRefreshing() + return + } + + service.getTransaction(by: id) { [weak self] result in + switch result { + case .success(let trs): + self?.transaction = trs + + DispatchQueue.main.async { + self?.tableView.reloadData() + self?.refreshControl.endRefreshing() + } + + case .failure(let error): + self?.dialogService.showRichError(error: error) + + DispatchQueue.main.async { + self?.refreshControl.endRefreshing() + } + } + } + } } diff --git a/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift b/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift index a3dc3573d..1f63f1b7b 100644 --- a/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift @@ -92,6 +92,7 @@ class EthTransactionsViewController: TransactionsListViewControllerBase { } vc.transaction = transaction + vc.service = ethWalletService navigationController?.pushViewController(vc, animated: true) } diff --git a/Adamant/Wallets/Ethereum/EthTransferViewController.swift b/Adamant/Wallets/Ethereum/EthTransferViewController.swift index 6d8a7ef00..6c4ad7d4b 100644 --- a/Adamant/Wallets/Ethereum/EthTransferViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransferViewController.swift @@ -77,11 +77,15 @@ class EthTransferViewController: TransferViewControllerBase { service.getTransaction(by: hash) { result in switch result { case .success(let transaction): - let detailsVc = vc.router.get(scene: AdamantScene.Wallets.Ethereum.transactionDetails) as? EthTransactionDetailsViewController - detailsVc?.transaction = transaction - vc.dialogService.showSuccess(withMessage: String.adamantLocalized.transfer.transferSuccess) - vc.delegate?.transferViewController(vc, didFinishWithTransfer: transaction, detailsViewController: detailsVc) + + if let detailsVc = vc.router.get(scene: AdamantScene.Wallets.Ethereum.transactionDetails) as? EthTransactionDetailsViewController { + detailsVc.transaction = transaction + detailsVc.service = service + vc.delegate?.transferViewController(vc, didFinishWithTransfer: transaction, detailsViewController: detailsVc) + } else { + vc.delegate?.transferViewController(vc, didFinishWithTransfer: transaction, detailsViewController: nil) + } case .failure(let error): vc.dialogService.showRichError(error: error) diff --git a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift index d8b86cd4a..0cc91a721 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift @@ -30,6 +30,8 @@ extension EthWalletService: RichMessageProvider { return } + vc.service = self + switch result { case .success(let ethTransaction): vc.transaction = ethTransaction diff --git a/Adamant/Wallets/TransactionDetailsViewControllerBase.swift b/Adamant/Wallets/TransactionDetailsViewControllerBase.swift index 19fb151ed..379bc877c 100644 --- a/Adamant/Wallets/TransactionDetailsViewControllerBase.swift +++ b/Adamant/Wallets/TransactionDetailsViewControllerBase.swift @@ -86,11 +86,7 @@ class TransactionDetailsViewControllerBase: FormViewController { // MARK: - Properties - var transaction: TransactionDetails? = nil { - didSet { - tableView?.reloadData() - } - } + var transaction: TransactionDetails? = nil private static let awaitingValueString = "⏱" From 534fdeaaa2662280388b5b74a1e68adcf097ce5f Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Sat, 27 Oct 2018 23:43:51 +0300 Subject: [PATCH 20/69] ETH Mainnet --- Adamant/AppDelegate.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index c9592010b..5fbc1f6d3 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -48,8 +48,8 @@ struct AdamantResources { ] static let ethServers = [ -// "https://ethnode1.adamant.im/" - "https://ropsten.infura.io/" // test network + "https://ethnode1.adamant.im/" +// "https://ropsten.infura.io/" // test network ] // Addresses @@ -70,8 +70,8 @@ struct AdamantResources { // Explorers static let adamantExplorerAddress = "https://explorer.adamant.im/tx/" -// static let ethereumExplorerAddress = "https://etherscan.io/tx/" - static let ethereumExplorerAddress = "https://ropsten.etherscan.io/tx/" // Testnet + static let ethereumExplorerAddress = "https://etherscan.io/tx/" +// static let ethereumExplorerAddress = "https://ropsten.etherscan.io/tx/" // Testnet private init() {} } From d7c1ee2d53c6b5f5cb7cf1d0720142a338131c99 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Sun, 28 Oct 2018 02:53:40 +0300 Subject: [PATCH 21/69] New ETH transactions API --- Adamant/Models/EthTransaction.swift | 92 +++++++++++ .../EthTransactionsViewController.swift | 34 +++- .../Wallets/Ethereum/EthWalletService.swift | 149 ++++++++++++------ 3 files changed, 218 insertions(+), 57 deletions(-) diff --git a/Adamant/Models/EthTransaction.swift b/Adamant/Models/EthTransaction.swift index 663cae5fe..e0f3c23cc 100644 --- a/Adamant/Models/EthTransaction.swift +++ b/Adamant/Models/EthTransaction.swift @@ -198,3 +198,95 @@ extension EthereumTransaction { } */ + +// MARK: - Adamant ETH API transactions + +struct EthTransactionShort { + let date: Date + let hash: String + let from: String + let to: String + let gasUsed: Decimal + let gasPrice: Decimal + let value: Decimal + let blockNumber: String + + func asEthTransaction(isOutgoing: Bool) -> EthTransaction { + return EthTransaction(date: date, + hash: hash, + value: value, + from: from, + to: to, + gasUsed: gasUsed, + gasPrice: gasPrice, + confirmations: nil, + isError: false, + receiptStatus: .ok, + blockNumber: blockNumber, + isOutgoing: isOutgoing) + } +} + +extension EthTransactionShort: Decodable { + enum CodingKeys: String, CodingKey { + case time + case txfrom + case txto + case gas + case gasprice + case block + case txhash + case value + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + from = try container.decode(String.self, forKey: .txfrom) + to = try container.decode(String.self, forKey: .txto) + + // Hash + let hashRaw = try container.decode(String.self, forKey: .txhash) + hash = hashRaw.replacingOccurrences(of: "\\", with: "0") + + // Block + let blockRaw = try container.decode(UInt64.self, forKey: .block) + blockNumber = String(blockRaw) + + // Date + let timestamp = try container.decode(TimeInterval.self, forKey: .time) + date = Date(timeIntervalSince1970: timestamp) + + // Gas used + gasUsed = try container.decode(Decimal.self, forKey: .gas) + + // Gas price + let gasPriceRaw = try container.decode(Decimal.self, forKey: .gasprice) + gasPrice = Decimal(sign: .plus, exponent: EthWalletService.currencyExponent, significand: gasPriceRaw) + + // Value + let valueRaw = try container.decode(Decimal.self, forKey: .value) + value = Decimal(sign: .plus, exponent: EthWalletService.currencyExponent, significand: valueRaw) + } +} + +// MARK: Adamant node Sample JSON +/* + + { + "time": 1540676411, + "txfrom": "0xcE25C5bbEB9f27ac942f914183279FDB31C999dC", + "txto": "0x201B95b75B4114A825b278710307EFA0b5A5Ebf1", + "gas": 21000, + "gasprice": 4000000000, + "block": 6595442, + "txhash": "\\x998b47613f294dd6795ccd28e2c68f244a97a87e20ba30f88012a34e899d029b", + "value": 4000000000000000000, + "contract_to": "", + "contract_value": "" + } + + Note broken txhash + contract_to & contract_value not requested from API + + */ diff --git a/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift b/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift index 1f63f1b7b..de540d52f 100644 --- a/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift @@ -21,7 +21,7 @@ class EthTransactionsViewController: TransactionsListViewControllerBase { var router: Router! // MARK: - Properties - var transactions: [EthTransaction] = [] + var transactions: [EthTransactionShort] = [] private var ethAddress: String = "" override func viewDidLoad() { @@ -84,16 +84,34 @@ class EthTransactionsViewController: TransactionsListViewControllerBase { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) + let hash = transactions[indexPath.row].hash - let transaction = transactions[indexPath.row] + guard let dialogService = dialogService else { + return + } guard let vc = router.get(scene: AdamantScene.Wallets.Ethereum.transactionDetails) as? EthTransactionDetailsViewController else { - return + fatalError("Failed to get EthTransactionDetailsViewController") } - - vc.transaction = transaction + vc.service = ethWalletService - navigationController?.pushViewController(vc, animated: true) + + dialogService.showProgress(withMessage: nil, userInteractionEnable: false) + + ethWalletService.getTransaction(by: hash) { [weak self] result in + dialogService.dismissProgress() + + switch result { + case .success(let ethTransaction): + DispatchQueue.main.async { + vc.transaction = ethTransaction + self?.navigationController?.pushViewController(vc, animated: true) + } + + case .failure(let error): + dialogService.showRichError(error: error) + } + } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -107,7 +125,7 @@ class EthTransactionsViewController: TransactionsListViewControllerBase { return cell } - func configureCell(_ cell: TransactionTableViewCell, for transaction: EthTransaction) { + func configureCell(_ cell: TransactionTableViewCell, for transaction: EthTransactionShort) { let outgoing = isOutgoing(transaction) let partnerId = outgoing ? transaction.to : transaction.from @@ -123,7 +141,7 @@ class EthTransactionsViewController: TransactionsListViewControllerBase { // MARK: - Tools extension EthTransactionsViewController { - private func isOutgoing(_ transaction: EthTransaction) -> Bool { + private func isOutgoing(_ transaction: EthTransactionShort) -> Bool { return transaction.from.lowercased() == ethAddress.lowercased() } } diff --git a/Adamant/Wallets/Ethereum/EthWalletService.swift b/Adamant/Wallets/Ethereum/EthWalletService.swift index cf7b51f29..2abb253ca 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService.swift @@ -75,6 +75,8 @@ class EthWalletService: WalletService { // MARK: - Properties + private static let transactionsListApiSubpath = "ethtxs" + let web3: web3 private let baseUrl: String let defaultDispatchQueue = DispatchQueue(label: "im.adamant.ethWalletService", qos: .utility, attributes: [.concurrent]) @@ -255,8 +257,8 @@ class EthWalletService: WalletService { return "https://api\(suffix).etherscan.io/api" } - private func buildUrl(queryItems: [URLQueryItem]? = nil) throws -> URL { - guard let url = URL(string: baseUrl), var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + private func buildUrl(url: URL, queryItems: [URLQueryItem]? = nil) throws -> URL { + guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { throw AdamantApiService.InternalError.endpointBuildFailed } @@ -490,53 +492,6 @@ extension EthWalletService { // MARK: - Transactions extension EthWalletService { - func getTransactionsHistory(address: String, page: Int = 1, size: Int = 50, completion: @escaping (WalletServiceResult<[EthTransaction]>) -> Void) { - let queryItems: [URLQueryItem] = [URLQueryItem(name: "module", value: "account"), - URLQueryItem(name: "action", value: "txlist"), - URLQueryItem(name: "address", value: address), - URLQueryItem(name: "page", value: "\(page)"), - URLQueryItem(name: "offset", value: "\(size)"), - URLQueryItem(name: "sort", value: "desc") - // ,URLQueryItem(name: "apikey", value: "YourApiKeyToken") - ] - - let endpoint: URL - do { - endpoint = try buildUrl(queryItems: queryItems) - } catch { - let err = AdamantApiService.InternalError.endpointBuildFailed.apiServiceErrorWith(error: error) - completion(.failure(error: WalletServiceError.apiError(err))) - return - } - - Alamofire.request(endpoint).responseData(queue: defaultDispatchQueue) { response in - switch response.result { - case .success(let data): - do { - let model: EthResponse = try JSONDecoder().decode(EthResponse.self, from: data) - - if model.status == 1 { - var transactions = model.result - - for index in 0.. ) -> Void) { let sender = wallet?.address let eth = web3.eth @@ -600,4 +555,100 @@ extension EthWalletService { } } } + + func getTransactionsHistory(address: String, offset: Int = 0, limit: Int = 100, completion: @escaping (WalletServiceResult<[EthTransactionShort]>) -> Void) { + + // Headers + let headers = [ + "Content-Type": "application/json" + ] + + // Request + let columns = "time,txfrom,txto,gas,gasprice,block,txhash,value" + let order = "time.desc" + + let queryItems: [URLQueryItem] = [URLQueryItem(name: "select", value: columns), + URLQueryItem(name: "limit", value: String(limit)), + URLQueryItem(name: "txfrom", value: "eq.\(address)"), + URLQueryItem(name: "offset", value: String(offset)), + URLQueryItem(name: "order", value: order) + ] + + guard let raw = AdamantResources.ethServers.randomElement(), let url = URL(string: raw) else { + fatalError("Failed to build ETH endpoint URL") + } + + let endpoint: URL + do { + endpoint = try buildUrl(url: url.appendingPathComponent(EthWalletService.transactionsListApiSubpath), queryItems: queryItems) + } catch { + let err = AdamantApiService.InternalError.endpointBuildFailed.apiServiceErrorWith(error: error) + completion(.failure(error: WalletServiceError.apiError(err))) + return + } + + Alamofire.request(endpoint, method: .get, headers: headers).responseData(queue: defaultDispatchQueue) { response in + switch response.result { + case .success(let data): + do { + let transactions = try JSONDecoder().decode([EthTransactionShort].self, from: data) + completion(.success(result: transactions)) + } catch { + completion(.failure(error: .internalError(message: "Failed to deserialize transactions", error: error))) + } + + case .failure: + completion(.failure(error: .networkError)) + } + } + } + + + /// Transaction history for Ropsten testnet + func getTransactionsHistoryRopsten(address: String, page: Int = 1, size: Int = 50, completion: @escaping (WalletServiceResult<[EthTransaction]>) -> Void) { + let queryItems: [URLQueryItem] = [URLQueryItem(name: "module", value: "account"), + URLQueryItem(name: "action", value: "txlist"), + URLQueryItem(name: "address", value: address), + URLQueryItem(name: "page", value: "\(page)"), + URLQueryItem(name: "offset", value: "\(size)"), + URLQueryItem(name: "sort", value: "desc") + // ,URLQueryItem(name: "apikey", value: "YourApiKeyToken") + ] + + let endpoint: URL + do { + endpoint = try buildUrl(url: URL(string: baseUrl)!, queryItems: queryItems) + } catch { + let err = AdamantApiService.InternalError.endpointBuildFailed.apiServiceErrorWith(error: error) + completion(.failure(error: WalletServiceError.apiError(err))) + return + } + + Alamofire.request(endpoint).responseData(queue: defaultDispatchQueue) { response in + switch response.result { + case .success(let data): + do { + let model: EthResponse = try JSONDecoder().decode(EthResponse.self, from: data) + + if model.status == 1 { + var transactions = model.result + + for index in 0.. Date: Sun, 28 Oct 2018 02:54:57 +0300 Subject: [PATCH 22/69] Build --- Adamant/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Adamant/Info.plist b/Adamant/Info.plist index 6cdc727cf..6c3c76083 100644 --- a/Adamant/Info.plist +++ b/Adamant/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2 CFBundleVersion -48 +49 LSRequiresIPhoneOS NSAppTransportSecurity From b7580edc60b6ea6269f59b83aed38bfdf7a57b2e Mon Sep 17 00:00:00 2001 From: Pavel AnokhovDate: Thu, 1 Nov 2018 21:58:01 +0300 Subject: [PATCH 23/69] Xcode update. --- .../xcshareddata/xcschemes/Adamant.Dev.BackgroundFetch.xcscheme | 2 +- Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Dev.xcscheme | 2 +- .../xcshareddata/xcschemes/Adamant.Release.xcscheme | 2 +- Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Test.xcscheme | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Dev.BackgroundFetch.xcscheme b/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Dev.BackgroundFetch.xcscheme index b89be5051..1f9e9c6e2 100644 --- a/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Dev.BackgroundFetch.xcscheme +++ b/Adamant.xcodeproj/xcshareddata/xcschemes/Adamant.Dev.BackgroundFetch.xcscheme @@ -1,6 +1,6 @@ Date: Thu, 1 Nov 2018 21:58:16 +0300 Subject: [PATCH 24/69] DateTimeRow for transaction details. --- Adamant/Wallets/TransactionDetailsViewControllerBase.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Adamant/Wallets/TransactionDetailsViewControllerBase.swift b/Adamant/Wallets/TransactionDetailsViewControllerBase.swift index 379bc877c..17b751587 100644 --- a/Adamant/Wallets/TransactionDetailsViewControllerBase.swift +++ b/Adamant/Wallets/TransactionDetailsViewControllerBase.swift @@ -226,11 +226,16 @@ class TransactionDetailsViewControllerBase: FormViewController { section.append(recipientRow) // MARK: Date - let dateRow = DateRow() { + let dateRow = DateTimeRow() { $0.disabled = true $0.tag = Rows.date.tag $0.title = Rows.date.localized $0.value = transaction?.dateValue + + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .medium + dateFormatter.timeStyle = .short + $0.dateFormatter = dateFormatter }.cellSetup { (cell, _) in cell.selectionStyle = .gray }.onCellSelection { [weak self] (_, row) in From 910d42362f2bb0564f14e2378fd730c87ff8ac3c Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Thu, 1 Nov 2018 22:10:29 +0300 Subject: [PATCH 25/69] Decerializing and formatting rich message transfer amounts. --- Adamant/Models/RichMessage.swift | 24 +++++++++---------- .../TransferMessageSizeCalculator.swift | 2 +- .../Chats/ChatViewController+MessageKit.swift | 9 +------ ...AdmWalletService+RichMessageProvider.swift | 2 +- ...EthWalletService+RichMessageProvider.swift | 2 +- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/Adamant/Models/RichMessage.swift b/Adamant/Models/RichMessage.swift index e8a2a83b3..df1b6dce2 100644 --- a/Adamant/Models/RichMessage.swift +++ b/Adamant/Models/RichMessage.swift @@ -38,27 +38,20 @@ struct RichContentKeys { struct RichMessageTransfer: RichMessage { let type: String - let amount: String + let amount: Decimal let hash: String let comments: String func content() -> [String:String] { return [ CodingKeys.type.stringValue: type, - CodingKeys.amount.stringValue: amount, + CodingKeys.amount.stringValue: AdamantBalanceFormat.full.format(amount), CodingKeys.hash.stringValue: hash, CodingKeys.comments.stringValue: comments ] } init(type: String, amount: Decimal, hash: String, comments: String) { - self.type = type - self.amount = RichMessageTransfer.serialize(balance: amount) - self.hash = hash - self.comments = comments - } - - init(type: String, amount: String, hash: String, comments: String) { self.type = type self.amount = amount self.hash = hash @@ -77,10 +70,10 @@ struct RichMessageTransfer: RichMessage { self.type = type self.hash = hash - if let amount = content[CodingKeys.amount.stringValue] { + if let raw = content[CodingKeys.amount.stringValue], let amount = Decimal(string: raw) { self.amount = amount } else { - self.amount = "0" + self.amount = 0 } if let comments = content[CodingKeys.comments.stringValue] { @@ -119,7 +112,14 @@ extension RichMessageTransfer { self.type = try container.decode(String.self, forKey: .type) self.hash = try container.decode(String.self, forKey: .hash) self.comments = try container.decode(String.self, forKey: .comments) - self.amount = try container.decode(String.self, forKey: .amount) + + if let raw = try? container.decode(String.self, forKey: .amount), let amount = Decimal(string: raw) { + self.amount = amount + } else if let amount = try? container.decode(Decimal.self, forKey: .amount) { + self.amount = amount + } else { + self.amount = 0 + } } } diff --git a/Adamant/SharedViews/TransferMessageSizeCalculator.swift b/Adamant/SharedViews/TransferMessageSizeCalculator.swift index 35eb4049b..e2d5b9f08 100644 --- a/Adamant/SharedViews/TransferMessageSizeCalculator.swift +++ b/Adamant/SharedViews/TransferMessageSizeCalculator.swift @@ -19,7 +19,7 @@ open class TransferMessageSizeCalculator: MessageSizeCalculator { fatalError("messageContainerSize received unhandled MessageDataType: \(message.kind)") } - let amount = transfer.amount + let amount = AdamantBalanceFormat.full.format(transfer.amount) var messageContainerSize = CGSize(width: cellWidth, height: cellHeight) diff --git a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift index b80b5a802..06d7cdb9c 100644 --- a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift +++ b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift @@ -606,15 +606,8 @@ extension TransferTransaction: MessageType { } public var kind: MessageKind { - let amountString: String - if let a = amount as Decimal? { - amountString = AdamantBalanceFormat.full.format(a) - } else { - amountString = "0" - } - return MessageKind.custom(RichMessageTransfer(type: AdmWalletService.richMessageType, - amount: amountString, + amount: amount as Decimal? ?? 0, hash: "", comments: "")) } diff --git a/Adamant/Wallets/Adamant/AdmWalletService+RichMessageProvider.swift b/Adamant/Wallets/Adamant/AdmWalletService+RichMessageProvider.swift index e11006da4..7e8e0d2cb 100644 --- a/Adamant/Wallets/Adamant/AdmWalletService+RichMessageProvider.swift +++ b/Adamant/Wallets/Adamant/AdmWalletService+RichMessageProvider.swift @@ -67,7 +67,7 @@ extension AdmWalletService: RichMessageProvider { cell.currencyLogoImageView.image = AdmWalletService.currencyLogo cell.currencySymbolLabel.text = AdmWalletService.currencySymbol - cell.amountLabel.text = richMessage.amount + cell.amountLabel.text = AdamantBalanceFormat.full.format(richMessage.amount) cell.dateLabel.text = message.sentDate.humanizedDateTime(withWeekday: false) cell.transactionStatus = nil diff --git a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift index 0cc91a721..42016450d 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift @@ -92,7 +92,7 @@ extension EthWalletService: RichMessageProvider { cell.currencyLogoImageView.image = EthWalletService.currencyLogo cell.currencySymbolLabel.text = EthWalletService.currencySymbol - cell.amountLabel.text = transfer.amount + cell.amountLabel.text = AdamantBalanceFormat.full.format(transfer.amount) cell.dateLabel.text = message.sentDate.humanizedDateTime(withWeekday: false) cell.transactionStatus = (message as? RichMessageTransaction)?.transactionStatus From 2c27657d8cd8265dbd969b406ae1699ed02330fd Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Thu, 1 Nov 2018 23:10:28 +0300 Subject: [PATCH 26/69] Showing Sender name for ETH transactions from transactions list Better last message formatting in ChatList --- .../EthTransactionsViewController.swift | 9 +++ ...EthWalletService+RichMessageProvider.swift | 55 ++++++++++++++++--- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift b/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift index de540d52f..35749c6a7 100644 --- a/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransactionsViewController.swift @@ -83,6 +83,8 @@ class EthTransactionsViewController: TransactionsListViewControllerBase { } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let address = ethWalletService.wallet?.address + tableView.deselectRow(at: indexPath, animated: true) let hash = transactions[indexPath.row].hash @@ -105,6 +107,13 @@ class EthTransactionsViewController: TransactionsListViewControllerBase { case .success(let ethTransaction): DispatchQueue.main.async { vc.transaction = ethTransaction + + if ethTransaction.senderAddress == address { + vc.senderName = String.adamantLocalized.transactionDetails.yourAddress + } else if ethTransaction.recipientAddress == address { + vc.recipientName = String.adamantLocalized.transactionDetails.yourAddress + } + self?.navigationController?.pushViewController(vc, animated: true) } diff --git a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift index 42016450d..c5911578e 100644 --- a/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift +++ b/Adamant/Wallets/Ethereum/EthWalletService+RichMessageProvider.swift @@ -14,15 +14,46 @@ extension EthWalletService: RichMessageProvider { // MARK: Events func richMessageTapped(for transaction: RichMessageTransaction, at indexPath: IndexPath, in chat: ChatViewController) { - guard let richContent = transaction.richContent, let hash = richContent[RichContentKeys.transfer.hash] else { - return + // MARK: 0. Prepare + guard let richContent = transaction.richContent, + let hash = richContent[RichContentKeys.transfer.hash], + let dialogService = dialogService else { + return } - guard let dialogService = dialogService else { - return + dialogService.showProgress(withMessage: nil, userInteractionEnable: false) + + // MARK: 1. Sender & recipient names + + let senderName: String? + let recipientName: String? + + if let address = accountService.account?.address { + if address == transaction.senderId { + senderName = String.adamantLocalized.transactionDetails.yourAddress + } else { + senderName = transaction.chatroom?.partner?.name + } + + if address == transaction.recipientId { + recipientName = String.adamantLocalized.transactionDetails.yourAddress + } else { + recipientName = transaction.chatroom?.partner?.name + } + } else if let partner = transaction.chatroom?.partner, let id = partner.address { + if transaction.senderId == id { + senderName = partner.name + recipientName = nil + } else { + recipientName = partner.name + senderName = nil + } + } else { + senderName = nil + recipientName = nil } - dialogService.showProgress(withMessage: nil, userInteractionEnable: false) + // MARK: 2. Go go transaction getTransaction(by: hash) { [weak self] result in dialogService.dismissProgress() @@ -31,6 +62,8 @@ extension EthWalletService: RichMessageProvider { } vc.service = self + vc.senderName = senderName + vc.recipientName = recipientName switch result { case .success(let ethTransaction): @@ -110,8 +143,16 @@ extension EthWalletService: RichMessageProvider { }() func shortDescription(for transaction: RichMessageTransaction) -> String { - guard let amount = transaction.richContent?[RichContentKeys.transfer.amount] else { - return "" + let amount: String + + guard let raw = transaction.richContent?[RichContentKeys.transfer.amount] else { + return "⬅️ \(EthWalletService.currencySymbol)" + } + + if let decimal = Decimal(string: raw) { + amount = AdamantBalanceFormat.full.format(decimal) + } else { + amount = raw } if transaction.isOutgoing { From f3794ac97d471ad9e2ef5fbf97ff2392bda4fb51 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Thu, 1 Nov 2018 23:47:53 +0300 Subject: [PATCH 27/69] Showing recipient name in transfer confirmation. --- Adamant/Wallets/TransferViewControllerBase.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Adamant/Wallets/TransferViewControllerBase.swift b/Adamant/Wallets/TransferViewControllerBase.swift index 829971d39..a2e1e87d2 100644 --- a/Adamant/Wallets/TransferViewControllerBase.swift +++ b/Adamant/Wallets/TransferViewControllerBase.swift @@ -449,11 +449,11 @@ class TransferViewControllerBase: FormViewController { // MARK: - Send Actions private func confirmSendFunds() { - guard let recipient = recipientAddress, let amount = amount else { + guard let recipientAddress = recipientAddress, let amount = amount else { return } - - guard validateRecipient(recipient) else { + + guard validateRecipient(recipientAddress) else { dialogService.showWarning(withMessage: String.adamantLocalized.transfer.addressValidationError) return } @@ -467,6 +467,13 @@ class TransferViewControllerBase: FormViewController { dialogService.showWarning(withMessage: "Not enought money to send report") return } + + let recipient: String + if let recipientName = recipientName { + recipient = "\(recipientName) \(recipientAddress)" + } else { + recipient = recipientAddress + } let formattedAmount = balanceFormatter.string(from: amount as NSDecimalNumber)! let title = String.adamantLocalized.alert.confirmSendMessage(formattedAmount: formattedAmount, recipient: recipient) From cb4fae6253cc35943e9666af42b40e6c30a7a6b0 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Fri, 2 Nov 2018 00:29:30 +0300 Subject: [PATCH 28/69] Another patch against empty messages. --- .../ChatModels.xcdatamodel/contents | 4 +-- ...essageTransaction+CoreDataProperties.swift | 2 +- .../Stories/Chats/ChatViewController.swift | 28 ++++++++++++------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents index 0c9750d82..175ae379c 100644 --- a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents +++ b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/Adamant/CoreData/TransferTransaction+CoreDataProperties.swift b/Adamant/CoreData/TransferTransaction+CoreDataProperties.swift index f402f64f0..948cc5fb0 100644 --- a/Adamant/CoreData/TransferTransaction+CoreDataProperties.swift +++ b/Adamant/CoreData/TransferTransaction+CoreDataProperties.swift @@ -2,7 +2,7 @@ // TransferTransaction+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 24.03.2018. +// Created by Anokhov Pavel on 03/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -17,6 +17,7 @@ extension TransferTransaction { return NSFetchRequest @@ -49,7 +49,7 @@ - + diff --git a/Adamant/CoreData/RichMessageTransaction+CoreDataProperties.swift b/Adamant/CoreData/RichMessageTransaction+CoreDataProperties.swift index 42b0c54ea..ce6089195 100644 --- a/Adamant/CoreData/RichMessageTransaction+CoreDataProperties.swift +++ b/Adamant/CoreData/RichMessageTransaction+CoreDataProperties.swift @@ -2,7 +2,7 @@ // RichMessageTransaction+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 06.10.2018. +// Created by Anokhov Pavel on 02/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 7017074d5..9e5e47b44 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -394,20 +394,28 @@ class ChatViewController: MessagesViewController { // MARK: Tools private func messageKind(for richMessage: RichMessageTransaction) -> MessageKind { - if let type = richMessage.richType, richMessageProviders[type] != nil, let richContent = richMessage.richContent, let richMessageTransfer = RichMessageTransfer(content: richContent) { + guard let type = richMessage.richType else { + return MessageKind.text(richMessage.richType ?? "Failed to read richmessage id: \(richMessage.id!)") + } + + guard var richContent = richMessage.richContent else { + fatalError() + } + + if richMessageProviders[type] != nil, let richMessageTransfer = RichMessageTransfer(content: richContent) { return MessageKind.custom(richMessageTransfer) - } else if var richContent = richMessage.richContent { - if let type = richMessage.richType { + } else { + if richContent[RichContentKeys.type] == nil { richContent[RichContentKeys.type] = type } - - if let data = try? JSONSerialization.data(withJSONObject: richContent, options: .prettyPrinted), let string = String(data: data, encoding: String.Encoding.utf8) { - return MessageKind.text(string) - } else { - return MessageKind.text(richMessage.richType ?? "") + + do { + let raw = try JSONSerialization.data(withJSONObject: richContent, options: []) + let serialized = String(data: raw, encoding: String.Encoding.utf8)! + return MessageKind.text(serialized) + } catch { + return MessageKind.text("Failed to read rich message: \(error.localizedDescription)") } - } else { - return MessageKind.text(richMessage.richType ?? "") } } } From 088596e8b8677e392f00cc8c50572902b0f4c2c6 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Fri, 2 Nov 2018 00:35:47 +0300 Subject: [PATCH 29/69] Fixed Rename/Share menu in chat --- Adamant/Stories/Chats/ChatViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 9e5e47b44..adda73b03 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -388,7 +388,9 @@ class ChatViewController: MessagesViewController { self?.present(alert, animated: true, completion: nil) } - dialogService.showAlert(title: nil, message: nil, style: .actionSheet, actions: [share, rename]) + let cancel = UIAlertAction(title: String.adamantLocalized.alert.cancel, style: .cancel, handler: nil) + + dialogService.showAlert(title: nil, message: nil, style: .actionSheet, actions: [share, rename, cancel]) } From 04256a642d6d4aabce160676a729bd4c5a92de19 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Sat, 3 Nov 2018 14:01:29 +0300 Subject: [PATCH 30/69] ChatTransaction.isHidden better chat deserialisation --- .../ChatModels.xcdatamodel/contents | 3 +- .../ChatTransaction+CoreDataProperties.swift | 5 +- .../DataProviders/AdamantChatsProvider.swift | 130 ++++++++++-------- .../Chats/ChatViewController+MessageKit.swift | 2 + 4 files changed, 79 insertions(+), 61 deletions(-) diff --git a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents index 175ae379c..52123bfb6 100644 --- a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents +++ b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents @@ -27,6 +27,7 @@ + @@ -58,7 +59,7 @@ - + diff --git a/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift b/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift index 4a187d05c..5af62e4af 100644 --- a/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift +++ b/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift @@ -2,7 +2,7 @@ // ChatTransaction+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 25.10.2018. +// Created by Anokhov Pavel on 02/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -17,11 +17,12 @@ extension ChatTransaction { return NSFetchRequest (entityName: "ChatTransaction") } + @NSManaged public var chatMessageId: String? @NSManaged public var isUnread: Bool @NSManaged public var showsChatroom: Bool @NSManaged public var silentNotification: Bool @NSManaged public var status: Int16 - @NSManaged public var chatMessageId: String? + @NSManaged public var isHidden: Bool @NSManaged public var chatroom: Chatroom? @NSManaged public var lastIn: Chatroom? diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 3d169f1ac..60196975a 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -601,7 +601,9 @@ extension AdamantChatsProvider { } let request: NSFetchRequest = NSFetchRequest(entityName: "ChatTransaction") - request.predicate = NSPredicate(format: "chatroom = %@", chatroom) + request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [ + NSPredicate(format: "chatroom = %@", chatroom), + NSPredicate(format: "isHidden == false")]) request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true), NSSortDescriptor(key: "transactionId", ascending: true)] let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil) @@ -612,8 +614,9 @@ extension AdamantChatsProvider { func getUnreadMessagesController() -> NSFetchedResultsController { let request = NSFetchRequest (entityName: "ChatTransaction") request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [ - NSPredicate(format: "isUnread == true"), - NSPredicate(format: "chatroom.isHidden == false")]) + NSPredicate(format: "chatroom.isHidden == false"), + NSPredicate(format: "isUnread == true"), + NSPredicate(format: "isHidden == false")]) request.sortDescriptors = [NSSortDescriptor.init(key: "date", ascending: false), NSSortDescriptor(key: "transactionId", ascending: false)] @@ -948,76 +951,87 @@ extension AdamantChatsProvider { return nil } - let decodedMessage = adamantCore.decodeMessage(rawMessage: chat.message, rawNonce: chat.ownMessage, senderPublicKey: publicKey, privateKey: privateKey) - let messageTransaction: ChatTransaction - switch chat.type { - case .message, .messageOld, .signal, .unknown: - let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) - trs.message = decodedMessage - messageTransaction = trs - - case .richMessage: - if let decodedMessage = decodedMessage, let data = decodedMessage.data(using: String.Encoding.utf8), let jsonRaw = try? JSONSerialization.jsonObject(with: data, options: []) { - switch jsonRaw { - // MARK: Valid json - case let json as [String:String]: - // Supported rich message type - if let type = json[RichContentKeys.type] { - let trs = RichMessageTransaction(entity: RichMessageTransaction.entity(), insertInto: context) - trs.richContent = json - trs.richType = type - trs.transactionStatus = richProviders[type] != nil ? .notInitiated : nil - messageTransaction = trs - } - - // Not supported, show as text message - else { - let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) - trs.message = decodedMessage - messageTransaction = trs - } - - // MARK: Bad json, try to fix it - case let json as [String:Any]: - // Supported type but in wrong format - if let type = json[RichContentKeys.type] as? String { - var fixedJson = [String:String]() + // MARK: Decode message, message must contain data + if let decodedMessage = adamantCore.decodeMessage(rawMessage: chat.message, rawNonce: chat.ownMessage, senderPublicKey: publicKey, privateKey: privateKey), decodedMessage.count > 0 { + switch chat.type { + // MARK: Text message + case .message, .messageOld, .signal, .unknown: + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + + messageTransaction = trs + + // MARK: Rich message + case .richMessage: + if let data = decodedMessage.data(using: String.Encoding.utf8), let jsonRaw = try? JSONSerialization.jsonObject(with: data, options: []) { + switch jsonRaw { + // MARK: Valid json + case let json as [String:String]: + // Supported rich message type + if let type = json[RichContentKeys.type] { + let trs = RichMessageTransaction(entity: RichMessageTransaction.entity(), insertInto: context) + trs.richContent = json + trs.richType = type + trs.transactionStatus = richProviders[type] != nil ? .notInitiated : nil + messageTransaction = trs + } + + // Not supported, show as text message + else { + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + messageTransaction = trs + } - for (key, raw) in json { - if let value = raw as? String { - fixedJson[key] = value - } else if let value = raw as? NSNumber, let amount = AdamantBalanceFormat.currencyFormatterFull.string(from: value) { - fixedJson[key] = amount - } else { - fixedJson[key] = String(describing: raw) + // MARK: Bad json, try to fix it + case let json as [String:Any]: + // Supported type but in wrong format + if let type = json[RichContentKeys.type] as? String { + var fixedJson = [String:String]() + + for (key, raw) in json { + if let value = raw as? String { + fixedJson[key] = value + } else if let value = raw as? NSNumber, let amount = AdamantBalanceFormat.currencyFormatterFull.string(from: value) { + fixedJson[key] = amount + } else { + fixedJson[key] = String(describing: raw) + } } + + let trs = RichMessageTransaction(entity: RichMessageTransaction.entity(), insertInto: context) + trs.richContent = fixedJson + trs.richType = type + trs.transactionStatus = richProviders[type] != nil ? .notInitiated : nil + messageTransaction = trs + } + // Not supported, show as text message + else { + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + messageTransaction = trs } - let trs = RichMessageTransaction(entity: RichMessageTransaction.entity(), insertInto: context) - trs.richContent = fixedJson - trs.richType = type - trs.transactionStatus = richProviders[type] != nil ? .notInitiated : nil - messageTransaction = trs - } - // Not supported, show as text message - else { + default: let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) trs.message = decodedMessage messageTransaction = trs } - - default: + } else { let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) trs.message = decodedMessage messageTransaction = trs } - } else { - let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) - trs.message = decodedMessage - messageTransaction = trs } } + // MARK: Failed to decode, or message was empty + else { + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = "" + trs.isHidden = true + messageTransaction = trs + } messageTransaction.date = transaction.date as NSDate messageTransaction.recipientId = transaction.recipientId diff --git a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift index 06d7cdb9c..59f3a2bdb 100644 --- a/Adamant/Stories/Chats/ChatViewController+MessageKit.swift +++ b/Adamant/Stories/Chats/ChatViewController+MessageKit.swift @@ -558,6 +558,8 @@ extension MessageTransaction: MessageType { public var kind: MessageKind { guard let message = message else { + isHidden = true + try? managedObjectContext?.save() return MessageKind.text("") } From 00121405f3643da98143fc1f644478696e5f5510 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Sat, 3 Nov 2018 19:23:08 +0300 Subject: [PATCH 31/69] Fixed fake messages (welcome messages) --- Adamant/AppDelegate.swift | 18 ++++++++++++-- .../DataProviders/ChatsProvider.swift | 4 ++-- .../AdamantChatsProvider+fakeMessages.swift | 24 +++++++++++-------- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index 5fbc1f6d3..9aad43655 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -408,7 +408,14 @@ extension AppDelegate { date: Date.adamantNullDate, unread: unread, silent: welcome.silentNotification, - completion: { _ in }) + showsChatroom: true, + completion: { result in + guard case let .failure(error) = result else { + return + } + + print("ERROR showing welcome message: \(error.message)") + }) } if let ico = AdamantContacts.adamantIco.messages["chats.ico_message"] { @@ -417,7 +424,14 @@ extension AppDelegate { date: Date.adamantNullDate, unread: unread, silent: ico.silentNotification, - completion: { _ in }) + showsChatroom: true, + completion: { result in + guard case let .failure(error) = result else { + return + } + + print("ERROR showing welcome message: \(error.message)") + }) } } } diff --git a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift index 9737079ee..d8df2a3e9 100644 --- a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift @@ -181,7 +181,7 @@ protocol ChatsProvider: DataProvider { func validateMessage(_ message: AdamantMessage) -> ValidateMessageResult // MARK: - Fake messages - func fakeSent(message: AdamantMessage, recipientId: String, date: Date, status: MessageStatus, completion: @escaping (ChatsProviderResult) -> Void) - func fakeReceived(message: AdamantMessage, senderId: String, date: Date, unread: Bool, silent: Bool, completion: @escaping (ChatsProviderResult) -> Void) + func fakeSent(message: AdamantMessage, recipientId: String, date: Date, status: MessageStatus, showsChatroom: Bool, completion: @escaping (ChatsProviderResult) -> Void) + func fakeReceived(message: AdamantMessage, senderId: String, date: Date, unread: Bool, silent: Bool, showsChatroom: Bool, completion: @escaping (ChatsProviderResult) -> Void) func fakeUpdate(status: MessageStatus, forTransactionId id: String, completion: @escaping (ChatsProviderResult) -> Void) } diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider+fakeMessages.swift b/Adamant/Services/DataProviders/AdamantChatsProvider+fakeMessages.swift index 11515a9ee..9eb2124d1 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider+fakeMessages.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider+fakeMessages.swift @@ -11,19 +11,19 @@ import CoreData extension AdamantChatsProvider { // MARK: - Public - func fakeSent(message: AdamantMessage, recipientId: String, date: Date, status: MessageStatus, completion: @escaping (ChatsProviderResult) -> Void) { + func fakeSent(message: AdamantMessage, recipientId: String, date: Date, status: MessageStatus, showsChatroom: Bool, completion: @escaping (ChatsProviderResult) -> Void) { validate(message: message, partnerId: recipientId) { [weak self] result in switch result { case .success(let loggedAddress, let partner): switch message { case .text(let text): - self?.fakeSent(text: text, loggedAddress: loggedAddress, recipient: partner, date: date, status: status, markdown: false, completion: completion) + self?.fakeSent(text: text, loggedAddress: loggedAddress, recipient: partner, date: date, status: status, markdown: false, showsChatroom: showsChatroom, completion: completion) case .richMessage(let payload): - self?.fakeSent(text: payload.serialized(), loggedAddress: loggedAddress, recipient: partner, date: date, status: status, markdown: false, completion: completion) + self?.fakeSent(text: payload.serialized(), loggedAddress: loggedAddress, recipient: partner, date: date, status: status, markdown: false, showsChatroom: showsChatroom, completion: completion) case .markdownText(let text): - self?.fakeSent(text: text, loggedAddress: loggedAddress, recipient: partner, date: date, status: status, markdown: true, completion: completion) + self?.fakeSent(text: text, loggedAddress: loggedAddress, recipient: partner, date: date, status: status, markdown: true, showsChatroom: showsChatroom, completion: completion) } case .failure(let error): @@ -32,19 +32,19 @@ extension AdamantChatsProvider { } } - func fakeReceived(message: AdamantMessage, senderId: String, date: Date, unread: Bool, silent: Bool, completion: @escaping (ChatsProviderResult) -> Void) { + func fakeReceived(message: AdamantMessage, senderId: String, date: Date, unread: Bool, silent: Bool, showsChatroom: Bool, completion: @escaping (ChatsProviderResult) -> Void) { validate(message: message, partnerId: senderId) { [weak self] result in switch result { case .success(let loggedAccount, let partner): switch message { case .text(let text): - self?.fakeReceived(text: text, loggedAddress: loggedAccount, sender: partner, date: date, unread: unread, silent: silent, markdown: false, completion: completion) + self?.fakeReceived(text: text, loggedAddress: loggedAccount, sender: partner, date: date, unread: unread, silent: silent, markdown: false, showsChatroom: showsChatroom, completion: completion) case .richMessage(let payload): - self?.fakeReceived(text: payload.serialized(), loggedAddress: loggedAccount, sender: partner, date: date, unread: unread, silent: silent, markdown: false, completion: completion) + self?.fakeReceived(text: payload.serialized(), loggedAddress: loggedAccount, sender: partner, date: date, unread: unread, silent: silent, markdown: false, showsChatroom: showsChatroom, completion: completion) case .markdownText(let text): - self?.fakeReceived(text: text, loggedAddress: loggedAccount, sender: partner, date: date, unread: unread, silent: silent, markdown: true, completion: completion) + self?.fakeReceived(text: text, loggedAddress: loggedAccount, sender: partner, date: date, unread: unread, silent: silent, markdown: true, showsChatroom: showsChatroom, completion: completion) } case .failure(let error): @@ -91,7 +91,7 @@ extension AdamantChatsProvider { // MARK: - Logic - private func fakeSent(text: String, loggedAddress: String, recipient: CoreDataAccount, date: Date, status: MessageStatus, markdown: Bool, completion: @escaping (ChatsProviderResult) -> Void) { + private func fakeSent(text: String, loggedAddress: String, recipient: CoreDataAccount, date: Date, status: MessageStatus, markdown: Bool, showsChatroom: Bool, completion: @escaping (ChatsProviderResult) -> Void) { // MARK: 0. Prepare let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) privateContext.parent = stack.container.viewContext @@ -107,9 +107,11 @@ extension AdamantChatsProvider { transaction.isUnread = false transaction.isMarkdown = markdown transaction.status = status.rawValue + transaction.showsChatroom = showsChatroom transaction.transactionId = UUID().uuidString transaction.blockId = UUID().uuidString + transaction.chatMessageId = transaction.transactionId // MARK: 2. Get Chatroom guard let id = recipient.chatroom?.objectID, let chatroom = privateContext.object(with: id) as? Chatroom else { @@ -127,7 +129,7 @@ extension AdamantChatsProvider { } } - private func fakeReceived(text: String, loggedAddress: String, sender: CoreDataAccount, date: Date, unread: Bool, silent: Bool, markdown: Bool, completion: @escaping (ChatsProviderResult) -> Void) { + private func fakeReceived(text: String, loggedAddress: String, sender: CoreDataAccount, date: Date, unread: Bool, silent: Bool, markdown: Bool, showsChatroom: Bool, completion: @escaping (ChatsProviderResult) -> Void) { // MARK: 0. Prepare let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) privateContext.parent = stack.container.viewContext @@ -144,9 +146,11 @@ extension AdamantChatsProvider { transaction.silentNotification = silent transaction.isMarkdown = markdown transaction.status = MessageStatus.delivered.rawValue + transaction.showsChatroom = showsChatroom transaction.transactionId = UUID().uuidString transaction.blockId = UUID().uuidString + transaction.chatMessageId = transaction.transactionId // MARK: 2. Get Chatroom guard let id = sender.chatroom?.objectID, let chatroom = privateContext.object(with: id) as? Chatroom else { From 09b5ed410530d8f742342d2ed3e4292c98b4cb93 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Sat, 3 Nov 2018 20:51:22 +0300 Subject: [PATCH 32/69] Chat messages with transfer amount deserialised as Transfers with comments. --- .../ChatModels.xcdatamodel/contents | 3 ++- .../TransferTransaction+CoreDataProperties.swift | 3 ++- .../DataProviders/AdamantChatsProvider.swift | 12 +++++++++--- .../DataProviders/AdamantTransfersProvider.swift | 6 +++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents index 52123bfb6..4ed504d71 100644 --- a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents +++ b/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents @@ -54,6 +54,7 @@ + @@ -63,6 +64,6 @@ - + (entityName: "TransferTransaction") } + @NSManaged public var comment: String? @NSManaged public var partner: CoreDataAccount? } diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 60196975a..3235bbc01 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -957,10 +957,16 @@ extension AdamantChatsProvider { switch chat.type { // MARK: Text message case .message, .messageOld, .signal, .unknown: - let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) - trs.message = decodedMessage - messageTransaction = trs + if transaction.amount > 0 { + let trs = TransferTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.comment = decodedMessage + messageTransaction = trs + } else { + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + messageTransaction = trs + } // MARK: Rich message case .richMessage: diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index f8e766c04..c8c054256 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -690,7 +690,11 @@ extension AdamantTransfersProvider { for (chatroom, trs) in chatrooms { chatroom.hasUnreadMessages = true - trs.forEach { $0.isUnread = true } + trs.forEach { + if !$0.isOutgoing { + $0.isUnread = true + } + } } transfers.filter({$0.height > unreadedHeight}).forEach({$0.isUnread = true}) From 289eeafb9cefd49f139d1205d4a3a596f03a1f9f Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Sat, 3 Nov 2018 22:12:15 +0300 Subject: [PATCH 33/69] Sender and recipient names for new transfer details. --- .../Adamant/AdmTransferViewController.swift | 22 ++++++++++++++++++- .../Ethereum/EthTransferViewController.swift | 1 + .../Wallets/TransferViewControllerBase.swift | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Adamant/Wallets/Adamant/AdmTransferViewController.swift b/Adamant/Wallets/Adamant/AdmTransferViewController.swift index f42d2b37e..a99600f4f 100644 --- a/Adamant/Wallets/Adamant/AdmTransferViewController.swift +++ b/Adamant/Wallets/Adamant/AdmTransferViewController.swift @@ -43,7 +43,27 @@ class AdmTransferViewController: TransferViewControllerBase { let detailsVC = self?.router.get(scene: AdamantScene.Wallets.Adamant.transactionDetails) as? AdmTransactionDetailsViewController detailsVC?.transaction = result - vc.delegate?.transferViewController(vc, didFinishWithTransfer: result, detailsViewController: detailsVC) + // MARK: Sender, you + detailsVC?.senderName = String.adamantLocalized.transactionDetails.yourAddress + + // MARK: Get recipient + if let recipientName = self?.recipientName { + detailsVC?.recipientName = recipientName + vc.delegate?.transferViewController(vc, didFinishWithTransfer: result, detailsViewController: detailsVC) + } else if let accountsProvider = self?.accountsProvider { + accountsProvider.getAccount(byAddress: recipient) { accResult in + switch accResult { + case .success(let account): + detailsVC?.recipientName = account.name + vc.delegate?.transferViewController(vc, didFinishWithTransfer: result, detailsViewController: detailsVC) + + default: + vc.delegate?.transferViewController(vc, didFinishWithTransfer: result, detailsViewController: detailsVC) + } + } + } else { + vc.delegate?.transferViewController(vc, didFinishWithTransfer: result, detailsViewController: detailsVC) + } case .failure(let error): guard let dialogService = self?.dialogService else { diff --git a/Adamant/Wallets/Ethereum/EthTransferViewController.swift b/Adamant/Wallets/Ethereum/EthTransferViewController.swift index 6c4ad7d4b..d9d0fd4ba 100644 --- a/Adamant/Wallets/Ethereum/EthTransferViewController.swift +++ b/Adamant/Wallets/Ethereum/EthTransferViewController.swift @@ -82,6 +82,7 @@ class EthTransferViewController: TransferViewControllerBase { if let detailsVc = vc.router.get(scene: AdamantScene.Wallets.Ethereum.transactionDetails) as? EthTransactionDetailsViewController { detailsVc.transaction = transaction detailsVc.service = service + detailsVc.senderName = String.adamantLocalized.transactionDetails.yourAddress vc.delegate?.transferViewController(vc, didFinishWithTransfer: transaction, detailsViewController: detailsVc) } else { vc.delegate?.transferViewController(vc, didFinishWithTransfer: transaction, detailsViewController: nil) diff --git a/Adamant/Wallets/TransferViewControllerBase.swift b/Adamant/Wallets/TransferViewControllerBase.swift index a2e1e87d2..528bf087a 100644 --- a/Adamant/Wallets/TransferViewControllerBase.swift +++ b/Adamant/Wallets/TransferViewControllerBase.swift @@ -125,6 +125,7 @@ class TransferViewControllerBase: FormViewController { // MARK: - Dependencies var accountService: AccountService! + var accountsProvider: AccountsProvider! var dialogService: DialogService! var router: Router! From 6ecc37e9828fe97a7629b1a62a435f0bb9439981 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Sun, 4 Nov 2018 14:44:54 +0300 Subject: [PATCH 34/69] ChatList: Initial Sync indicator --- Adamant/AppDelegate.swift | 6 +- .../Assets/l18n/de.lproj/Localizable.strings | 3 + .../Assets/l18n/en.lproj/Localizable.strings | 3 + .../Assets/l18n/ru.lproj/Localizable.strings | 3 + .../DataProviders/ChatsProvider.swift | 4 +- .../DataProviders/AdamantChatsProvider.swift | 20 ++-- .../Chats/ChatListViewController.swift | 91 ++++++++++++++++++- .../Stories/Chats/ChatListViewController.xib | 53 ++++++++++- 8 files changed, 170 insertions(+), 13 deletions(-) diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index 9aad43655..d3d5d3477 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -211,7 +211,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } // MARK: 7. Welcome messages - NotificationCenter.default.addObserver(forName: Notification.Name.AdamantChatsProvider.initialSyncFinished, object: nil, queue: OperationQueue.main, using: handleWelcomeMessages) + NotificationCenter.default.addObserver(forName: Notification.Name.AdamantChatsProvider.initiallySyncedChanged, object: nil, queue: OperationQueue.main, using: handleWelcomeMessages) return true } @@ -389,6 +389,10 @@ extension AppDelegate { // MARK: - Welcome messages extension AppDelegate { private func handleWelcomeMessages(notification: Notification) { + guard let synced = notification.userInfo?[AdamantUserInfoKey.ChatProvider.initiallySynced] as? Bool, synced else { + return + } + guard let stack = container.resolve(CoreDataStack.self), let chatProvider = container.resolve(ChatsProvider.self) else { fatalError("Whoa...") } diff --git a/Adamant/Assets/l18n/de.lproj/Localizable.strings b/Adamant/Assets/l18n/de.lproj/Localizable.strings index d2bac6522..db707b2bd 100755 --- a/Adamant/Assets/l18n/de.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/de.lproj/Localizable.strings @@ -184,6 +184,9 @@ /* ChatList: outgoing message preview format, like 'You: %@' */ "ChatListPage.SentMessageFormat" = "Sie: %@"; +/* ChatList: First syncronization is in progress */ +"ChatListPage.SyncingChats" = "Syncing chats…"; + /* ChatList: scene title */ "ChatListPage.Title" = "Chats"; diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings index 617b65205..61f3c15bb 100755 --- a/Adamant/Assets/l18n/en.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings @@ -181,6 +181,9 @@ /* ChatList: outgoing message preview format, like 'You: %@' */ "ChatListPage.SentMessageFormat" = "You: %@"; +/* ChatList: First syncronization is in progress */ +"ChatListPage.SyncingChats" = "Syncing chats…"; + /* ChatList: scene title */ "ChatListPage.Title" = "Chats"; diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings index 8a5f47a1e..ab95886e6 100644 --- a/Adamant/Assets/l18n/ru.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/ru.lproj/Localizable.strings @@ -181,6 +181,9 @@ /* ChatList: outgoing message preview format, like 'You: %@' */ "ChatListPage.SentMessageFormat" = "Вы: %@"; +/* ChatList: First syncronization is in progress */ +"ChatListPage.SyncingChats" = "Загружаем чаты…"; + /* ChatList: scene title */ "ChatListPage.Title" = "Чаты"; diff --git a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift index d8df2a3e9..8f5fb0ddc 100644 --- a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift @@ -120,7 +120,7 @@ extension Notification.Name { /// Received new messagess. See AdamantUserInfoKey.ChatProvider static let newUnreadMessages = Notification.Name("adamant.chatsProvider.newUnreadMessages") - static let initialSyncFinished = Notification.Name("adamant.chatsProvider.initialSyncFinished") + static let initiallySyncedChanged = Notification.Name("adamant.chatsProvider.initialSyncChanged") private init() {} } @@ -135,6 +135,8 @@ extension AdamantUserInfoKey { /// lastMessageHeight: new lastMessageHeight static let lastMessageHeight = "adamant.chatsProvider.newMessage.lastHeight" + static let initiallySynced = "adamant.chatsProvider.initiallySynced" + private init() {} } } diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 3235bbc01..680eb7afd 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -21,13 +21,18 @@ class AdamantChatsProvider: ChatsProvider { var richProviders: [String:RichMessageProviderWithStatusCheck]! // MARK: Properties - private(set) var state: State = .empty - private(set) var isInitiallySynced: Bool = false - private(set) var receivedLastHeight: Int64? - private(set) var readedLastHeight: Int64? - private let apiTransactions = 100 - private var unconfirmedTransactions: [UInt64:NSManagedObjectID] = [:] - + private(set) var state: State = .empty + private(set) var receivedLastHeight: Int64? + private(set) var readedLastHeight: Int64? + private let apiTransactions = 100 + private var unconfirmedTransactions: [UInt64:NSManagedObjectID] = [:] + + private(set) var isInitiallySynced: Bool = false { + didSet { + NotificationCenter.default.post(name: Notification.Name.AdamantChatsProvider.initiallySyncedChanged, object: self, userInfo: [AdamantUserInfoKey.ChatProvider.initiallySynced : isInitiallySynced]) + } + } + private let processingQueue = DispatchQueue(label: "im.adamant.processing.chat", qos: .utility, attributes: [.concurrent]) private let sendingQueue = DispatchQueue(label: "im.adamant.sending.chat", qos: .utility, attributes: [.concurrent]) private let unconfirmedsSemaphore = DispatchSemaphore(value: 1) @@ -217,7 +222,6 @@ extension AdamantChatsProvider { if let synced = self?.isInitiallySynced, !synced { self?.isInitiallySynced = true - NotificationCenter.default.post(name: Notification.Name.AdamantChatsProvider.initialSyncFinished, object: self) } completion?(.success) diff --git a/Adamant/Stories/Chats/ChatListViewController.swift b/Adamant/Stories/Chats/ChatListViewController.swift index 5b8abffba..6d971d32c 100644 --- a/Adamant/Stories/Chats/ChatListViewController.swift +++ b/Adamant/Stories/Chats/ChatListViewController.swift @@ -13,7 +13,8 @@ extension String.adamantLocalized { struct chatList { static let title = NSLocalizedString("ChatListPage.Title", comment: "ChatList: scene title") static let sentMessagePrefix = NSLocalizedString("ChatListPage.SentMessageFormat", comment: "ChatList: outgoing message preview format, like 'You: %@'") - + static let syncingChats = NSLocalizedString("ChatListPage.SyncingChats", comment: "ChatList: First syncronization is in progress") + private init() {} } } @@ -55,6 +56,15 @@ class ChatListViewController: UIViewController { return refreshControl }() + // MARK: Busy indicator + + @IBOutlet weak var busyBackgroundView: UIView! + @IBOutlet weak var busyIndicatorView: UIView! + @IBOutlet weak var busyIndicatorLabel: UILabel! + + private(set) var isBusy: Bool = true + + // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() @@ -85,6 +95,27 @@ class ChatListViewController: UIViewController { NotificationCenter.default.addObserver(forName: Notification.Name.AdamantAccountService.userLoggedOut, object: nil, queue: OperationQueue.main) { [weak self] _ in self?.initFetchedRequestControllers(provider: nil) } + + // MARK: Busy Indicator + busyIndicatorLabel.text = String.adamantLocalized.chatList.syncingChats + + busyIndicatorView.layer.cornerRadius = 14 + busyIndicatorView.clipsToBounds = true + + isBusy = !chatsProvider.isInitiallySynced + if !isBusy { + setIsBusy(false, animated: false) + } + + NotificationCenter.default.addObserver(forName: Notification.Name.AdamantChatsProvider.initiallySyncedChanged, object: chatsProvider, queue: OperationQueue.main) { [weak self] notification in + if let synced = notification.userInfo?[AdamantUserInfoKey.ChatProvider.initiallySynced] as? Bool { + self?.setIsBusy(!synced) + } else if let synced = self?.chatsProvider.isInitiallySynced { + self?.setIsBusy(!synced) + } else { + self?.setIsBusy(true) + } + } } deinit { @@ -188,6 +219,64 @@ class ChatListViewController: UIViewController { } } } + + func setIsBusy(_ busy: Bool, animated: Bool = true) { + isBusy = busy + + // MARK: 0. Check if animated. + guard animated else { + if !busy { + busyBackgroundView.isHidden = true + return + } + + if Thread.isMainThread { + busyBackgroundView.isHidden = false + busyBackgroundView.alpha = 1.0 + busyIndicatorView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) + } else { + DispatchQueue.main.async { + self.busyBackgroundView.isHidden = false + self.busyBackgroundView.alpha = 1.0 + self.busyIndicatorView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) + } + } + + return + } + + // MARK: 1. Prepare animation and completion + let animations: () -> Void = { + self.busyBackgroundView.alpha = busy ? 1.0 : 0.0 + self.busyIndicatorView.transform = busy ? CGAffineTransform(scaleX: 1.0, y: 1.0) : CGAffineTransform(scaleX: 1.2, y: 1.2) + } + + let completion: (Bool) -> Void = { completed in + guard completed else { + return + } + + self.busyBackgroundView.isHidden = !busy + } + + // MARK: 2. Initial values + let initialValues: () -> Void = { + self.busyBackgroundView.alpha = busy ? 0.0 : 1.0 + self.busyIndicatorView.transform = busy ? CGAffineTransform(scaleX: 1.2, y: 1.2) : CGAffineTransform(scaleX: 1.0, y: 1.0) + + self.busyBackgroundView.isHidden = false + } + + if Thread.isMainThread { + initialValues() + UIView.animate(withDuration: 0.2, animations: animations, completion: completion) + } else { + DispatchQueue.main.async { + initialValues() + UIView.animate(withDuration: 0.2, animations: animations, completion: completion) + } + } + } } diff --git a/Adamant/Stories/Chats/ChatListViewController.xib b/Adamant/Stories/Chats/ChatListViewController.xib index 582643743..0af4c8a11 100644 --- a/Adamant/Stories/Chats/ChatListViewController.xib +++ b/Adamant/Stories/Chats/ChatListViewController.xib @@ -1,17 +1,20 @@ - + - + + @@ -25,13 +28,59 @@+ + + + + + ++ ++ + ++ + + ++ ++ + ++ + ++ + ++ + + ++ + + + + ++ + + + + + From 6e2eac33f91aaf027632f57cb8b30fae7644e649 Mon Sep 17 00:00:00 2001 From: Pavel Anokhov Date: Mon, 5 Nov 2018 15:18:57 +0300 Subject: [PATCH 35/69] Transfer comments. --- Adamant/AppDelegate.swift | 4 +- Adamant/Models/RichMessage.swift | 2 +- .../TransferCollectionViewCell.swift | 22 ++++- .../TransferCollectionViewCell.xib | 18 ++-- .../TransferMessageSizeCalculator.swift | 29 +++++- .../Chats/ChatViewController+MessageKit.swift | 5 + .../Chats/ComplexTransferViewController.swift | 5 +- .../AdmTransactionDetailsViewController.swift | 2 +- .../AdmTransactionsViewController.swift | 1 + ...AdmWalletService+RichMessageProvider.swift | 3 + .../Wallets/Adamant/AdmWalletService.swift | 3 +- .../Ethereum/EthTransferViewController.swift | 14 ++- ...EthWalletService+RichMessageProvider.swift | 10 ++ .../Ethereum/EthWalletService+Send.swift | 2 +- .../Wallets/Ethereum/EthWalletService.swift | 1 - ...TransactionDetailsViewControllerBase.swift | 91 ++++++++++++++++--- .../Wallets/TransferViewControllerBase.swift | 26 +++++- Adamant/Wallets/WalletService.swift | 9 +- 18 files changed, 206 insertions(+), 41 deletions(-) diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index d3d5d3477..f061a2e5b 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -48,8 +48,8 @@ struct AdamantResources { ] static let ethServers = [ - "https://ethnode1.adamant.im/" -// "https://ropsten.infura.io/" // test network +// "https://ethnode1.adamant.im/" + "https://ropsten.infura.io/" // test network ] // Addresses diff --git a/Adamant/Models/RichMessage.swift b/Adamant/Models/RichMessage.swift index df1b6dce2..596db0449 100644 --- a/Adamant/Models/RichMessage.swift +++ b/Adamant/Models/RichMessage.swift @@ -45,7 +45,7 @@ struct RichMessageTransfer: RichMessage { func content() -> [String:String] { return [ CodingKeys.type.stringValue: type, - CodingKeys.amount.stringValue: AdamantBalanceFormat.full.format(amount), + CodingKeys.amount.stringValue: RichMessageTransfer.serialize(balance: amount), CodingKeys.hash.stringValue: hash, CodingKeys.comments.stringValue: comments ] diff --git a/Adamant/SharedViews/TransferCollectionViewCell.swift b/Adamant/SharedViews/TransferCollectionViewCell.swift index 4bfb8cce5..94eaee7f9 100644 --- a/Adamant/SharedViews/TransferCollectionViewCell.swift +++ b/Adamant/SharedViews/TransferCollectionViewCell.swift @@ -10,14 +10,34 @@ import UIKit //import MessageKit class TransferCollectionViewCell: UICollectionViewCell, ChatCell, TapRecognizerTransferCell { + + // MARK: Hacks&Helpers + /// Comment label constraints inside transfer content view + static let commentLabelTrailAndLead: CGFloat = 24 + + /// Transfer status image size and space between status image and transfer bubble + static let statusImageSizeAndSpace: CGFloat = 42 + + /// Cell height without transfer comment + static let cellHeightCompact: CGFloat = 126 + + /// Cell height with transfer comment without comment label's height. You need to calculate label's height and add to this value. + static let cellHeightWithComment: CGFloat = 131 + + /// Comment label's font. Used to calculate total cell height + static let commentFont = UIFont.systemFont(ofSize: 14.0) + + // MARK: IBOutlets + @IBOutlet weak var sentLabel: UILabel! @IBOutlet weak var amountLabel: UILabel! @IBOutlet weak var currencySymbolLabel: UILabel! @IBOutlet weak var currencyLogoImageView: UIImageView! - @IBOutlet weak var tapForDetailsLabel: UILabel! + @IBOutlet weak var commentsLabel: UILabel! @IBOutlet weak var dateLabel: UILabel! @IBOutlet weak var transferContentView: UIView! + @IBOutlet weak var transferContentWidthConstraint: NSLayoutConstraint! @IBOutlet weak var bubbleView: UIView! @IBOutlet weak var leadingConstraint: NSLayoutConstraint? diff --git a/Adamant/SharedViews/TransferCollectionViewCell.xib b/Adamant/SharedViews/TransferCollectionViewCell.xib index a7fd0a0e5..6b8571c0a 100644 --- a/Adamant/SharedViews/TransferCollectionViewCell.xib +++ b/Adamant/SharedViews/TransferCollectionViewCell.xib @@ -1,11 +1,11 @@ - + @@ -55,14 +55,14 @@ - + -