diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index fd5688bdc..858853fc9 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -41,6 +41,24 @@ E905D39D204C13B900DDB504 /* SecuredStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E905D39C204C13B900DDB504 /* SecuredStore.swift */; }; E905D39F204C281400DDB504 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E905D39E204C281400DDB504 /* LoginViewController.swift */; }; E9061B97207501E40011F104 /* AdamantUserInfoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9061B96207501E40011F104 /* AdamantUserInfoKey.swift */; }; + E908471B2196FE590095825D /* Adamant.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = E90847192196FE590095825D /* Adamant.xcdatamodeld */; }; + E908472A2196FEA80095825D /* RichMessageTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E908471C2196FEA80095825D /* RichMessageTransaction+CoreDataClass.swift */; }; + E908472B2196FEA80095825D /* RichMessageTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E908471D2196FEA80095825D /* RichMessageTransaction+CoreDataProperties.swift */; }; + E908472C2196FEA80095825D /* CoreDataAccount+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E908471E2196FEA80095825D /* CoreDataAccount+CoreDataClass.swift */; }; + E908472D2196FEA80095825D /* CoreDataAccount+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E908471F2196FEA80095825D /* CoreDataAccount+CoreDataProperties.swift */; }; + E908472E2196FEA80095825D /* BaseTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847202196FEA80095825D /* BaseTransaction+CoreDataClass.swift */; }; + E908472F2196FEA80095825D /* BaseTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847212196FEA80095825D /* BaseTransaction+CoreDataProperties.swift */; }; + E90847302196FEA80095825D /* ChatTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847222196FEA80095825D /* ChatTransaction+CoreDataClass.swift */; }; + E90847312196FEA80095825D /* ChatTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847232196FEA80095825D /* ChatTransaction+CoreDataProperties.swift */; }; + E90847322196FEA80095825D /* TransferTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847242196FEA80095825D /* TransferTransaction+CoreDataClass.swift */; }; + E90847332196FEA80095825D /* TransferTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847252196FEA80095825D /* TransferTransaction+CoreDataProperties.swift */; }; + E90847342196FEA80095825D /* MessageTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847262196FEA80095825D /* MessageTransaction+CoreDataClass.swift */; }; + E90847352196FEA80095825D /* MessageTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847272196FEA80095825D /* MessageTransaction+CoreDataProperties.swift */; }; + E90847362196FEA80095825D /* Chatroom+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847282196FEA80095825D /* Chatroom+CoreDataClass.swift */; }; + E90847372196FEA80095825D /* Chatroom+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847292196FEA80095825D /* Chatroom+CoreDataProperties.swift */; }; + E90847392196FEF50095825D /* BaseTransaction+TransactionDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90847382196FEF50095825D /* BaseTransaction+TransactionDetails.swift */; }; + E908473B219707200095825D /* AccountViewController+StayIn.swift in Sources */ = {isa = PBXBuildFile; fileRef = E908473A219707200095825D /* AccountViewController+StayIn.swift */; }; + E908473D219713300095825D /* NotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E908473C219713300095825D /* NotificationsViewController.swift */; }; E90A4943204C5ED6009F6A65 /* EurekaPassphraseRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90A4942204C5ED6009F6A65 /* EurekaPassphraseRow.swift */; }; E90A4945204C6204009F6A65 /* PassphraseCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E90A4944204C5F60009F6A65 /* PassphraseCell.xib */; }; E90A494B204D9EB8009F6A65 /* AdamantAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90A494A204D9EB8009F6A65 /* AdamantAuthentication.swift */; }; @@ -54,18 +72,6 @@ E9147B6320505C7500145913 /* QRCodeReader+adamant.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9147B6220505C7500145913 /* QRCodeReader+adamant.swift */; }; E9147B6F205088DE00145913 /* LoginViewController+Pinpad.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9147B6E205088DE00145913 /* LoginViewController+Pinpad.swift */; }; E9150B802066861D0065A985 /* ChatViewController+MessageKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B7F2066861D0065A985 /* ChatViewController+MessageKit.swift */; }; - E9150B972066DA210065A985 /* CoreDataAccount+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B8B2066DA210065A985 /* CoreDataAccount+CoreDataClass.swift */; }; - E9150B982066DA210065A985 /* CoreDataAccount+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B8C2066DA210065A985 /* CoreDataAccount+CoreDataProperties.swift */; }; - E9150B992066DA210065A985 /* TransferTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B8D2066DA210065A985 /* TransferTransaction+CoreDataClass.swift */; }; - E9150B9A2066DA210065A985 /* TransferTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B8E2066DA210065A985 /* TransferTransaction+CoreDataProperties.swift */; }; - E9150B9B2066DA210065A985 /* BaseTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B8F2066DA210065A985 /* BaseTransaction+CoreDataClass.swift */; }; - E9150B9C2066DA210065A985 /* BaseTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B902066DA210065A985 /* BaseTransaction+CoreDataProperties.swift */; }; - E9150B9D2066DA210065A985 /* Chatroom+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B912066DA210065A985 /* Chatroom+CoreDataClass.swift */; }; - E9150B9E2066DA210065A985 /* Chatroom+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B922066DA210065A985 /* Chatroom+CoreDataProperties.swift */; }; - E9150B9F2066DA210065A985 /* MessageTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B932066DA210065A985 /* MessageTransaction+CoreDataClass.swift */; }; - E9150BA02066DA210065A985 /* MessageTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B942066DA210065A985 /* MessageTransaction+CoreDataProperties.swift */; }; - E9150BA12066DA210065A985 /* ChatTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B952066DA210065A985 /* ChatTransaction+CoreDataClass.swift */; }; - E9150BA22066DA210065A985 /* ChatTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9150B962066DA210065A985 /* ChatTransaction+CoreDataProperties.swift */; }; E91947AC20001A9A001362F8 /* ApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91947AB20001A9A001362F8 /* ApiService.swift */; }; E91947B020002393001362F8 /* AdamantApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91947AF20002393001362F8 /* AdamantApiService.swift */; }; E91947B22000246A001362F8 /* AdamantError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91947B12000246A001362F8 /* AdamantError.swift */; }; @@ -79,7 +85,7 @@ E921597520611A6A0000CA5C /* AdamantReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921597420611A6A0000CA5C /* AdamantReachability.swift */; }; E921597B206503000000CA5C /* ButtonsStripeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921597A206503000000CA5C /* ButtonsStripeView.swift */; }; E921597D2065031D0000CA5C /* ButtonsStripe.xib in Resources */ = {isa = PBXBuildFile; fileRef = E921597C2065031D0000CA5C /* ButtonsStripe.xib */; }; - E9220E0D21988EEA009C9642 /* ChatModels.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = E95F85B1200954D00070534A /* ChatModels.xcdatamodeld */; }; + E9220E0D21988EEA009C9642 /* (null) in Sources */ = {isa = PBXBuildFile; }; E9220E1121988F81009C9642 /* JSAdamantCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9220E0221983155009C9642 /* JSAdamantCore.swift */; }; E9220E1221988F81009C9642 /* adamant-core.js in Resources */ = {isa = PBXBuildFile; fileRef = E9220E0121983155009C9642 /* adamant-core.js */; }; E9220E1321988F81009C9642 /* JSAdamantCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85762007E8EC0070534A /* JSAdamantCoreTests.swift */; }; @@ -196,8 +202,6 @@ E9AA8BFA212C166600F9249F /* EthWalletService+Send.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9AA8BF9212C166600F9249F /* EthWalletService+Send.swift */; }; E9AA8BFC212C169200F9249F /* EthWalletService+Transfers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9AA8BFB212C169200F9249F /* EthWalletService+Transfers.swift */; }; E9AA8C02212C5BF500F9249F /* AdmWalletService+Send.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9AA8C01212C5BF500F9249F /* AdmWalletService+Send.swift */; }; - E9AD78812158EED300742061 /* RichMessageTransaction+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9AD787F2158EED300742061 /* RichMessageTransaction+CoreDataClass.swift */; }; - E9AD78822158EED300742061 /* RichMessageTransaction+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9AD78802158EED300742061 /* RichMessageTransaction+CoreDataProperties.swift */; }; E9B1AA572121ACC000080A2A /* AdmWalletViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9B1AA562121ACBF00080A2A /* AdmWalletViewController.swift */; }; E9B1AA592122D59600080A2A /* WalletsRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9B1AA582122D59600080A2A /* WalletsRoutes.swift */; }; E9B1AA5B21283E0F00080A2A /* AdmTransferViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9B1AA5A21283E0F00080A2A /* AdmTransferViewController.swift */; }; @@ -218,7 +222,6 @@ E9D1BE1A211DA25300E86B72 /* UIView+constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D1BE19211DA25300E86B72 /* UIView+constraints.swift */; }; E9D1BE1C211DABE100E86B72 /* WalletPagingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D1BE1B211DABE100E86B72 /* WalletPagingItem.swift */; }; E9DFB71C21624C9200CF8C7C /* AdmTransactionDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DFB71B21624C9200CF8C7C /* AdmTransactionDetailsViewController.swift */; }; - E9DFB720216619F400CF8C7C /* BaseTransaction+TransactionDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DFB71F216619F400CF8C7C /* BaseTransaction+TransactionDetails.swift */; }; E9E7CD8B20026B0600DFC4DB /* AccountService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CD8A20026B0600DFC4DB /* AccountService.swift */; }; E9E7CD8D20026B6600DFC4DB /* DialogService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CD8C20026B6600DFC4DB /* DialogService.swift */; }; E9E7CD8F20026CD300DFC4DB /* AdamantDialogService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CD8E20026CD300DFC4DB /* AdamantDialogService.swift */; }; @@ -252,6 +255,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 */; }; @@ -309,6 +313,24 @@ E905D39E204C281400DDB504 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; E9061B96207501E40011F104 /* AdamantUserInfoKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantUserInfoKey.swift; sourceTree = ""; }; E9061B982077AF8E0011F104 /* Adamant.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Adamant.entitlements; sourceTree = ""; }; + E908471A2196FE590095825D /* Adamant.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Adamant.xcdatamodel; sourceTree = ""; }; + E908471C2196FEA80095825D /* RichMessageTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichMessageTransaction+CoreDataClass.swift"; sourceTree = ""; }; + E908471D2196FEA80095825D /* RichMessageTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichMessageTransaction+CoreDataProperties.swift"; sourceTree = ""; }; + E908471E2196FEA80095825D /* CoreDataAccount+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreDataAccount+CoreDataClass.swift"; sourceTree = ""; }; + E908471F2196FEA80095825D /* CoreDataAccount+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreDataAccount+CoreDataProperties.swift"; sourceTree = ""; }; + E90847202196FEA80095825D /* BaseTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTransaction+CoreDataClass.swift"; sourceTree = ""; }; + E90847212196FEA80095825D /* BaseTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTransaction+CoreDataProperties.swift"; sourceTree = ""; }; + E90847222196FEA80095825D /* ChatTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatTransaction+CoreDataClass.swift"; sourceTree = ""; }; + E90847232196FEA80095825D /* ChatTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatTransaction+CoreDataProperties.swift"; sourceTree = ""; }; + E90847242196FEA80095825D /* TransferTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TransferTransaction+CoreDataClass.swift"; sourceTree = ""; }; + E90847252196FEA80095825D /* TransferTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TransferTransaction+CoreDataProperties.swift"; sourceTree = ""; }; + E90847262196FEA80095825D /* MessageTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageTransaction+CoreDataClass.swift"; sourceTree = ""; }; + E90847272196FEA80095825D /* MessageTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageTransaction+CoreDataProperties.swift"; sourceTree = ""; }; + E90847282196FEA80095825D /* Chatroom+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Chatroom+CoreDataClass.swift"; sourceTree = ""; }; + E90847292196FEA80095825D /* Chatroom+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Chatroom+CoreDataProperties.swift"; sourceTree = ""; }; + E90847382196FEF50095825D /* BaseTransaction+TransactionDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTransaction+TransactionDetails.swift"; sourceTree = ""; }; + E908473A219707200095825D /* AccountViewController+StayIn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountViewController+StayIn.swift"; sourceTree = ""; }; + E908473C219713300095825D /* NotificationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsViewController.swift; sourceTree = ""; }; E90A4942204C5ED6009F6A65 /* EurekaPassphraseRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EurekaPassphraseRow.swift; sourceTree = ""; }; E90A4944204C5F60009F6A65 /* PassphraseCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PassphraseCell.xib; sourceTree = ""; }; E90A494A204D9EB8009F6A65 /* AdamantAuthentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantAuthentication.swift; sourceTree = ""; }; @@ -327,18 +349,6 @@ E9147B6220505C7500145913 /* QRCodeReader+adamant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QRCodeReader+adamant.swift"; sourceTree = ""; }; E9147B6E205088DE00145913 /* LoginViewController+Pinpad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoginViewController+Pinpad.swift"; sourceTree = ""; }; E9150B7F2066861D0065A985 /* ChatViewController+MessageKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatViewController+MessageKit.swift"; sourceTree = ""; }; - E9150B8B2066DA210065A985 /* CoreDataAccount+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreDataAccount+CoreDataClass.swift"; sourceTree = ""; }; - E9150B8C2066DA210065A985 /* CoreDataAccount+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreDataAccount+CoreDataProperties.swift"; sourceTree = ""; }; - E9150B8D2066DA210065A985 /* TransferTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TransferTransaction+CoreDataClass.swift"; sourceTree = ""; }; - E9150B8E2066DA210065A985 /* TransferTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TransferTransaction+CoreDataProperties.swift"; sourceTree = ""; }; - E9150B8F2066DA210065A985 /* BaseTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTransaction+CoreDataClass.swift"; sourceTree = ""; }; - E9150B902066DA210065A985 /* BaseTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTransaction+CoreDataProperties.swift"; sourceTree = ""; }; - E9150B912066DA210065A985 /* Chatroom+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Chatroom+CoreDataClass.swift"; sourceTree = ""; }; - E9150B922066DA210065A985 /* Chatroom+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Chatroom+CoreDataProperties.swift"; sourceTree = ""; }; - E9150B932066DA210065A985 /* MessageTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageTransaction+CoreDataClass.swift"; sourceTree = ""; }; - E9150B942066DA210065A985 /* MessageTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageTransaction+CoreDataProperties.swift"; sourceTree = ""; }; - E9150B952066DA210065A985 /* ChatTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatTransaction+CoreDataClass.swift"; sourceTree = ""; }; - E9150B962066DA210065A985 /* ChatTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatTransaction+CoreDataProperties.swift"; sourceTree = ""; }; E919479F200010DC001362F8 /* Exo+2_400_italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo+2_400_italic.ttf"; sourceTree = ""; }; E91947A0200010DC001362F8 /* Roboto_300_normal.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Roboto_300_normal.ttf; sourceTree = ""; }; E91947A1200010DD001362F8 /* Roboto_400_italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Roboto_400_italic.ttf; sourceTree = ""; }; @@ -429,7 +439,6 @@ E95F85842008CB3A0070534A /* ChatListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewController.swift; sourceTree = ""; }; E95F85862008FDBF0070534A /* ChatAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatAsset.swift; sourceTree = ""; }; E95F8588200900B10070534A /* ChatType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatType.swift; sourceTree = ""; }; - E95F85B2200954D00070534A /* ChatModels.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = ChatModels.xcdatamodel; sourceTree = ""; }; E95F85B6200A4D8F0070534A /* TestTools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestTools.swift; sourceTree = ""; }; E95F85B9200A4DC90070534A /* TransactionSend.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = TransactionSend.json; sourceTree = ""; }; E95F85BB200A4E670070534A /* ParsingModelsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParsingModelsTests.swift; sourceTree = ""; }; @@ -483,8 +492,6 @@ E9AA8BF9212C166600F9249F /* EthWalletService+Send.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EthWalletService+Send.swift"; sourceTree = ""; }; E9AA8BFB212C169200F9249F /* EthWalletService+Transfers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EthWalletService+Transfers.swift"; sourceTree = ""; }; E9AA8C01212C5BF500F9249F /* AdmWalletService+Send.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdmWalletService+Send.swift"; sourceTree = ""; }; - E9AD787F2158EED300742061 /* RichMessageTransaction+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichMessageTransaction+CoreDataClass.swift"; sourceTree = ""; }; - E9AD78802158EED300742061 /* RichMessageTransaction+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichMessageTransaction+CoreDataProperties.swift"; sourceTree = ""; }; E9B1AA562121ACBF00080A2A /* AdmWalletViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdmWalletViewController.swift; sourceTree = ""; }; E9B1AA582122D59600080A2A /* WalletsRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletsRoutes.swift; sourceTree = ""; }; E9B1AA5A21283E0F00080A2A /* AdmTransferViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdmTransferViewController.swift; sourceTree = ""; }; @@ -506,7 +513,6 @@ E9D1BE1B211DABE100E86B72 /* WalletPagingItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletPagingItem.swift; sourceTree = ""; }; E9DFB71B21624C9200CF8C7C /* AdmTransactionDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdmTransactionDetailsViewController.swift; sourceTree = ""; }; E9DFB71D21651FBD00CF8C7C /* JSAdamantCore+Native.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSAdamantCore+Native.swift"; sourceTree = ""; }; - E9DFB71F216619F400CF8C7C /* BaseTransaction+TransactionDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTransaction+TransactionDetails.swift"; sourceTree = ""; }; E9E7CD8A20026B0600DFC4DB /* AccountService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountService.swift; sourceTree = ""; }; E9E7CD8C20026B6600DFC4DB /* DialogService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialogService.swift; sourceTree = ""; }; E9E7CD8E20026CD300DFC4DB /* AdamantDialogService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdamantDialogService.swift; sourceTree = ""; }; @@ -531,6 +537,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 = ""; }; @@ -708,6 +715,7 @@ E940086A2114A70600CD2D67 /* LskAccount.swift */, E993302321369B8A00CD5200 /* RichMessage.swift */, E971591921681D6900A5F904 /* TransactionStatus.swift */, + E9FCA1E5218334C00005E83D /* SimpleTransactionDetails.swift */, ); path = Models; sourceTree = ""; @@ -933,22 +941,22 @@ E95F859220094B8E0070534A /* CoreData */ = { isa = PBXGroup; children = ( - E9AD787F2158EED300742061 /* RichMessageTransaction+CoreDataClass.swift */, - E9AD78802158EED300742061 /* RichMessageTransaction+CoreDataProperties.swift */, - E9150B8F2066DA210065A985 /* BaseTransaction+CoreDataClass.swift */, - E9150B902066DA210065A985 /* BaseTransaction+CoreDataProperties.swift */, - E9DFB71F216619F400CF8C7C /* BaseTransaction+TransactionDetails.swift */, - E9150B912066DA210065A985 /* Chatroom+CoreDataClass.swift */, - E9150B922066DA210065A985 /* Chatroom+CoreDataProperties.swift */, - E9150B952066DA210065A985 /* ChatTransaction+CoreDataClass.swift */, - E9150B962066DA210065A985 /* ChatTransaction+CoreDataProperties.swift */, - E9150B8B2066DA210065A985 /* CoreDataAccount+CoreDataClass.swift */, - E9150B8C2066DA210065A985 /* CoreDataAccount+CoreDataProperties.swift */, - E9150B932066DA210065A985 /* MessageTransaction+CoreDataClass.swift */, - E9150B942066DA210065A985 /* MessageTransaction+CoreDataProperties.swift */, - E9150B8D2066DA210065A985 /* TransferTransaction+CoreDataClass.swift */, - E9150B8E2066DA210065A985 /* TransferTransaction+CoreDataProperties.swift */, - E95F85B1200954D00070534A /* ChatModels.xcdatamodeld */, + E90847202196FEA80095825D /* BaseTransaction+CoreDataClass.swift */, + E90847212196FEA80095825D /* BaseTransaction+CoreDataProperties.swift */, + E90847382196FEF50095825D /* BaseTransaction+TransactionDetails.swift */, + E90847222196FEA80095825D /* ChatTransaction+CoreDataClass.swift */, + E90847232196FEA80095825D /* ChatTransaction+CoreDataProperties.swift */, + E90847262196FEA80095825D /* MessageTransaction+CoreDataClass.swift */, + E90847272196FEA80095825D /* MessageTransaction+CoreDataProperties.swift */, + E90847242196FEA80095825D /* TransferTransaction+CoreDataClass.swift */, + E90847252196FEA80095825D /* TransferTransaction+CoreDataProperties.swift */, + E908471C2196FEA80095825D /* RichMessageTransaction+CoreDataClass.swift */, + E908471D2196FEA80095825D /* RichMessageTransaction+CoreDataProperties.swift */, + E908471E2196FEA80095825D /* CoreDataAccount+CoreDataClass.swift */, + E908471F2196FEA80095825D /* CoreDataAccount+CoreDataProperties.swift */, + E90847282196FEA80095825D /* Chatroom+CoreDataClass.swift */, + E90847292196FEA80095825D /* Chatroom+CoreDataProperties.swift */, + E90847192196FE590095825D /* Adamant.xcdatamodeld */, ); path = CoreData; sourceTree = ""; @@ -975,6 +983,7 @@ E90055F820ECD86800D0CB2D /* SecurityViewController+StayIn.swift */, E90055FA20ECE78A00D0CB2D /* SecurityViewController+notifications.swift */, E9942B7F203C058C00C163AF /* QRGeneratorViewController.swift */, + E908473C219713300095825D /* NotificationsViewController.swift */, ); path = Settings; sourceTree = ""; @@ -1044,6 +1053,7 @@ children = ( E9E7CDB02002B97B00DFC4DB /* AccountRoutes.swift */, E983AE2820E65F3200497E1A /* AccountViewController.swift */, + E908473A219707200095825D /* AccountViewController+StayIn.swift */, E983AE2020E655C500497E1A /* AccountHeaderView.swift */, E941CCDA20E786D700C96220 /* AccountHeader.xib */, E983AE2C20E6720D00497E1A /* AccountFooter.xib */, @@ -1390,13 +1400,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E908472A2196FEA80095825D /* RichMessageTransaction+CoreDataClass.swift in Sources */, 64A223D620F760BB005157CB /* Localization.swift in Sources */, E94E7B08205D4CB80042B639 /* SharedRoutes.swift in Sources */, E9256F5F2034C21100DE86E9 /* String+localized.swift in Sources */, E9CAE8D22018AA7700345E76 /* AdamantApi+Accounts.swift in Sources */, E987024920C2B1F700E393F4 /* AdamantChatsProvider+fakeMessages.swift in Sources */, E9942B84203CBFCE00C163AF /* AdamantQRTools.swift in Sources */, - E9AD78812158EED300742061 /* RichMessageTransaction+CoreDataClass.swift in Sources */, + E908472F2196FEA80095825D /* BaseTransaction+CoreDataProperties.swift in Sources */, E94008872114F05B00CD2D67 /* AddressValidationResult.swift in Sources */, E974D168215D033C003AD7E8 /* ChatCell.swift in Sources */, 64A223D820F7A08E005157CB /* LskApiService.swift in Sources */, @@ -1404,6 +1415,7 @@ E9E7CD8F20026CD300DFC4DB /* AdamantDialogService.swift in Sources */, E993301E212EF39700CD5200 /* EthTransferViewController.swift in Sources */, E9CAE8DA2018ACD300345E76 /* AdamantApi+Chats.swift in Sources */, + E90847362196FEA80095825D /* Chatroom+CoreDataClass.swift in Sources */, E91947B020002393001362F8 /* AdamantApiService.swift in Sources */, E921597B206503000000CA5C /* ButtonsStripeView.swift in Sources */, E9A03FD820DC0ABA007653A1 /* AdamantNodesSource.swift in Sources */, @@ -1412,22 +1424,19 @@ E9147B6320505C7500145913 /* QRCodeReader+adamant.swift in Sources */, E90055F720EC200900D0CB2D /* SecurityViewController.swift in Sources */, 644EC35E20F34F1E00F40C73 /* DelegateDetailsViewController.swift in Sources */, - 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 */, E9204B5020C94C4A00F3B9AB /* Date+humanizedString.swift in Sources */, E9E7CD9120026FA100DFC4DB /* SwinjectDependencies.swift in Sources */, 64BD2B7520E2814B00E2CD36 /* EthTransaction.swift in Sources */, + E908472B2196FEA80095825D /* RichMessageTransaction+CoreDataProperties.swift in Sources */, E9E7CD8B20026B0600DFC4DB /* AccountService.swift in Sources */, 64F085D920E2D7600006DE68 /* AdmTransactionsViewController.swift in Sources */, E9D1BE1C211DABE100E86B72 /* WalletPagingItem.swift in Sources */, - E9150B992066DA210065A985 /* TransferTransaction+CoreDataClass.swift in Sources */, E940086E2114AA2E00CD2D67 /* WalletService.swift in Sources */, - E9150BA02066DA210065A985 /* MessageTransaction+CoreDataProperties.swift in Sources */, - E9AD78822158EED300742061 /* RichMessageTransaction+CoreDataProperties.swift in Sources */, E9B3D39A201F90570019EB36 /* AccountsProvider.swift in Sources */, E950652320404C84008352E5 /* AdamantUriTools.swift in Sources */, E95F85C7200A9B070070534A /* ChatTableViewCell.swift in Sources */, @@ -1449,11 +1458,11 @@ E91947B420002809001362F8 /* AdamantAccount.swift in Sources */, E9E7CDC22003F5A400DFC4DB /* TransactionsListViewControllerBase.swift in Sources */, E905D39F204C281400DDB504 /* LoginViewController.swift in Sources */, - E9150B9C2066DA210065A985 /* BaseTransaction+CoreDataProperties.swift in Sources */, E9B1AA592122D59600080A2A /* WalletsRoutes.swift in Sources */, E971591A21681D6900A5F904 /* TransactionStatus.swift in Sources */, E9E7CDB52002BA6900DFC4DB /* SwinjectedRouter.swift in Sources */, E95F85872008FDBF0070534A /* ChatAsset.swift in Sources */, + E908472C2196FEA80095825D /* CoreDataAccount+CoreDataClass.swift in Sources */, E9A03FD220DBC0F2007653A1 /* NodeEditorViewController.swift in Sources */, E9393FAA2055D03300EE6F30 /* AdamantMessage.swift in Sources */, E90A494D204DA932009F6A65 /* LocalAuthentication.swift in Sources */, @@ -1467,14 +1476,14 @@ E9A174B72057F1B3003667CD /* AdamantChatsProvider+backgroundFetch.swift in Sources */, E9E7CDB32002B9FB00DFC4DB /* LoginRoutes.swift in Sources */, E941CCDE20E7B70200C96220 /* WalletCollectionViewCell.swift in Sources */, - E9150B9E2066DA210065A985 /* Chatroom+CoreDataProperties.swift in Sources */, E9AA8BFA212C166600F9249F /* EthWalletService+Send.swift in Sources */, - E9220E0D21988EEA009C9642 /* ChatModels.xcdatamodeld in Sources */, + E9220E0D21988EEA009C9642 /* (null) in Sources */, E91947B22000246A001362F8 /* AdamantError.swift in Sources */, E95F85802008C8D70070534A /* ChatsRoutes.swift in Sources */, E9942B87203D9E5100C163AF /* EurekaQRRow.swift in Sources */, E93EFE13200D1156000BB482 /* ChatViewController.swift in Sources */, E9AA8C02212C5BF500F9249F /* AdmWalletService+Send.swift in Sources */, + E90847332196FEA80095825D /* TransferTransaction+CoreDataProperties.swift in Sources */, E9FEECAC2143CEED007DD7C8 /* TransferMessageSizeCalculator.swift in Sources */, E927171E20C04614002BB9A6 /* UIColor+hex.swift in Sources */, E99818942120892F0018C84C /* WalletViewControllerBase.swift in Sources */, @@ -1487,24 +1496,24 @@ E9DFB71C21624C9200CF8C7C /* AdmTransactionDetailsViewController.swift in Sources */, E94008722114EACF00CD2D67 /* WalletAccount.swift in Sources */, E93B0D742028B21400126346 /* ChatsProvider.swift in Sources */, - E9150BA12066DA210065A985 /* ChatTransaction+CoreDataClass.swift in Sources */, E9CAE8D42018AC1800345E76 /* AdamantApi+Keys.swift in Sources */, E9C51EEF20139DC600385EB7 /* TransactionIdResponse.swift in Sources */, E9722066201F42BB004F2AAD /* CoreDataStack.swift in Sources */, E913C8F21FFFA51D001A83F7 /* AppDelegate.swift in Sources */, E905D39B2048A9BD00DDB504 /* KeychainStore.swift in Sources */, - E9DFB720216619F400CF8C7C /* BaseTransaction+TransactionDetails.swift in Sources */, E9E7CDCA20040CC200DFC4DB /* Transaction.swift in Sources */, E9AA8BFC212C169200F9249F /* EthWalletService+Transfers.swift in Sources */, 649E9A152111B3C200686B01 /* Mnemonic.swift in Sources */, E95F85692006AB9D0070534A /* NormalizedTransaction.swift in Sources */, - E9150B9B2066DA210065A985 /* BaseTransaction+CoreDataClass.swift in Sources */, - E9150B9A2066DA210065A985 /* TransferTransaction+CoreDataProperties.swift in Sources */, E913C90D1FFFA99B001A83F7 /* Keypair.swift in Sources */, E9150B802066861D0065A985 /* ChatViewController+MessageKit.swift in Sources */, E90055F920ECD86800D0CB2D /* SecurityViewController+StayIn.swift in Sources */, + E90847322196FEA80095825D /* TransferTransaction+CoreDataClass.swift in Sources */, + E90847392196FEF50095825D /* BaseTransaction+TransactionDetails.swift in Sources */, + E908473B219707200095825D /* AccountViewController+StayIn.swift in Sources */, E9204B5220C9762400F3B9AB /* MessageStatus.swift in Sources */, E993302421369B8A00CD5200 /* RichMessage.swift in Sources */, + E908471B2196FE590095825D /* Adamant.xcdatamodeld in Sources */, E940087B2114ED0600CD2D67 /* EthWalletService.swift in Sources */, E948E03B20235E2300975D6B /* SettingsRoutes.swift in Sources */, E9CAE8D82018ACA700345E76 /* AdamantApi+Transfers.swift in Sources */, @@ -1513,20 +1522,20 @@ E9FEECA62143C300007DD7C8 /* EthWalletService+RichMessageProvider.swift in Sources */, E91947AC20001A9A001362F8 /* ApiService.swift in Sources */, E993302221354BC300CD5200 /* EthWalletRoutes.swift in Sources */, - E9150B9F2066DA210065A985 /* MessageTransaction+CoreDataClass.swift in Sources */, E90055F520EBF5DA00D0CB2D /* AboutViewController.swift in Sources */, E965A53020B594120041A3EA /* AdamantApi+States.swift in Sources */, + E908473D219713300095825D /* NotificationsViewController.swift in Sources */, + E908472E2196FEA80095825D /* BaseTransaction+CoreDataClass.swift in Sources */, 64D059FF20D3116B003AD655 /* NodesListViewController.swift in Sources */, E91E5BF220DAF05500B06B3C /* EurekaNodeRow.swift in Sources */, + E90847312196FEA80095825D /* ChatTransaction+CoreDataProperties.swift in Sources */, E99330262136B0E500CD5200 /* TransferViewControllerBase+QR.swift in Sources */, - E9150B982066DA210065A985 /* CoreDataAccount+CoreDataProperties.swift in Sources */, E9B1AA5B21283E0F00080A2A /* AdmTransferViewController.swift in Sources */, E940086B2114A70600CD2D67 /* LskAccount.swift in Sources */, E9B3D3A1201FA26B0019EB36 /* AdamantAccountsProvider.swift in Sources */, E9FAE5DA203DBFEF008D3A6B /* Comparable+clamped.swift in Sources */, E94008802114EE2000CD2D67 /* AdmWallet.swift in Sources */, E9A03FDA20DC0B14007653A1 /* NodesSource.swift in Sources */, - E9150B972066DA210065A985 /* CoreDataAccount+CoreDataClass.swift in Sources */, E926E02E213EAABF005E536B /* TransferViewControllerBase+Alert.swift in Sources */, E9B1AA572121ACC000080A2A /* AdmWalletViewController.swift in Sources */, E95F857A2007F0260070534A /* ServerResponse.swift in Sources */, @@ -1554,18 +1563,22 @@ E93D7AC02052CF63005D19DC /* AdamantNotificationService.swift in Sources */, E93B0D762028B28E00126346 /* AdamantChatsProvider.swift in Sources */, E993302021354B1800CD5200 /* AdmWalletRoutes.swift in Sources */, + E908472D2196FEA80095825D /* CoreDataAccount+CoreDataProperties.swift in Sources */, 64FA53D120E24942006783C9 /* TransactionDetailsViewControllerBase.swift in Sources */, 64BD2B7720E2820300E2CD36 /* TransactionDetails.swift in Sources */, E94008852114EE7500CD2D67 /* LskWalletService.swift in Sources */, E96E86B821679C120061F80A /* EthTransactionDetailsViewController.swift in Sources */, E90A4943204C5ED6009F6A65 /* EurekaPassphraseRow.swift in Sources */, + E90847302196FEA80095825D /* ChatTransaction+CoreDataClass.swift in Sources */, E965A53420B833A00041A3EA /* StateAsset.swift in Sources */, E9393FA82055C92700EE6F30 /* Decimal+adamant.swift in Sources */, E95F8589200900B10070534A /* ChatType.swift in Sources */, E913C9081FFFA943001A83F7 /* AdamantCore.swift in Sources */, E9EC342120052ABB00C0E546 /* TransferViewControllerBase.swift in Sources */, E921534E20EE1E8700C0843F /* EurekaAlertLabelRow.swift in Sources */, + E90847352196FEA80095825D /* MessageTransaction+CoreDataProperties.swift in Sources */, E9A03FD420DBC824007653A1 /* NodeVersion.swift in Sources */, + E90847342196FEA80095825D /* MessageTransaction+CoreDataClass.swift in Sources */, E94008892114F0F700CD2D67 /* AdmWalletService.swift in Sources */, 64EE46B220FE0C8D00194DDA /* LskTransactionsViewController.swift in Sources */, E94008832114EE4700CD2D67 /* LskWallet.swift in Sources */, @@ -1579,6 +1592,7 @@ E9147B5F20500E9300145913 /* MyLittlePinpad+adamant.swift in Sources */, E9981896212095CA0018C84C /* EthWalletViewController.swift in Sources */, E98FC34620F9210100032D65 /* Date+adamant.swift in Sources */, + E90847372196FEA80095825D /* Chatroom+CoreDataProperties.swift in Sources */, E905D39D204C13B900DDB504 /* SecuredStore.swift in Sources */, E98FC34820F921EA00032D65 /* DelegateVote.swift in Sources */, E90055FB20ECE78A00D0CB2D /* SecurityViewController+notifications.swift in Sources */, @@ -1878,13 +1892,13 @@ /* End XCConfigurationList section */ /* Begin XCVersionGroup section */ - E95F85B1200954D00070534A /* ChatModels.xcdatamodeld */ = { + E90847192196FE590095825D /* Adamant.xcdatamodeld */ = { isa = XCVersionGroup; children = ( - E95F85B2200954D00070534A /* ChatModels.xcdatamodel */, + E908471A2196FE590095825D /* Adamant.xcdatamodel */, ); - currentVersion = E95F85B2200954D00070534A /* ChatModels.xcdatamodel */; - path = ChatModels.xcdatamodeld; + currentVersion = E908471A2196FE590095825D /* Adamant.xcdatamodel */; + path = Adamant.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; }; diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index 416ae81bc..4f2ae5d13 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -36,19 +36,19 @@ extension StoreKey { // MARK: - Resources struct AdamantResources { - static let coreDataModel = Bundle.main.url(forResource: "ChatModels", withExtension: "momd")! + static let coreDataModel = Bundle.main.url(forResource: "Adamant", withExtension: "momd")! static let nodes: [Node] = [ - Node(scheme: .https, host: "endless.adamant.im", port: nil), - Node(scheme: .https, host: "clown.adamant.im", port: nil), - Node(scheme: .https, host: "lake.adamant.im", port: nil), + Node(scheme: .https, host: "endless.adamant.im", port: nil), + Node(scheme: .https, host: "clown.adamant.im", port: nil), + Node(scheme: .https, host: "lake.adamant.im", port: nil), // Node(scheme: .http, host: "80.211.177.181", port: nil), // Bugged one -// Node(scheme: .http, host: "163.172.183.198", port: nil) // Testnet +// Node(scheme: .http, host: "163.172.132.38", port: 36667) // Testnet ] static let ethServers = [ -// "https://ethnode1.adamant.im/" - "https://ropsten.infura.io/" // test network + "https://ethnode1.adamant.im/" +// "https://ropsten.infura.io/" // test network ] // Addresses @@ -69,8 +69,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() {} } @@ -210,7 +210,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 } @@ -388,6 +388,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...") } @@ -407,7 +411,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"] { @@ -416,7 +427,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/Assets/Assets.xcassets/Buttons/Attachment.imageset/Attachment.png b/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Attachment.png new file mode 100644 index 000000000..e56e76c9d Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Attachment.png differ diff --git a/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Attachment@2x.png b/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Attachment@2x.png new file mode 100644 index 000000000..2fa4f2343 Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Attachment@2x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Attachment@3x.png b/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Attachment@3x.png new file mode 100644 index 000000000..58a979302 Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Attachment@3x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Contents.json b/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Contents.json new file mode 100644 index 000000000..4a3f44c25 --- /dev/null +++ b/Adamant/Assets/Assets.xcassets/Buttons/Attachment.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Attachment.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Attachment@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Attachment@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/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 000000000..7dda95dc5 Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney.png differ 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 000000000..1dad6d686 Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney@2x.png differ 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 000000000..f0bf131fe Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Buttons/SendMoney.imageset/SendMoney@3x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/attachment.png b/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/attachment.png deleted file mode 100644 index 6a43d5685..000000000 Binary files a/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/attachment.png and /dev/null differ 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 1dc44dbff..000000000 Binary files a/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/attachment@2x.png and /dev/null differ diff --git a/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/attachment@3x.png b/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/attachment@3x.png deleted file mode 100644 index ff9d96d71..000000000 Binary files a/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/attachment@3x.png and /dev/null differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/Contents.json b/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/Contents.json new file mode 100644 index 000000000..c86f6b844 --- /dev/null +++ b/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "row_Notifications.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "row_Notifications@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "row_Notifications@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/Row/row_Notifications.imageset/row_Notifications.png b/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/row_Notifications.png new file mode 100644 index 000000000..26eee9baa Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/row_Notifications.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/row_Notifications@2x.png b/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/row_Notifications@2x.png new file mode 100644 index 000000000..c437032ab Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/row_Notifications@2x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/row_Notifications@3x.png b/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/row_Notifications@3x.png new file mode 100644 index 000000000..3ec104803 Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_Notifications.imageset/row_Notifications@3x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/Contents.json b/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/Contents.json similarity index 60% rename from Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/Contents.json rename to Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/Contents.json index dc0cd0535..722fd96da 100644 --- a/Adamant/Assets/Assets.xcassets/Icons/attachment.imageset/Contents.json +++ b/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/Contents.json @@ -2,22 +2,25 @@ "images" : [ { "idiom" : "universal", - "filename" : "attachment.png", + "filename" : "row_QR.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "attachment@2x.png", + "filename" : "row_QR@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "attachment@3x.png", + "filename" : "row_QR@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/Row/row_QR.imageset/row_QR.png b/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/row_QR.png new file mode 100644 index 000000000..75b070e40 Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/row_QR.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/row_QR@2x.png b/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/row_QR@2x.png new file mode 100644 index 000000000..14c87fb7a Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/row_QR@2x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/row_QR@3x.png b/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/row_QR@3x.png new file mode 100644 index 000000000..7a33e7d84 Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_QR.imageset/row_QR@3x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/Contents.json b/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/Contents.json new file mode 100644 index 000000000..91863b5f7 --- /dev/null +++ b/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "row_faceid.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "row_faceid@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "row_faceid@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/Row/row_faceid.imageset/row_faceid.png b/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/row_faceid.png new file mode 100644 index 000000000..fa075c07a Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/row_faceid.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/row_faceid@2x.png b/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/row_faceid@2x.png new file mode 100644 index 000000000..441ff22af Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/row_faceid@2x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/row_faceid@3x.png b/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/row_faceid@3x.png new file mode 100644 index 000000000..4d99bc8b7 Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_faceid.imageset/row_faceid@3x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/Contents.json b/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/Contents.json new file mode 100644 index 000000000..d07f8f42a --- /dev/null +++ b/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "row_touchid.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "row_touchid@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "row_touchid@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/Row/row_touchid.imageset/row_touchid.png b/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/row_touchid.png new file mode 100644 index 000000000..4e8ffba9a Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/row_touchid.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/row_touchid@2x.png b/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/row_touchid@2x.png new file mode 100644 index 000000000..cf66f1d3a Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/row_touchid@2x.png differ diff --git a/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/row_touchid@3x.png b/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/row_touchid@3x.png new file mode 100644 index 000000000..e558ea6e4 Binary files /dev/null and b/Adamant/Assets/Assets.xcassets/Row/row_touchid.imageset/row_touchid@3x.png differ diff --git a/Adamant/Assets/l18n/de.lproj/Localizable.strings b/Adamant/Assets/l18n/de.lproj/Localizable.strings index 8694f0d0e..487cc6aee 100755 --- a/Adamant/Assets/l18n/de.lproj/Localizable.strings +++ b/Adamant/Assets/l18n/de.lproj/Localizable.strings @@ -77,10 +77,13 @@ "AccountsProvider.Error.AddressNotValidFormat" = "Ungültige Adresse: %@"; /* AccountService: Alert title. Changes in version 1.2 */ -"AccountService.update.v12.title" = "Version 1.2 update: Cryptowallets"; +"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" = "Sie müssen sich wieder einloggen, um die Ethereum-Wallet zu initiieren"; + +/* AccountService: User must relogin into app to initiate wallets */ +"AccountService.reloginToInitiateWallets" = "Erneut einloggen, um die Wallet zu initiieren"; /* Login: user typed in invalid passphrase */ "AccountServiceError.InvalidPassphrase" = "Falsche Passphrase"; @@ -118,6 +121,12 @@ /* Account tab: 'Send tokens' button */ "AccountTab.Row.SendTokens" = "Tokens senden"; +/* Account tab: 'Send ADM tokens' button */ +"AccountTab.Row.SendAdm" = "ADM senden"; + +/* Account tab: 'Send ETH tokens' button */ +"AccountTab.Row.SendEth" = "ETH senden"; + /* Account tab: 'Address' row */ "AccountTab.Row.Address" = "Addresse"; @@ -137,7 +146,7 @@ "AccountTab.Section.Application" = "Application"; /* Account tab: Adamant wallet */ -"AccountTab.Wallets.adamant_wallet" = "Adamant Wallet"; +"AccountTab.Wallets.adamant_wallet" = "ADAMANT Wallet"; /* Account tab: Ethereum wallet */ "AccountTab.Wallets.ethereum_wallet" = "Ethereum Wallet"; @@ -175,6 +184,9 @@ /* ChatList: outgoing message preview format, like 'You: %@' */ "ChatListPage.SentMessageFormat" = "Sie: %@"; +/* ChatList: First syncronization is in progress */ +"ChatListPage.SyncingChats" = "Chats werden synchronisiert…"; + /* ChatList: scene title */ "ChatListPage.Title" = "Chats"; @@ -566,16 +578,16 @@ "TransactionListScene.Title" = "Transaktionen"; /* Transaction status: updating in progress */ -"TransactionStatus.Updating" = "Updating..."; +"TransactionStatus.Updating" = "Aktualisieren…"; /* Transaction status: transaction is pending */ -"TransactionStatus.Pending" = "Pending"; +"TransactionStatus.Pending" = "Ausstehend"; /* Transaction status: success */ -"TransactionStatus.Success" = "Success"; +"TransactionStatus.Success" = "Erfolgreich"; /* Transaction status: transaction failed */ -"TransactionStatus.Failed" = "Failed"; +"TransactionStatus.Failed" = "Abgelehnt"; /* Transaction details: scene title */ "TransactionDetailsScene.Title" = "Details"; @@ -587,7 +599,7 @@ "TransactionDetailsScene.Row.Block" = "Block"; /* Transaction details: Transaction delivery status. */ -"TransactionDetailsScene.Row.Status" = "Transaction status"; +"TransactionDetailsScene.Row.Status" = "Transaktionsstatus"; /* Transaction details: confirmations row. */ "TransactionDetailsScene.Row.Confirmations" = "Bestätigungen"; @@ -616,6 +628,12 @@ /* Export transaction: 'Share transaction URL' button */ "TransactionDetailsScene.Share.URL" = "URL"; +/* "Transaction details: 'Comments' section" */ +"TransactionDetailsScene.Section.Comment" = "Kommentare"; + +/* "Transaction details: 'Actions' section" */ +"TransactionDetailsScene.Section.Actions" = "Aktionen"; + /* Transaction details: 'Your address' flag. */ "TransactionDetailsScene.YourAddress" = "Sie"; @@ -646,11 +664,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"; @@ -667,9 +688,18 @@ /* Transfer: 'Your wallet' section */ "TransferScene.Section.YourWallet" = "Ihre Wallet"; +/* Transfer: 'Recipient info' section */ +"TransferScene.Section.Recipient" = "Empfänger"; + /* Transfer: Confirm transfer alert: Send tokens button */ "TransferScene.Send" = "Senden"; +/* Transfer: Confirm transfer alert: 'Can't Undo' message */ +"TransferScene.CantUndo" = "Sie können diese Aktion nicht rückgängig machen"; + +/* Tranfser: Confirm using maximum available for transfer tokens as amount to transfer. */ +"TransferScene.UseMaxToTransfer" = "Alle verfügbaren Tokens versenden?"; + /* 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"; @@ -679,3 +709,14 @@ /* Transfer: Tokens transfered successfully message */ "TransferScene.TransferSuccessMessage" = "Fertig!"; +/* Wallet Services: Shared error, user do not have enought money. */ +"WalletServices.SharedErrors.NotEnoughtMoney" = "Nicht genug Geld"; + +/* Wallet Services: Shared error, user has not yet initiated a specific wallet. */ +"WalletServices.SharedErrors.WalletNotInitiated" = "Die Wallet wurde noch nicht initiiert"; + +/* Wallet Services: Shared error, invalid amount format. %@ for amount */ +"WalletServices.SharedErrors.InvalidAmountFormat" = "Ungültiger Betrag zum Senden: %@"; + +/* Wallet Services: Shared error, transaction not found */ +"WalletServices.SharedErrors.TransactionNotFound" = "Transaktion nicht gefunden"; diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings index e3a15db40..e57fa1f07 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"; @@ -115,6 +118,12 @@ /* Account tab: 'Send tokens' button */ "AccountTab.Row.SendTokens" = "Send Tokens"; +/* Account tab: 'Send ADM tokens' button */ +"AccountTab.Row.SendAdm" = "Send ADM"; + +/* Account tab: 'Send ETH tokens' button */ +"AccountTab.Row.SendEth" = "Send ETH"; + /* Account tab: 'Address' row */ "AccountTab.Row.Address" = "Address"; @@ -134,7 +143,7 @@ "AccountTab.Section.Application" = "Application"; /* Account tab: Adamant wallet */ -"AccountTab.Wallets.adamant_wallet" = "Adamant Wallet"; +"AccountTab.Wallets.adamant_wallet" = "ADAMANT Wallet"; /* Account tab: Ethereum wallet */ "AccountTab.Wallets.ethereum_wallet" = "Ethereum Wallet"; @@ -172,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"; @@ -614,7 +626,7 @@ "No transactions found" = "Transactions not found"; /* Transaction status: updating in progress */ -"TransactionStatus.Updating" = "Updating..."; +"TransactionStatus.Updating" = "Updating…"; /* Transaction status: transaction is pending */ "TransactionStatus.Pending" = "Pending"; @@ -664,6 +676,12 @@ /* Export transaction: 'Share transaction URL' button */ "TransactionDetailsScene.Share.URL" = "URL"; +/* "Transaction details: 'Comments' section" */ +"TransactionDetailsScene.Section.Comment" = "Comments"; + +/* "Transaction details: 'Actions' section" */ +"TransactionDetailsScene.Section.Actions" = "Actions"; + /* Transaction details: 'Requesting Data' progress message. */ "TransactionDetailsScene.RequestingData" = "Requesting Data.."; @@ -697,11 +715,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"; @@ -721,11 +742,17 @@ /* Transfer: 'Your wallet' section */ "TransferScene.Section.YourWallet" = "Your Wallet"; +/* Transfer: 'Recipient info' section */ +"TransferScene.Section.Recipient" = "Recipient"; + /* Transfer: Confirm transfer alert: Send tokens button */ "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$@?"; @@ -735,3 +762,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 94b148aca..783ab8304 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" = "Неправильный пароль"; @@ -115,6 +118,12 @@ /* Account tab: 'Send tokens' button */ "AccountTab.Row.SendTokens" = "Отправить токены"; +/* Account tab: 'Send ADM tokens' button */ +"AccountTab.Row.SendAdm" = "Отправить ADM"; + +/* Account tab: 'Send ETH tokens' button */ +"AccountTab.Row.SendEth" = "Отправить ETH"; + /* Account tab: 'Address' row */ "AccountTab.Row.Address" = "Адрес"; @@ -134,7 +143,7 @@ "AccountTab.Section.Application" = "Приложение"; /* Account tab: Adamant wallet */ -"AccountTab.Wallets.adamant_wallet" = "Adamant кошелёк"; +"AccountTab.Wallets.adamant_wallet" = "ADAMANT кошелёк"; /* Account tab: Ethereum wallet */ "AccountTab.Wallets.ethereum_wallet" = "Ethereum кошелёк"; @@ -172,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" = "Чаты"; @@ -527,7 +539,7 @@ "SecurityPage.Row.Notifications" = "Уведомления"; /* Security: Stay logged row */ -"SecurityPage.Row.StayLoggedIn" = "Оставаться в системе"; +"SecurityPage.Row.StayLoggedIn" = "Оставаться в приложении"; /* Security: Security section */ "SecurityPage.Section.Security" = "Безопасность"; @@ -614,7 +626,7 @@ "No transactions found" = "Транзакции не найдены"; /* Transaction status: updating in progress */ -"TransactionStatus.Updating" = "Обновление..."; +"TransactionStatus.Updating" = "Обновление…"; /* Transaction status: transaction is pending */ "TransactionStatus.Pending" = "Ожидается"; @@ -664,6 +676,12 @@ /* Export transaction: 'Share transaction URL' button */ "TransactionDetailsScene.Share.URL" = "URL"; +/* "Transaction details: 'Comments' section" */ +"TransactionDetailsScene.Section.Comment" = "Комментарии"; + +/* "Transaction details: 'Actions' section" */ +"TransactionDetailsScene.Section.Actions" = "Действия"; + /* Transaction details: 'Requesting Data' progress message. */ "TransactionDetailsScene.RequestingData" = "Запрашиваем данные.."; @@ -697,11 +715,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" = "Отправить"; @@ -721,11 +742,17 @@ /* Transfer: 'Your wallet' section */ "TransferScene.Section.YourWallet" = "Ваш кошелёк"; +/* Transfer: 'Recipient info' section */ +"TransferScene.Section.Recipient" = "Получатель"; + /* Transfer: Confirm transfer alert: Send tokens button */ "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$@?"; @@ -735,3 +762,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/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents b/Adamant/CoreData/Adamant.xcdatamodeld/Adamant.xcdatamodel/contents similarity index 84% rename from Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents rename to Adamant/CoreData/Adamant.xcdatamodeld/Adamant.xcdatamodel/contents index 318bdb5e3..4ed504d71 100644 --- a/Adamant/CoreData/ChatModels.xcdatamodeld/ChatModels.xcdatamodel/contents +++ b/Adamant/CoreData/Adamant.xcdatamodeld/Adamant.xcdatamodel/contents @@ -1,20 +1,22 @@ - + - + + - + + @@ -24,8 +26,10 @@ - + + + @@ -46,19 +50,20 @@ - + + - - - + + + - + \ No newline at end of file diff --git a/Adamant/CoreData/BaseTransaction+CoreDataClass.swift b/Adamant/CoreData/BaseTransaction+CoreDataClass.swift index 0de737909..084cd19fa 100644 --- a/Adamant/CoreData/BaseTransaction+CoreDataClass.swift +++ b/Adamant/CoreData/BaseTransaction+CoreDataClass.swift @@ -2,7 +2,7 @@ // BaseTransaction+CoreDataClass.swift // Adamant // -// Created by Anokhov Pavel on 24.03.2018. +// Created by Anokhov Pavel on 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // diff --git a/Adamant/CoreData/BaseTransaction+CoreDataProperties.swift b/Adamant/CoreData/BaseTransaction+CoreDataProperties.swift index 7f092a6ed..094000d39 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 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -23,6 +23,7 @@ extension BaseTransaction { @NSManaged public var date: NSDate? @NSManaged public var fee: NSDecimalNumber? @NSManaged public var height: Int64 + @NSManaged public var isConfirmed: Bool @NSManaged public var isOutgoing: Bool @NSManaged public var recipientId: String? @NSManaged public var senderId: String? diff --git a/Adamant/CoreData/BaseTransaction+TransactionDetails.swift b/Adamant/CoreData/BaseTransaction+TransactionDetails.swift index 25c01bbf5..a3bf454e2 100644 --- a/Adamant/CoreData/BaseTransaction+TransactionDetails.swift +++ b/Adamant/CoreData/BaseTransaction+TransactionDetails.swift @@ -2,19 +2,21 @@ // BaseTransaction+TransactionDetails.swift // Adamant // -// Created by Anokhov Pavel on 04.10.2018. +// Created by Anokhov Pavel on 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // 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/ChatTransaction+CoreDataClass.swift b/Adamant/CoreData/ChatTransaction+CoreDataClass.swift index 502a3e4da..14ae9054c 100644 --- a/Adamant/CoreData/ChatTransaction+CoreDataClass.swift +++ b/Adamant/CoreData/ChatTransaction+CoreDataClass.swift @@ -2,7 +2,7 @@ // ChatTransaction+CoreDataClass.swift // Adamant // -// Created by Anokhov Pavel on 24.03.2018. +// Created by Anokhov Pavel on 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -12,10 +12,10 @@ import CoreData @objc(ChatTransaction) public class ChatTransaction: BaseTransaction { - var statusEnum: MessageStatus { - get { return MessageStatus(rawValue: self.status) ?? .failed } - set { self.status = newValue.rawValue } - } + var statusEnum: MessageStatus { + get { return MessageStatus(rawValue: self.status) ?? .failed } + set { self.status = newValue.rawValue } + } func serializedMessage() -> String? { fatalError("You must implement serializedMessage in ChatTransaction classes") diff --git a/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift b/Adamant/CoreData/ChatTransaction+CoreDataProperties.swift index 8b7a56808..0ed0cc2c0 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 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -17,10 +17,12 @@ extension ChatTransaction { return NSFetchRequest(entityName: "ChatTransaction") } + @NSManaged public var chatMessageId: String? + @NSManaged public var isHidden: Bool @NSManaged public var isUnread: Bool + @NSManaged public var showsChatroom: Bool @NSManaged public var silentNotification: Bool @NSManaged public var status: Int16 - @NSManaged public var isConfirmed: Bool @NSManaged public var chatroom: Chatroom? @NSManaged public var lastIn: Chatroom? diff --git a/Adamant/CoreData/Chatroom+CoreDataClass.swift b/Adamant/CoreData/Chatroom+CoreDataClass.swift index a4379f1bb..20859c0d3 100644 --- a/Adamant/CoreData/Chatroom+CoreDataClass.swift +++ b/Adamant/CoreData/Chatroom+CoreDataClass.swift @@ -2,7 +2,7 @@ // Chatroom+CoreDataClass.swift // Adamant // -// Created by Anokhov Pavel on 24.03.2018. +// Created by Anokhov Pavel on 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -12,73 +12,73 @@ import CoreData @objc(Chatroom) public class Chatroom: NSManagedObject { - static let entityName = "Chatroom" - - func markAsReaded() { - if hasUnreadMessages { - hasUnreadMessages = false - } - - if let trs = transactions as? Set { - trs.filter { $0.isUnread }.forEach { $0.isUnread = false } - } - } - - private var semaphore: DispatchSemaphore? - - func updateLastTransaction() { - var semaphore = self.semaphore - - if let semaphore = semaphore { - semaphore.wait() - } else { - semaphore = DispatchSemaphore(value: 1) - } - - self.semaphore = semaphore - defer { - self.semaphore = nil - semaphore?.signal() - } - - if let transactions = transactions as? Set { - if let newest = transactions.sorted(by: { (lhs: ChatTransaction, rhs: ChatTransaction) in - guard let l = lhs.date as Date? else { - return true - } - - guard let r = rhs.date as Date? else { - return false - } - - switch l.compare(r) { - case .orderedAscending: - return true - - case .orderedDescending: - return false - - /// Rare case of identical date, compare IDs - case .orderedSame: - guard let lid = lhs.transactionId else { - return true - } - - guard let hid = rhs.transactionId else { - return false - } - - return lid < hid - } - }).last { - if newest != lastTransaction { - lastTransaction = newest - updatedAt = newest.date - } - } else if lastTransaction != nil { - lastTransaction = nil - updatedAt = nil - } - } - } + static let entityName = "Chatroom" + + func markAsReaded() { + if hasUnreadMessages { + hasUnreadMessages = false + } + + if let trs = transactions as? Set { + trs.filter { $0.isUnread }.forEach { $0.isUnread = false } + } + } + + private var semaphore: DispatchSemaphore? + + func updateLastTransaction() { + var semaphore = self.semaphore + + if let semaphore = semaphore { + semaphore.wait() + } else { + semaphore = DispatchSemaphore(value: 1) + } + + self.semaphore = semaphore + defer { + self.semaphore = nil + semaphore?.signal() + } + + if let transactions = transactions as? Set { + if let newest = transactions.sorted(by: { (lhs: ChatTransaction, rhs: ChatTransaction) in + guard let l = lhs.date as Date? else { + return true + } + + guard let r = rhs.date as Date? else { + return false + } + + switch l.compare(r) { + case .orderedAscending: + return true + + case .orderedDescending: + return false + + /// Rare case of identical date, compare IDs + case .orderedSame: + guard let lid = lhs.transactionId else { + return true + } + + guard let hid = rhs.transactionId else { + return false + } + + return lid < hid + } + }).last { + if newest != lastTransaction { + lastTransaction = newest + updatedAt = newest.date + } + } else if lastTransaction != nil { + lastTransaction = nil + updatedAt = nil + } + } + } } diff --git a/Adamant/CoreData/Chatroom+CoreDataProperties.swift b/Adamant/CoreData/Chatroom+CoreDataProperties.swift index f5c7ab8f8..49ab630b4 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 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -18,10 +18,11 @@ extension Chatroom { } @NSManaged public var hasUnreadMessages: Bool + @NSManaged public var isForcedVisible: 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 lastTransaction: ChatTransaction? @NSManaged public var partner: CoreDataAccount? @NSManaged public var transactions: NSSet? diff --git a/Adamant/CoreData/CoreDataAccount+CoreDataClass.swift b/Adamant/CoreData/CoreDataAccount+CoreDataClass.swift index bcf176584..de05bc472 100644 --- a/Adamant/CoreData/CoreDataAccount+CoreDataClass.swift +++ b/Adamant/CoreData/CoreDataAccount+CoreDataClass.swift @@ -2,7 +2,7 @@ // CoreDataAccount+CoreDataClass.swift // Adamant // -// Created by Anokhov Pavel on 24.03.2018. +// Created by Anokhov Pavel on 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -12,5 +12,5 @@ import CoreData @objc(CoreDataAccount) public class CoreDataAccount: NSManagedObject { - static let entityName = "CoreDataAccount" + static let entityName = "CoreDataAccount" } diff --git a/Adamant/CoreData/CoreDataAccount+CoreDataProperties.swift b/Adamant/CoreData/CoreDataAccount+CoreDataProperties.swift index 52afbaed9..53d4a7bfb 100644 --- a/Adamant/CoreData/CoreDataAccount+CoreDataProperties.swift +++ b/Adamant/CoreData/CoreDataAccount+CoreDataProperties.swift @@ -2,7 +2,7 @@ // CoreDataAccount+CoreDataProperties.swift // Adamant // -// Created by Anokhov Pavel on 05.06.2018. +// Created by Anokhov Pavel on 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -19,9 +19,9 @@ extension CoreDataAccount { @NSManaged public var address: String? @NSManaged public var avatar: String? + @NSManaged public var isSystem: Bool @NSManaged public var name: String? @NSManaged public var publicKey: String? - @NSManaged public var isSystem: Bool @NSManaged public var chatroom: Chatroom? @NSManaged public var transfers: NSSet? diff --git a/Adamant/CoreData/MessageTransaction+CoreDataClass.swift b/Adamant/CoreData/MessageTransaction+CoreDataClass.swift index bd6712a26..4d4978621 100644 --- a/Adamant/CoreData/MessageTransaction+CoreDataClass.swift +++ b/Adamant/CoreData/MessageTransaction+CoreDataClass.swift @@ -2,7 +2,7 @@ // MessageTransaction+CoreDataClass.swift // Adamant // -// Created by Anokhov Pavel on 24.03.2018. +// Created by Anokhov Pavel on 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -12,7 +12,7 @@ import CoreData @objc(MessageTransaction) public class MessageTransaction: ChatTransaction { - static let entityName = "MessageTransaction" + static let entityName = "MessageTransaction" override func serializedMessage() -> String? { return message diff --git a/Adamant/CoreData/MessageTransaction+CoreDataProperties.swift b/Adamant/CoreData/MessageTransaction+CoreDataProperties.swift index 31252778a..0db63c902 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 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // diff --git a/Adamant/CoreData/RichMessageTransaction+CoreDataClass.swift b/Adamant/CoreData/RichMessageTransaction+CoreDataClass.swift index ba824d5de..a01f3ef55 100644 --- a/Adamant/CoreData/RichMessageTransaction+CoreDataClass.swift +++ b/Adamant/CoreData/RichMessageTransaction+CoreDataClass.swift @@ -2,7 +2,7 @@ // RichMessageTransaction+CoreDataClass.swift // Adamant // -// Created by Anokhov Pavel on 24.09.2018. +// Created by Anokhov Pavel on 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // diff --git a/Adamant/CoreData/RichMessageTransaction+CoreDataProperties.swift b/Adamant/CoreData/RichMessageTransaction+CoreDataProperties.swift index 42b0c54ea..1ac9073d2 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 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // diff --git a/Adamant/CoreData/TransferTransaction+CoreDataClass.swift b/Adamant/CoreData/TransferTransaction+CoreDataClass.swift index 3d99a784a..cb943c022 100644 --- a/Adamant/CoreData/TransferTransaction+CoreDataClass.swift +++ b/Adamant/CoreData/TransferTransaction+CoreDataClass.swift @@ -2,7 +2,7 @@ // TransferTransaction+CoreDataClass.swift // Adamant // -// Created by Anokhov Pavel on 24.03.2018. +// Created by Anokhov Pavel on 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -12,5 +12,5 @@ import CoreData @objc(TransferTransaction) public class TransferTransaction: ChatTransaction { - static let entityName = "TransferTransaction" + static let entityName = "TransferTransaction" } diff --git a/Adamant/CoreData/TransferTransaction+CoreDataProperties.swift b/Adamant/CoreData/TransferTransaction+CoreDataProperties.swift index f402f64f0..58b76634a 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 10/11/2018. // Copyright © 2018 Adamant. All rights reserved. // // @@ -17,6 +17,7 @@ extension TransferTransaction { return NSFetchRequest(entityName: "TransferTransaction") } + @NSManaged public var comment: String? @NSManaged public var partner: CoreDataAccount? } diff --git a/Adamant/Helpers/AdamantBalanceFormat.swift b/Adamant/Helpers/AdamantBalanceFormat.swift index 67f0161ba..e3330c8a1 100644 --- a/Adamant/Helpers/AdamantBalanceFormat.swift +++ b/Adamant/Helpers/AdamantBalanceFormat.swift @@ -31,6 +31,7 @@ enum AdamantBalanceFormat { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.roundingMode = .floor + formatter.minimumFractionDigits = 0 let positiveFormat: String @@ -87,6 +88,30 @@ enum AdamantBalanceFormat { return defaultFormatter.string(from: NSNumber(floatLiteral: value))! } } + + // MARK: Other formatters + + static var rawNumberDotFormatter: NumberFormatter = { + let f = NumberFormatter() + f.numberStyle = .decimal + f.roundingMode = .floor + f.decimalSeparator = "." + f.usesGroupingSeparator = false + f.minimumFractionDigits = 0 + f.maximumFractionDigits = 12 // 18 is too low, 0.007 for example will serialize as 0.007000000000000001 + return f + }() + + static var rawNumberCommaFormatter: NumberFormatter = { + let f = NumberFormatter() + f.numberStyle = .decimal + f.roundingMode = .floor + f.decimalSeparator = "," + f.usesGroupingSeparator = false + f.minimumFractionDigits = 0 + f.maximumFractionDigits = 12 // 18 is too low, 0.007 for example will serialize as 0.007000000000000001 + return f + }() } diff --git a/Adamant/Helpers/String+localized.swift b/Adamant/Helpers/String+localized.swift index fc3f88d6f..8207c5cff 100644 --- a/Adamant/Helpers/String+localized.swift +++ b/Adamant/Helpers/String+localized.swift @@ -49,6 +49,8 @@ extension String { static let unknownError = NSLocalizedString("Error.UnknownError", comment: "Shared unknown error") + static let notEnoughtMoney = NSLocalizedString("WalletServices.SharedErrors.NotEnoughtMoney", comment: "Wallet Services: Shared error, user do not have enought money.") + static func internalError(message: String) -> String { return String.localizedStringWithFormat(NSLocalizedString("Error.InternalErrorFormat", comment: "Shared error: Internal error format, %@ for message"), message) } diff --git a/Adamant/Info.plist b/Adamant/Info.plist index 592072a21..34a81d9dc 100644 --- a/Adamant/Info.plist +++ b/Adamant/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.1 + 1.2 CFBundleVersion - 47 + 58 LSRequiresIPhoneOS NSAppTransportSecurity diff --git a/Adamant/Models/EthTransaction.swift b/Adamant/Models/EthTransaction.swift index 4f73d99e2..e0f3c23cc 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 @@ -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,11 @@ extension EthTransaction: TransactionDetails { var confirmationsValue: String? { return confirmations } var blockValue: String? { return blockNumber} - var feeValue: Decimal { + 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, @@ -194,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/Models/RichMessage.swift b/Adamant/Models/RichMessage.swift index 2eb53ff0e..d05105b9b 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: RichMessageTransfer.serialize(balance: 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,16 @@ struct RichMessageTransfer: RichMessage { self.type = type self.hash = hash - if let amount = content[CodingKeys.amount.stringValue] { - self.amount = amount + if let raw = content[CodingKeys.amount.stringValue] { + if let number = AdamantBalanceFormat.rawNumberDotFormatter.number(from: raw) { + self.amount = number.decimalValue + } else if let number = AdamantBalanceFormat.rawNumberCommaFormatter.number(from: raw) { + self.amount = number.decimalValue + } else { + self.amount = 0 + } } else { - self.amount = "0" + self.amount = 0 } if let comments = content[CodingKeys.comments.stringValue] { @@ -119,22 +118,25 @@ 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) { + if let number = AdamantBalanceFormat.rawNumberDotFormatter.number(from: raw) { + self.amount = number.decimalValue + } else if let number = AdamantBalanceFormat.rawNumberCommaFormatter.number(from: raw) { + self.amount = number.decimalValue + } else { + self.amount = 0 + } + } else if let amount = try? container.decode(Decimal.self, forKey: .amount) { + self.amount = amount + } else { + self.amount = 0 + } } } extension RichMessageTransfer { - static var formatter: NumberFormatter = { - let f = NumberFormatter() - f.numberStyle = .decimal - f.roundingMode = .floor - f.decimalSeparator = "." - f.minimumFractionDigits = 0 - f.maximumFractionDigits = 18 - return f - }() - static func serialize(balance: Decimal) -> String { - return formatter.string(fromDecimal: balance) ?? "0" + return AdamantBalanceFormat.rawNumberDotFormatter.string(fromDecimal: balance) ?? "0" } } 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/ServiceProtocols/AccountService.swift b/Adamant/ServiceProtocols/AccountService.swift index 9cc434827..f3141639b 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() {} @@ -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/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/ServiceProtocols/DataProviders/ChatsProvider.swift b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift index 9737079ee..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() {} } } @@ -181,7 +183,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/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/ServiceProtocols/DialogService.swift b/Adamant/ServiceProtocols/DialogService.swift index c760212c1..d9a9727d2 100644 --- a/Adamant/ServiceProtocols/DialogService.swift +++ b/Adamant/ServiceProtocols/DialogService.swift @@ -20,7 +20,7 @@ extension String.adamantLocalized.alert { enum ShareType { case copyToPasteboard case share - case generateQr(sharingTip: String?) + case generateQr(encodedContent: String?, sharingTip: String?) case saveToPhotolibrary(image: UIImage) var localized: String { @@ -44,20 +44,6 @@ enum ShareContentType { case passphrase case address - func shareTypes(sharingTip: String?) -> [ShareType] { - switch self { - case .address: - return [.copyToPasteboard, - .share, - .generateQr(sharingTip: sharingTip)] - - case .passphrase: - return [.copyToPasteboard, - .share, - .generateQr(sharingTip: sharingTip)] - } - } - var excludedActivityTypes: [UIActivity.ActivityType]? { switch self { case .passphrase: diff --git a/Adamant/ServiceProtocols/NotificationsService.swift b/Adamant/ServiceProtocols/NotificationsService.swift index 113a3801f..0201ffcdc 100644 --- a/Adamant/ServiceProtocols/NotificationsService.swift +++ b/Adamant/ServiceProtocols/NotificationsService.swift @@ -113,7 +113,35 @@ extension AdamantUserInfoKey { // MARK: - Protocol enum NotificationsServiceResult { case success - case denied(error: Error?) + case failure(error: NotificationsServiceError) +} + +enum NotificationsServiceError: Error { + case notEnoughtMoney + case denied(error: Error?) +} + +extension NotificationsServiceError: RichError { + var message: String { + switch self { + case .notEnoughtMoney: return String.adamantLocalized.sharedErrors.notEnoughtMoney + case .denied: return String.adamantLocalized.notifications.notificationsDisabled + } + } + + var internalError: Error? { + switch self { + case .notEnoughtMoney: return nil + case .denied(let error): return error + } + } + + var level: ErrorLevel { + switch self { + case .notEnoughtMoney: return .warning + case .denied: return .error + } + } } protocol NotificationsService: class { diff --git a/Adamant/ServiceProtocols/RichMessageProvider.swift b/Adamant/ServiceProtocols/RichMessageProvider.swift index 4c381abcf..fef451b21 100644 --- a/Adamant/ServiceProtocols/RichMessageProvider.swift +++ b/Adamant/ServiceProtocols/RichMessageProvider.swift @@ -14,7 +14,7 @@ enum CellSource { case nib(nib: UINib) } -protocol RichMessageProvider { +protocol RichMessageProvider: class { static var richMessageType: String { get } var cellIdentifierSent: String { get } @@ -34,4 +34,12 @@ protocol RichMessageProvider { protocol RichMessageProviderWithStatusCheck: RichMessageProvider { func statusForTransactionBy(hash: String, completion: @escaping (WalletServiceResult) -> Void) + + var delayBetweenChecks: TimeInterval { get } +} + +extension RichMessageProviderWithStatusCheck { + var delayBetweenChecks: TimeInterval { + return 10.0 + } } diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift index d964de469..ca8b1581e 100644 --- a/Adamant/Services/AdamantAccountService.swift +++ b/Adamant/Services/AdamantAccountService.swift @@ -14,7 +14,8 @@ class AdamantAccountService: AccountService { var apiService: ApiService! var adamantCore: AdamantCore! - var notificationsService: NotificationsService! + weak var notificationsService: NotificationsService! + var dialogService: DialogService! var securedStore: SecuredStore! { didSet { securedStoreSemaphore.wait() @@ -86,9 +87,55 @@ class AdamantAccountService: AccountService { // MARK: Wallets var wallets: [WalletService] = [ AdmWalletService(), - try! EthWalletService(apiUrl: AdamantResources.ethServers.first!), // TODO: Move to background thread + EthWalletService(), // TODO: Move to background thread // LskWalletService() ] + + init() { + guard let ethWallet = wallets[1] as? EthWalletService else { + fatalError("Failed to get EthWalletService") + } + + ethWallet.initiateNetwork(apiUrl: AdamantResources.ethServers.first!) { result in + switch result { + case .success: + break + + case .failure(let error): + switch error { + case .networkError: + NotificationCenter.default.addObserver(forName: Notification.Name.AdamantReachabilityMonitor.reachabilityChanged, object: nil, queue: nil) { notification in + guard let connection = notification.userInfo?[AdamantUserInfoKey.ReachabilityMonitor.connection] as? AdamantConnection else { + return + } + + switch connection { + case .none: + break + + case .wifi, .cellular: + ethWallet.initiateNetwork(apiUrl: AdamantResources.ethServers.first!) { result in + switch result { + case .success: + NotificationCenter.default.removeObserver(self, name: Notification.Name.AdamantReachabilityMonitor.reachabilityChanged, object: nil) + + case .failure(let error): + self.dialogService.showRichError(error: error) + } + } + } + } + + case .notLogged, .transactionNotFound, .notEnoughtMoney, .accountNotFound, .walletNotInitiated, .invalidAmount: + break + + case .remoteServiceError, .apiError, .internalError: + self.dialogService.showRichError(error: error) + self.wallets.remove(at: 1) + } + } + } + } } // MARK: - Saved data @@ -155,6 +202,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 +374,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 +496,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 +508,7 @@ fileprivate enum Key { case pin case useBiometry case passphrase + case showedV12 var stringValue: String { switch self { @@ -453,6 +517,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/Services/AdamantAddressBookService.swift b/Adamant/Services/AdamantAddressBookService.swift index 74c7d7a71..5b29adad8 100644 --- a/Adamant/Services/AdamantAddressBookService.swift +++ b/Adamant/Services/AdamantAddressBookService.swift @@ -11,7 +11,7 @@ import libsodium class AdamantAddressBookService: AddressBookService { let addressBookKey = "contact_list" - let waitTime: TimeInterval = 60.0 // in sec + let waitTime: TimeInterval = 20.0 // in sec // MARK: - Dependencies @@ -148,6 +148,14 @@ class AdamantAddressBookService: AddressBookService { } func update(_ completion: ((AddressBookServiceResult) -> Void)?) { + // Check if book has changes. Skip update until changes is saved + isChangingSemaphore.wait() + guard !hasChanges else { + isChangingSemaphore.signal() + return + } + isChangingSemaphore.signal() + isSavingSemaphore.wait() getAddressBook { result in @@ -211,6 +219,16 @@ class AdamantAddressBookService: AddressBookService { isSavingSemaphore.wait() + // Check again + isChangingSemaphore.wait() + guard hasChanges else { + isSavingSemaphore.signal() + isChangingSemaphore.signal() + return + } + isChangingSemaphore.signal() + + // Background task savingBookTaskId = UIApplication.shared.beginBackgroundTask { UIApplication.shared.endBackgroundTask(self.savingBookTaskId) @@ -226,12 +244,35 @@ class AdamantAddressBookService: AddressBookService { } switch result { - case .success: - self.isChangingSemaphore.wait() - self.hasChanges = false - self.isChangingSemaphore.signal() - - self.removedNames.removeAll() + case .success(let id): + var done: Bool = false + let group = DispatchGroup() + + // Hold updates until transaction passed on backend + while !done { + Thread.sleep(forTimeInterval: 3.0) + + group.enter() + + self.apiService.getTransaction(id: id) { result in + defer { group.leave() } + + switch result { + case .success: done = true + default: break + } + } + + group.wait() + } + + if done { + self.isChangingSemaphore.wait() + self.hasChanges = false + self.isChangingSemaphore.signal() + + self.removedNames.removeAll() + } case .failure(let error): self.dialogService.showRichError(error: error) @@ -239,7 +280,7 @@ class AdamantAddressBookService: AddressBookService { } } - private func saveAddressBook(_ book: [String: String], completion: @escaping (AddressBookServiceResult) -> Void) { + private func saveAddressBook(_ book: [String: String], completion: @escaping (AddressBookServiceResultId) -> Void) { guard let loggedAccount = accountService.account, let keypair = accountService.keypair else { completion(.failure(.notLogged)) return @@ -261,8 +302,8 @@ class AdamantAddressBookService: AddressBookService { // MARK: 2. Submit to KVS apiService.store(key: addressBookKey, value: value, type: .keyValue, sender: address, keypair: keypair) { (result) in switch result { - case .success: - completion(.success) + case .success(let id): + completion(.success(id: id)) case .failure(let error): completion(.failure(.apiServiceError(error: error))) @@ -341,3 +382,8 @@ extension AdamantAddressBookService { return processedBook } } + +private enum AddressBookServiceResultId { + case success(id: UInt64) + case failure(AddressBookServiceError) +} diff --git a/Adamant/Services/AdamantDialogService.swift b/Adamant/Services/AdamantDialogService.swift index 6c88215ab..36c040267 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,7 +182,7 @@ extension AdamantDialogService { alertVC.alertActionStackView.spacing = 0 alertVC.alertActionStackViewHeightConstraint.constant = 100 - present(alertVC, animated: true, completion: nil) + present(alertVC, animated: true, completion: nil) } func showRichError(error: RichError) { @@ -237,9 +241,9 @@ extension AdamantDialogService { self?.present(vc, animated: true, completion: completion) }) - case .generateQr(let sharingTip): + case .generateQr(let encodedContent, let sharingTip): alert.addAction(UIAlertAction(title: type.localized, style: .default) { [weak self] _ in - switch AdamantQRTools.generateQrFrom(string: string) { + switch AdamantQRTools.generateQrFrom(string: encodedContent ?? string) { case .success(let qr): guard let vc = self?.router.get(scene: AdamantScene.Shared.shareQr) as? ShareQrViewController else { fatalError("Can't find ShareQrViewController") @@ -412,7 +416,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/AdamantNotificationService.swift b/Adamant/Services/AdamantNotificationService.swift index 45e97652d..e16e54a1f 100644 --- a/Adamant/Services/AdamantNotificationService.swift +++ b/Adamant/Services/AdamantNotificationService.swift @@ -93,10 +93,18 @@ extension AdamantNotificationsService { completion?(.success) return - case .backgroundFetch, .push: + case .push: + guard let account = accountService?.account, account.balance > AdamantApiService.KvsFee else { + completion?(.failure(error: .notEnoughtMoney)) + return + } + + fallthrough + + case .backgroundFetch: authorizeNotifications { [weak self] (success, error) in guard success else { - completion?(.denied(error: error)) + completion?(.failure(error: .denied(error: error))) return } diff --git a/Adamant/Services/ApiService/AdamantApi+Transfers.swift b/Adamant/Services/ApiService/AdamantApi+Transfers.swift index e71552d74..2fdb0c58c 100644 --- a/Adamant/Services/ApiService/AdamantApi+Transfers.swift +++ b/Adamant/Services/ApiService/AdamantApi+Transfers.swift @@ -14,7 +14,7 @@ extension AdamantApiService { // MARK: 1. Prepare params let params: [String : Any] = [ "type": TransactionType.send.rawValue, - "amount": amount.shiftedToAdamant(), + "amount": (amount.shiftedToAdamant() as NSDecimalNumber).uint64Value, "recipientId": recipient, "senderId": sender, "publicKey": keypair.publicKey @@ -55,7 +55,7 @@ extension AdamantApiService { // MARK: 4.2. Create transaction let transaction: [String: Any] = [ "type": normalizedTransaction.type.rawValue, - "amount": normalizedTransaction.amount.shiftedToAdamant(), + "amount": (normalizedTransaction.amount.shiftedToAdamant() as NSDecimalNumber).uint64Value, "senderPublicKey": normalizedTransaction.senderPublicKey, "requesterPublicKey": normalizedTransaction.requesterPublicKey ?? NSNull(), "timestamp": normalizedTransaction.timestamp, 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 { diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 66fc390d9..c7ec7aeca 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) @@ -302,6 +306,7 @@ extension AdamantChatsProvider { transaction.senderId = senderId transaction.type = Int16(type.rawValue) transaction.isOutgoing = true + transaction.chatMessageId = UUID().uuidString transaction.message = text @@ -320,6 +325,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 +380,6 @@ extension AdamantChatsProvider { } // MARK: 3. Prepare transaction - transaction.transactionId = UUID().uuidString - transaction.blockId = UUID().uuidString transaction.statusEnum = MessageStatus.pending chatroom.addToTransactions(transaction) @@ -586,7 +590,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 @@ -598,7 +605,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) @@ -609,8 +618,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)] @@ -945,31 +955,94 @@ 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 transaction = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) - transaction.message = decodedMessage - messageTransaction = transaction - - 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 + // MARK: Decode message, message must contain data + if let decodedMessage = adamantCore.decodeMessage(rawMessage: chat.message, rawNonce: chat.ownMessage, senderPublicKey: publicKey, privateKey: privateKey)?.trimmingCharacters(in: .whitespacesAndNewlines), !decodedMessage.isEmpty { + switch chat.type { + // MARK: Text message + case .message, .messageOld, .signal, .unknown: + if transaction.amount > 0 { + let trs = TransferTransaction(entity: TransferTransaction.entity(), insertInto: context) + trs.comment = decodedMessage + messageTransaction = trs + } else { + let trs = MessageTransaction(entity: MessageTransaction.entity(), insertInto: context) + trs.message = decodedMessage + messageTransaction = trs + } - transaction.transactionStatus = richProviders[type] != nil ? .notInitiated : nil + // 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 + } + + // 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 + } + // 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.amount = transaction.amount as NSDecimalNumber messageTransaction.date = transaction.date as NSDate messageTransaction.recipientId = transaction.recipientId messageTransaction.senderId = transaction.senderId @@ -980,7 +1053,8 @@ extension AdamantChatsProvider { messageTransaction.isOutgoing = isOutgoing messageTransaction.blockId = transaction.blockId messageTransaction.confirmations = transaction.confirmations - + messageTransaction.chatMessageId = UUID().uuidString + messageTransaction.fee = transaction.fee as NSDecimalNumber messageTransaction.statusEnum = MessageStatus.delivered return messageTransaction diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index 84806c037..c8c054256 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 @@ -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)) @@ -340,9 +340,12 @@ extension AdamantTransfersProvider { transaction.senderId = senderId 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 @@ -382,8 +385,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))) @@ -610,6 +622,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) @@ -636,6 +649,9 @@ extension AdamantTransfersProvider { transfer.blockId = t.blockId 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 @@ -674,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}) diff --git a/Adamant/SharedViews/TransferCollectionViewCell.swift b/Adamant/SharedViews/TransferCollectionViewCell.swift index 89cdaf4fa..94eaee7f9 100644 --- a/Adamant/SharedViews/TransferCollectionViewCell.swift +++ b/Adamant/SharedViews/TransferCollectionViewCell.swift @@ -9,15 +9,35 @@ import UIKit //import MessageKit -class TransferCollectionViewCell: UICollectionViewCell, ChatCell, TapRecognizerCustomCell { +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? @@ -28,7 +48,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 +56,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/SharedViews/TransferCollectionViewCell.xib b/Adamant/SharedViews/TransferCollectionViewCell.xib index c5b859836..6b8571c0a 100644 --- a/Adamant/SharedViews/TransferCollectionViewCell.xib +++ b/Adamant/SharedViews/TransferCollectionViewCell.xib @@ -1,11 +1,11 @@ - + - + @@ -55,14 +55,14 @@ -