diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index 65d927ded..589dd859c 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -29,13 +29,8 @@ E91947B22000246A001362F8 /* AdamantError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91947B12000246A001362F8 /* AdamantError.swift */; }; E91947B420002809001362F8 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = E91947B320002809001362F8 /* Account.swift */; }; E9256F5F2034C21100DE86E9 /* String+localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9256F5E2034C21100DE86E9 /* String+localized.swift */; }; - E9256F602034E0E100DE86E9 /* Chats.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E9256F622034E0E100DE86E9 /* Chats.storyboard */; }; - E9256F632034E7DE00DE86E9 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E9256F652034E7DE00DE86E9 /* Settings.storyboard */; }; - E9256F692034E98700DE86E9 /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E9256F6B2034E98700DE86E9 /* Login.storyboard */; }; E9256F6D20357B1700DE86E9 /* LoginHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = E9256F6C20357B1700DE86E9 /* LoginHeader.xib */; }; E9256F762039A9A200DE86E9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E9256F752039A9A200DE86E9 /* LaunchScreen.storyboard */; }; - E9256F792039B29A00DE86E9 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E9256F7B2039B29A00DE86E9 /* Localizable.strings */; }; - E9256F7E2039B29D00DE86E9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E9256F802039B29D00DE86E9 /* InfoPlist.strings */; }; E9393FA82055C92700EE6F30 /* Decimal+adamant.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9393FA72055C92700EE6F30 /* Decimal+adamant.swift */; }; E9393FAA2055D03300EE6F30 /* AdamantMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9393FA92055D03300EE6F30 /* AdamantMessage.swift */; }; E93B0D742028B21400126346 /* ChatsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93B0D732028B21400126346 /* ChatsProvider.swift */; }; @@ -43,18 +38,26 @@ E93D7ABE2052CEE1005D19DC /* NotificationsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93D7ABD2052CEE1005D19DC /* NotificationsService.swift */; }; E93D7AC02052CF63005D19DC /* AdamantNotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93D7ABF2052CF63005D19DC /* AdamantNotificationService.swift */; }; E93D7AC22052EE21005D19DC /* SettingsViewController+StayIn.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93D7AC12052EE21005D19DC /* SettingsViewController+StayIn.swift */; }; - E93D7AC520530F76005D19DC /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = E93D7AC720530F76005D19DC /* Localizable.stringsdict */; }; E93EFE13200D1156000BB482 /* ChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93EFE12200D1156000BB482 /* ChatViewController.swift */; }; - E9452A4C20344834005BA34C /* Account.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E9452A4E20344834005BA34C /* Account.storyboard */; }; E94883E7203F07CD00F6E1B0 /* PassphraseValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E94883E6203F07CD00F6E1B0 /* PassphraseValidation.swift */; }; E948E03B20235E2300975D6B /* SettingsRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E948E03A20235E2300975D6B /* SettingsRoutes.swift */; }; E948E0462024DAE800975D6B /* AdamantUserInfoKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = E948E0452024DAE800975D6B /* AdamantUserInfoKeys.swift */; }; E948E0482024F02700975D6B /* LoginFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = E948E0472024F02700975D6B /* LoginFooter.xib */; }; E948E04C2027679300975D6B /* AdamantExportTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = E948E04B2027679300975D6B /* AdamantExportTools.swift */; }; - E948E04E20278D5600975D6B /* SettingsDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = E948E04D20278D5600975D6B /* SettingsDependencies.swift */; }; + E94E7B01205D3F090042B639 /* ChatListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E94E7B00205D3F090042B639 /* ChatListViewController.xib */; }; + E94E7B06205D48B20042B639 /* TransactionsRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E94E7B05205D48B20042B639 /* TransactionsRoutes.swift */; }; + E94E7B08205D4CB80042B639 /* SharedRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E94E7B07205D4CB80042B639 /* SharedRoutes.swift */; }; + E94E7B0A205D59F50042B639 /* AccountViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E94E7B09205D59F50042B639 /* AccountViewController.xib */; }; + E94E7B0C205D5E4A0042B639 /* TransactionsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E94E7B0B205D5E4A0042B639 /* TransactionsViewController.xib */; }; + E94E7B0E205D5EA80042B639 /* TransactionDetailsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E94E7B0D205D5EA80042B639 /* TransactionDetailsViewController.xib */; }; E9502740202E257E002C1098 /* RepeaterService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E950273F202E257E002C1098 /* RepeaterService.swift */; }; E950652120404BF0008352E5 /* AdamantUriBuilding.swift in Sources */ = {isa = PBXBuildFile; fileRef = E950652020404BF0008352E5 /* AdamantUriBuilding.swift */; }; E950652320404C84008352E5 /* AdamantUriTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = E950652220404C84008352E5 /* AdamantUriTools.swift */; }; + E95CB451205D77B200A7218E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E95CB453205D77B200A7218E /* InfoPlist.strings */; }; + E95CB456205D77B500A7218E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E95CB458205D77B500A7218E /* Localizable.strings */; }; + E95CB45E205D7F9600A7218E /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = E95CB460205D7F9600A7218E /* Localizable.stringsdict */; }; + E95CB464205D8BBA00A7218E /* EurekaAccountRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95CB462205D8BBA00A7218E /* EurekaAccountRow.swift */; }; + E95CB465205D8BBA00A7218E /* AccountCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E95CB463205D8BBA00A7218E /* AccountCell.xib */; }; E95F856520067A030070534A /* GlobalConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F856420067A030070534A /* GlobalConstants.swift */; }; E95F85692006AB9D0070534A /* NormalizedTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85682006AB9D0070534A /* NormalizedTransaction.swift */; }; E95F856B200789450070534A /* JSModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F856A200789450070534A /* JSModels.swift */; }; @@ -64,8 +67,7 @@ E95F85772007E8EC0070534A /* JSAdamantCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85762007E8EC0070534A /* JSAdamantCoreTests.swift */; }; E95F857A2007F0260070534A /* ServerResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85792007F0260070534A /* ServerResponse.swift */; }; E95F85802008C8D70070534A /* ChatsRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F857E2008C8D60070534A /* ChatsRoutes.swift */; }; - E95F85812008C8D70070534A /* ChatsDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F857F2008C8D70070534A /* ChatsDependencies.swift */; }; - E95F85852008CB3A0070534A /* ChatsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85842008CB3A0070534A /* ChatsListViewController.swift */; }; + E95F85852008CB3A0070534A /* ChatListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85842008CB3A0070534A /* ChatListViewController.swift */; }; E95F85872008FDBF0070534A /* Chat.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85862008FDBF0070534A /* Chat.swift */; }; E95F8589200900B10070534A /* ChatType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F8588200900B10070534A /* ChatType.swift */; }; E95F858B200931410070534A /* TransactionAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F858A200931410070534A /* TransactionAsset.swift */; }; @@ -119,16 +121,12 @@ E9E7CD8F20026CD300DFC4DB /* AdamantDialogService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CD8E20026CD300DFC4DB /* AdamantDialogService.swift */; }; E9E7CD9120026FA100DFC4DB /* SwinjectDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CD9020026FA100DFC4DB /* SwinjectDependencies.swift */; }; E9E7CD932002740500DFC4DB /* AdamantAccountService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CD922002740500DFC4DB /* AdamantAccountService.swift */; }; - E9E7CD952002812400DFC4DB /* LoginDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CD942002812400DFC4DB /* LoginDependencies.swift */; }; - E9E7CDA92002AF1E00DFC4DB /* AccountDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDA82002AF1E00DFC4DB /* AccountDependencies.swift */; }; E9E7CDAC2002AFA500DFC4DB /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDAA2002AFA500DFC4DB /* AccountViewController.swift */; }; E9E7CDAF2002B8A100DFC4DB /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDAE2002B8A100DFC4DB /* Router.swift */; }; E9E7CDB12002B97B00DFC4DB /* AccountRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDB02002B97B00DFC4DB /* AccountRoutes.swift */; }; E9E7CDB32002B9FB00DFC4DB /* LoginRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDB22002B9FB00DFC4DB /* LoginRoutes.swift */; }; E9E7CDB52002BA6900DFC4DB /* SwinjectedRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDB42002BA6900DFC4DB /* SwinjectedRouter.swift */; }; E9E7CDB72003994E00DFC4DB /* AdamantUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDB62003994E00DFC4DB /* AdamantUtilities.swift */; }; - E9E7CDBB2003AAA700DFC4DB /* RoundAvatarTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDB92003AAA700DFC4DB /* RoundAvatarTableViewCell.swift */; }; - E9E7CDBC2003AAA700DFC4DB /* RoundAvatarTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E9E7CDBA2003AAA700DFC4DB /* RoundAvatarTableViewCell.xib */; }; E9E7CDBE2003AEFB00DFC4DB /* CellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDBD2003AEFB00DFC4DB /* CellFactory.swift */; }; E9E7CDC02003AF6D00DFC4DB /* AdamantCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDBF2003AF6D00DFC4DB /* AdamantCellFactory.swift */; }; E9E7CDC22003F5A400DFC4DB /* TransactionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDC12003F5A400DFC4DB /* TransactionsViewController.swift */; }; @@ -205,17 +203,8 @@ E91947B12000246A001362F8 /* AdamantError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantError.swift; sourceTree = ""; }; E91947B320002809001362F8 /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; E9256F5E2034C21100DE86E9 /* String+localized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+localized.swift"; sourceTree = ""; }; - E9256F612034E0E100DE86E9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Chats.storyboard; sourceTree = ""; }; - E9256F642034E7DE00DE86E9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Settings.storyboard; sourceTree = ""; }; - E9256F6A2034E98700DE86E9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Login.storyboard; sourceTree = ""; }; E9256F6C20357B1700DE86E9 /* LoginHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoginHeader.xib; sourceTree = ""; }; - E9256F6E2039A7E800DE86E9 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Account.strings; sourceTree = ""; }; - E9256F6F2039A7E900DE86E9 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Chats.strings; sourceTree = ""; }; - E9256F702039A7E900DE86E9 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Login.strings; sourceTree = ""; }; - E9256F712039A7E900DE86E9 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Settings.strings; sourceTree = ""; }; E9256F752039A9A200DE86E9 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; - E9256F7A2039B29A00DE86E9 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; - E9256F7F2039B29D00DE86E9 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; E9393FA72055C92700EE6F30 /* Decimal+adamant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Decimal+adamant.swift"; sourceTree = ""; }; E9393FA92055D03300EE6F30 /* AdamantMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantMessage.swift; sourceTree = ""; }; E93B0D732028B21400126346 /* ChatsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsProvider.swift; sourceTree = ""; }; @@ -223,19 +212,29 @@ E93D7ABD2052CEE1005D19DC /* NotificationsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsService.swift; sourceTree = ""; }; E93D7ABF2052CF63005D19DC /* AdamantNotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantNotificationService.swift; sourceTree = ""; }; E93D7AC12052EE21005D19DC /* SettingsViewController+StayIn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsViewController+StayIn.swift"; sourceTree = ""; }; - E93D7AC620530F76005D19DC /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = ""; }; - E93D7AC920530F8E005D19DC /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = Base; path = Base.lproj/Localizable.stringsdict; sourceTree = ""; }; E93EFE12200D1156000BB482 /* ChatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatViewController.swift; sourceTree = ""; }; - E9452A4D20344834005BA34C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Account.storyboard; sourceTree = ""; }; E94883E6203F07CD00F6E1B0 /* PassphraseValidation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassphraseValidation.swift; sourceTree = ""; }; E948E03A20235E2300975D6B /* SettingsRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRoutes.swift; sourceTree = ""; }; E948E0452024DAE800975D6B /* AdamantUserInfoKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantUserInfoKeys.swift; sourceTree = ""; }; E948E0472024F02700975D6B /* LoginFooter.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LoginFooter.xib; sourceTree = ""; }; E948E04B2027679300975D6B /* AdamantExportTools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantExportTools.swift; sourceTree = ""; }; - E948E04D20278D5600975D6B /* SettingsDependencies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDependencies.swift; sourceTree = ""; }; + E94E7B00205D3F090042B639 /* ChatListViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ChatListViewController.xib; sourceTree = ""; }; + E94E7B05205D48B20042B639 /* TransactionsRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionsRoutes.swift; sourceTree = ""; }; + E94E7B07205D4CB80042B639 /* SharedRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedRoutes.swift; sourceTree = ""; }; + E94E7B09205D59F50042B639 /* AccountViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountViewController.xib; sourceTree = ""; }; + E94E7B0B205D5E4A0042B639 /* TransactionsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TransactionsViewController.xib; sourceTree = ""; }; + E94E7B0D205D5EA80042B639 /* TransactionDetailsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TransactionDetailsViewController.xib; sourceTree = ""; }; E950273F202E257E002C1098 /* RepeaterService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeaterService.swift; sourceTree = ""; }; E950652020404BF0008352E5 /* AdamantUriBuilding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantUriBuilding.swift; sourceTree = ""; }; E950652220404C84008352E5 /* AdamantUriTools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantUriTools.swift; sourceTree = ""; }; + E95CB452205D77B200A7218E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + E95CB457205D77B500A7218E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + E95CB459205D77DC00A7218E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + E95CB45A205D77DD00A7218E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; + E95CB45F205D7F9600A7218E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; + E95CB461205D7FD000A7218E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = ""; }; + E95CB462205D8BBA00A7218E /* EurekaAccountRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EurekaAccountRow.swift; sourceTree = ""; }; + E95CB463205D8BBA00A7218E /* AccountCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountCell.xib; sourceTree = ""; }; E95F856420067A030070534A /* GlobalConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalConstants.swift; sourceTree = ""; }; E95F85682006AB9D0070534A /* NormalizedTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalizedTransaction.swift; sourceTree = ""; }; E95F856A200789450070534A /* JSModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSModels.swift; sourceTree = ""; }; @@ -245,8 +244,7 @@ E95F85762007E8EC0070534A /* JSAdamantCoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSAdamantCoreTests.swift; sourceTree = ""; }; E95F85792007F0260070534A /* ServerResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerResponse.swift; sourceTree = ""; }; E95F857E2008C8D60070534A /* ChatsRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsRoutes.swift; sourceTree = ""; }; - E95F857F2008C8D70070534A /* ChatsDependencies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsDependencies.swift; sourceTree = ""; }; - E95F85842008CB3A0070534A /* ChatsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsListViewController.swift; sourceTree = ""; }; + E95F85842008CB3A0070534A /* ChatListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewController.swift; sourceTree = ""; }; E95F85862008FDBF0070534A /* Chat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Chat.swift; sourceTree = ""; }; E95F8588200900B10070534A /* ChatType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatType.swift; sourceTree = ""; }; E95F858A200931410070534A /* TransactionAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionAsset.swift; sourceTree = ""; }; @@ -300,16 +298,12 @@ E9E7CD8E20026CD300DFC4DB /* AdamantDialogService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdamantDialogService.swift; sourceTree = ""; }; E9E7CD9020026FA100DFC4DB /* SwinjectDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwinjectDependencies.swift; sourceTree = ""; }; E9E7CD922002740500DFC4DB /* AdamantAccountService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantAccountService.swift; sourceTree = ""; }; - E9E7CD942002812400DFC4DB /* LoginDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginDependencies.swift; sourceTree = ""; }; - E9E7CDA82002AF1E00DFC4DB /* AccountDependencies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDependencies.swift; sourceTree = ""; }; E9E7CDAA2002AFA500DFC4DB /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = ""; }; E9E7CDAE2002B8A100DFC4DB /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; E9E7CDB02002B97B00DFC4DB /* AccountRoutes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountRoutes.swift; sourceTree = ""; }; E9E7CDB22002B9FB00DFC4DB /* LoginRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginRoutes.swift; sourceTree = ""; }; E9E7CDB42002BA6900DFC4DB /* SwinjectedRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwinjectedRouter.swift; sourceTree = ""; }; E9E7CDB62003994E00DFC4DB /* AdamantUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdamantUtilities.swift; sourceTree = ""; }; - E9E7CDB92003AAA700DFC4DB /* RoundAvatarTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundAvatarTableViewCell.swift; sourceTree = ""; }; - E9E7CDBA2003AAA700DFC4DB /* RoundAvatarTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RoundAvatarTableViewCell.xib; sourceTree = ""; }; E9E7CDBD2003AEFB00DFC4DB /* CellFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellFactory.swift; sourceTree = ""; }; E9E7CDBF2003AF6D00DFC4DB /* AdamantCellFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdamantCellFactory.swift; sourceTree = ""; }; E9E7CDC12003F5A400DFC4DB /* TransactionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionsViewController.swift; sourceTree = ""; }; @@ -476,12 +470,10 @@ E913C9111FFFAB05001A83F7 /* Assets */ = { isa = PBXGroup; children = ( + E95CB45B205D7DFD00A7218E /* l18n */, E9E7CDA42002A6EB00DFC4DB /* Fonts */, E913C8F81FFFA51D001A83F7 /* Assets.xcassets */, E9A174B820587B83003667CD /* notification.mp3 */, - E9256F802039B29D00DE86E9 /* InfoPlist.strings */, - E9256F7B2039B29A00DE86E9 /* Localizable.strings */, - E93D7AC720530F76005D19DC /* Localizable.stringsdict */, E9256F752039A9A200DE86E9 /* LaunchScreen.storyboard */, E9C51EEC2011416E00385EB7 /* adamant-core.js */, ); @@ -493,6 +485,7 @@ children = ( E9FAE5DB203ECD41008D3A6B /* Shared */, E9E7CDA52002AE1C00DFC4DB /* Account */, + E94E7B04205D48950042B639 /* Transactions */, E95F857B2008C8B20070534A /* Chats */, E919479A20001007001362F8 /* Login */, E982F69820235AF000566AC7 /* Settings */, @@ -503,8 +496,6 @@ E919479A20001007001362F8 /* Login */ = { isa = PBXGroup; children = ( - E9256F6B2034E98700DE86E9 /* Login.storyboard */, - E9E7CD942002812400DFC4DB /* LoginDependencies.swift */, E9E7CDB22002B9FB00DFC4DB /* LoginRoutes.swift */, E905D39E204C281400DDB504 /* LoginViewController.swift */, E9147B602050599000145913 /* LoginViewController+QR.swift */, @@ -527,6 +518,20 @@ path = ServerResponses; sourceTree = ""; }; + E94E7B04205D48950042B639 /* Transactions */ = { + isa = PBXGroup; + children = ( + E94E7B05205D48B20042B639 /* TransactionsRoutes.swift */, + E9E7CDC12003F5A400DFC4DB /* TransactionsViewController.swift */, + E94E7B0B205D5E4A0042B639 /* TransactionsViewController.xib */, + E9EC34132005178500C0E546 /* TransactionDetailsViewController.swift */, + E94E7B0D205D5EA80042B639 /* TransactionDetailsViewController.xib */, + E9E7CDC52003F6D200DFC4DB /* TransactionTableViewCell.swift */, + E9E7CDC62003F6D200DFC4DB /* TransactionTableViewCell.xib */, + ); + path = Transactions; + sourceTree = ""; + }; E950651F20404997008352E5 /* Utilities */ = { isa = PBXGroup; children = ( @@ -538,15 +543,26 @@ path = Utilities; sourceTree = ""; }; + E95CB45B205D7DFD00A7218E /* l18n */ = { + isa = PBXGroup; + children = ( + E95CB460205D7F9600A7218E /* Localizable.stringsdict */, + E95CB458205D77B500A7218E /* Localizable.strings */, + E95CB453205D77B200A7218E /* InfoPlist.strings */, + ); + path = l18n; + sourceTree = ""; + }; E95F857B2008C8B20070534A /* Chats */ = { isa = PBXGroup; children = ( - E9256F622034E0E100DE86E9 /* Chats.storyboard */, - E95F857F2008C8D70070534A /* ChatsDependencies.swift */, E95F857E2008C8D60070534A /* ChatsRoutes.swift */, - E95F85842008CB3A0070534A /* ChatsListViewController.swift */, + E95F85842008CB3A0070534A /* ChatListViewController.swift */, + E94E7B00205D3F090042B639 /* ChatListViewController.xib */, E93EFE12200D1156000BB482 /* ChatViewController.swift */, E9C51EF02013F18000385EB7 /* NewChatViewController.swift */, + E95F85C5200A9B070070534A /* ChatTableViewCell.swift */, + E95F85C6200A9B070070534A /* ChatTableViewCell.xib */, ); path = Chats; sourceTree = ""; @@ -585,8 +601,6 @@ E982F69820235AF000566AC7 /* Settings */ = { isa = PBXGroup; children = ( - E9256F652034E7DE00DE86E9 /* Settings.storyboard */, - E948E04D20278D5600975D6B /* SettingsDependencies.swift */, E948E03A20235E2300975D6B /* SettingsRoutes.swift */, E982F69B20235B4D00566AC7 /* SettingsViewController.swift */, E93D7AC12052EE21005D19DC /* SettingsViewController+StayIn.swift */, @@ -654,12 +668,9 @@ E9E7CDA52002AE1C00DFC4DB /* Account */ = { isa = PBXGroup; children = ( - E9452A4E20344834005BA34C /* Account.storyboard */, - E9E7CDA82002AF1E00DFC4DB /* AccountDependencies.swift */, E9E7CDB02002B97B00DFC4DB /* AccountRoutes.swift */, E9E7CDAA2002AFA500DFC4DB /* AccountViewController.swift */, - E9E7CDC12003F5A400DFC4DB /* TransactionsViewController.swift */, - E9EC34132005178500C0E546 /* TransactionDetailsViewController.swift */, + E94E7B09205D59F50042B639 /* AccountViewController.xib */, E9EC342020052ABB00C0E546 /* TransferViewController.swift */, ); path = Account; @@ -668,14 +679,10 @@ E9E7CDB82003AA8E00DFC4DB /* SharedCells */ = { isa = PBXGroup; children = ( - E95F85C5200A9B070070534A /* ChatTableViewCell.swift */, - E95F85C6200A9B070070534A /* ChatTableViewCell.xib */, - E9E7CDB92003AAA700DFC4DB /* RoundAvatarTableViewCell.swift */, - E9E7CDBA2003AAA700DFC4DB /* RoundAvatarTableViewCell.xib */, - E9E7CDC52003F6D200DFC4DB /* TransactionTableViewCell.swift */, - E9E7CDC62003F6D200DFC4DB /* TransactionTableViewCell.xib */, E9942B86203D9E5100C163AF /* EurekaQRRow.swift */, E9942B88203D9ECA00C163AF /* QrCell.xib */, + E95CB462205D8BBA00A7218E /* EurekaAccountRow.swift */, + E95CB463205D8BBA00A7218E /* AccountCell.xib */, ); path = SharedCells; sourceTree = ""; @@ -700,8 +707,9 @@ E9FAE5DB203ECD41008D3A6B /* Shared */ = { isa = PBXGroup; children = ( - E9FAE5E1203ED1AE008D3A6B /* ShareQrViewController.xib */, E9FAE5E0203ED1AE008D3A6B /* ShareQrViewController.swift */, + E9FAE5E1203ED1AE008D3A6B /* ShareQrViewController.xib */, + E94E7B07205D4CB80042B639 /* SharedRoutes.swift */, ); path = Shared; sourceTree = ""; @@ -779,7 +787,7 @@ hasScannedForEncodings = 0; knownRegions = ( en, - Base, + ru, ); mainGroup = E913C8E51FFFA51D001A83F7; productRefGroup = E913C8EF1FFFA51D001A83F7 /* Products */; @@ -798,34 +806,34 @@ buildActionMask = 2147483647; files = ( E9256F762039A9A200DE86E9 /* LaunchScreen.storyboard in Resources */, + E95CB451205D77B200A7218E /* InfoPlist.strings in Resources */, E9EC341B200524CA00C0E546 /* Roboto_300_normal.ttf in Resources */, - E9E7CDBC2003AAA700DFC4DB /* RoundAvatarTableViewCell.xib in Resources */, E95F85C8200A9B070070534A /* ChatTableViewCell.xib in Resources */, E913C8F91FFFA51D001A83F7 /* Assets.xcassets in Resources */, E9EC341F200524CA00C0E546 /* Roboto_700_normal.ttf in Resources */, - E9256F692034E98700DE86E9 /* Login.storyboard in Resources */, E9942B89203D9ECA00C163AF /* QrCell.xib in Resources */, + E95CB465205D8BBA00A7218E /* AccountCell.xib in Resources */, E9C51EED2011416E00385EB7 /* adamant-core.js in Resources */, E90A4945204C6204009F6A65 /* PassphraseCell.xib in Resources */, E9E7CDC82003F6D200DFC4DB /* TransactionTableViewCell.xib in Resources */, E9EC3417200524CA00C0E546 /* Exo+2_400_italic.ttf in Resources */, + E94E7B01205D3F090042B639 /* ChatListViewController.xib in Resources */, E9EC341E200524CA00C0E546 /* Roboto_500_normal.ttf in Resources */, E9EC3419200524CA00C0E546 /* Exo+2_500_normal.ttf in Resources */, - E9256F7E2039B29D00DE86E9 /* InfoPlist.strings in Resources */, E9256F6D20357B1700DE86E9 /* LoginHeader.xib in Resources */, E9EC3416200524CA00C0E546 /* Exo+2_300_normal.ttf in Resources */, - E9256F632034E7DE00DE86E9 /* Settings.storyboard in Resources */, E9EC341C200524CA00C0E546 /* Roboto_400_italic.ttf in Resources */, - E9256F792039B29A00DE86E9 /* Localizable.strings in Resources */, - E93D7AC520530F76005D19DC /* Localizable.stringsdict in Resources */, + E94E7B0C205D5E4A0042B639 /* TransactionsViewController.xib in Resources */, E9EC3415200524CA00C0E546 /* Exo+2_100_normal.ttf in Resources */, E9EC341A200524CA00C0E546 /* Exo+2_700_normal.ttf in Resources */, + E94E7B0A205D59F50042B639 /* AccountViewController.xib in Resources */, + E95CB456205D77B500A7218E /* Localizable.strings in Resources */, + E95CB45E205D7F9600A7218E /* Localizable.stringsdict in Resources */, + E94E7B0E205D5EA80042B639 /* TransactionDetailsViewController.xib in Resources */, E9EC341D200524CA00C0E546 /* Roboto_400_normal.ttf in Resources */, E9A174B920587B84003667CD /* notification.mp3 in Resources */, - E9256F602034E0E100DE86E9 /* Chats.storyboard in Resources */, E9EC3418200524CA00C0E546 /* Exo+2_400_normal.ttf in Resources */, E948E0482024F02700975D6B /* LoginFooter.xib in Resources */, - E9452A4C20344834005BA34C /* Account.storyboard in Resources */, E9FAE5E3203ED1AE008D3A6B /* ShareQrViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -863,7 +871,6 @@ "${BUILT_PRODUCTS_DIR}/QRCodeReader.swift/QRCodeReader.framework", "${BUILT_PRODUCTS_DIR}/RNCryptor/RNCryptor.framework", "${BUILT_PRODUCTS_DIR}/Swinject/Swinject.framework", - "${BUILT_PRODUCTS_DIR}/SwinjectStoryboard/SwinjectStoryboard.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -878,7 +885,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/QRCodeReader.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNCryptor.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Swinject.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwinjectStoryboard.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -925,6 +931,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E94E7B08205D4CB80042B639 /* SharedRoutes.swift in Sources */, E9256F5F2034C21100DE86E9 /* String+localized.swift in Sources */, E9CAE8D22018AA7700345E76 /* AdamantApi+Accounts.swift in Sources */, E9942B84203CBFCE00C163AF /* AdamantQRTools.swift in Sources */, @@ -948,15 +955,14 @@ E982F69C20235B4D00566AC7 /* SettingsViewController.swift in Sources */, E9147B6F205088DE00145913 /* LoginViewController+Pinpad.swift in Sources */, E9FAE5E2203ED1AE008D3A6B /* ShareQrViewController.swift in Sources */, + E94E7B06205D48B20042B639 /* TransactionsRoutes.swift in Sources */, E9CE778E202B9DC2009CF70D /* TransferTransaction+CoreDataProperties.swift in Sources */, E95F85B3200954D00070534A /* ChatModels.xcdatamodeld in Sources */, E95F858B200931410070534A /* TransactionAsset.swift in Sources */, E90A494B204D9EB8009F6A65 /* AdamantAuthentication.swift in Sources */, E9E7CDAC2002AFA500DFC4DB /* AccountViewController.swift in Sources */, - E95F85812008C8D70070534A /* ChatsDependencies.swift in Sources */, E91947B420002809001362F8 /* Account.swift in Sources */, E9E7CDC22003F5A400DFC4DB /* TransactionsViewController.swift in Sources */, - E9E7CDA92002AF1E00DFC4DB /* AccountDependencies.swift in Sources */, E905D39F204C281400DDB504 /* LoginViewController.swift in Sources */, E9CE7785202B9DC2009CF70D /* Chatroom+CoreDataClass.swift in Sources */, E9CE7786202B9DC2009CF70D /* Chatroom+CoreDataProperties.swift in Sources */, @@ -966,12 +972,10 @@ E9393FAA2055D03300EE6F30 /* AdamantMessage.swift in Sources */, E90A494D204DA932009F6A65 /* LocalAuthentication.swift in Sources */, E95F856F2007B61D0070534A /* GetPublicKeyResponse.swift in Sources */, - E9E7CDBB2003AAA700DFC4DB /* RoundAvatarTableViewCell.swift in Sources */, E9E7CDAF2002B8A100DFC4DB /* Router.swift in Sources */, E9E7CDC72003F6D200DFC4DB /* TransactionTableViewCell.swift in Sources */, E9B3D3A9202082450019EB36 /* AdamantTransfersProvider.swift in Sources */, E9A174B72057F1B3003667CD /* AdamantChatsProvider+backgroundFetch.swift in Sources */, - E948E04E20278D5600975D6B /* SettingsDependencies.swift in Sources */, E9E7CDB32002B9FB00DFC4DB /* LoginRoutes.swift in Sources */, E91947B22000246A001362F8 /* AdamantError.swift in Sources */, E95F85802008C8D70070534A /* ChatsRoutes.swift in Sources */, @@ -989,11 +993,11 @@ E9E7CDCA20040CC200DFC4DB /* Transaction.swift in Sources */, E95F85692006AB9D0070534A /* NormalizedTransaction.swift in Sources */, E913C90D1FFFA99B001A83F7 /* Keypair.swift in Sources */, + E95CB464205D8BBA00A7218E /* EurekaAccountRow.swift in Sources */, E948E03B20235E2300975D6B /* SettingsRoutes.swift in Sources */, E9CAE8D82018ACA700345E76 /* AdamantApi+Transfers.swift in Sources */, - E9E7CD952002812400DFC4DB /* LoginDependencies.swift in Sources */, E9E7CDCC20040FDC00DFC4DB /* TransactionType.swift in Sources */, - E95F85852008CB3A0070534A /* ChatsListViewController.swift in Sources */, + E95F85852008CB3A0070534A /* ChatListViewController.swift in Sources */, E91947AC20001A9A001362F8 /* ApiService.swift in Sources */, E9B3D3A1201FA26B0019EB36 /* AdamantAccountsProvider.swift in Sources */, E9FAE5DA203DBFEF008D3A6B /* Comparable+Clamped.swift in Sources */, @@ -1056,67 +1060,33 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - E9256F622034E0E100DE86E9 /* Chats.storyboard */ = { - isa = PBXVariantGroup; - children = ( - E9256F612034E0E100DE86E9 /* Base */, - E9256F6F2039A7E900DE86E9 /* ru */, - ); - name = Chats.storyboard; - sourceTree = ""; - }; - E9256F652034E7DE00DE86E9 /* Settings.storyboard */ = { + E95CB453205D77B200A7218E /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( - E9256F642034E7DE00DE86E9 /* Base */, - E9256F712039A7E900DE86E9 /* ru */, + E95CB452205D77B200A7218E /* en */, + E95CB45A205D77DD00A7218E /* ru */, ); - name = Settings.storyboard; - sourceTree = ""; - }; - E9256F6B2034E98700DE86E9 /* Login.storyboard */ = { - isa = PBXVariantGroup; - children = ( - E9256F6A2034E98700DE86E9 /* Base */, - E9256F702039A7E900DE86E9 /* ru */, - ); - name = Login.storyboard; + name = InfoPlist.strings; sourceTree = ""; }; - E9256F7B2039B29A00DE86E9 /* Localizable.strings */ = { + E95CB458205D77B500A7218E /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( - E9256F7A2039B29A00DE86E9 /* ru */, + E95CB457205D77B500A7218E /* en */, + E95CB459205D77DC00A7218E /* ru */, ); name = Localizable.strings; sourceTree = ""; }; - E9256F802039B29D00DE86E9 /* InfoPlist.strings */ = { + E95CB460205D7F9600A7218E /* Localizable.stringsdict */ = { isa = PBXVariantGroup; children = ( - E9256F7F2039B29D00DE86E9 /* ru */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - E93D7AC720530F76005D19DC /* Localizable.stringsdict */ = { - isa = PBXVariantGroup; - children = ( - E93D7AC620530F76005D19DC /* ru */, - E93D7AC920530F8E005D19DC /* Base */, + E95CB45F205D7F9600A7218E /* en */, + E95CB461205D7FD000A7218E /* ru */, ); name = Localizable.stringsdict; sourceTree = ""; }; - E9452A4E20344834005BA34C /* Account.storyboard */ = { - isa = PBXVariantGroup; - children = ( - E9452A4D20344834005BA34C /* Base */, - E9256F6E2039A7E800DE86E9 /* ru */, - ); - name = Account.storyboard; - sourceTree = ""; - }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/Adamant/AppDelegate.swift b/Adamant/AppDelegate.swift index eee8b92f8..581fca8e3 100644 --- a/Adamant/AppDelegate.swift +++ b/Adamant/AppDelegate.swift @@ -8,13 +8,20 @@ import UIKit import Swinject -import SwinjectStoryboard + +extension String.adamantLocalized { + struct tabItems { + static let account = NSLocalizedString("Tabs.Account", comment: "Main tab bar: Account page") + static let chats = NSLocalizedString("Tabs.Chats", comment: "Main tab bar: Chats page") + static let settings = NSLocalizedString("Tabs.Settings", comment: "Main tab bar: Settings page") + } +} @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? var repeater: RepeaterService! + var container: Container! weak var accountService: AccountService? weak var notificationService: NotificationsService? @@ -23,14 +30,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // MARK: 1. Initiating Swinject - let container = SwinjectStoryboard.defaultContainer - Container.loggingFunction = nil // Logging currently not supported with SwinjectStoryboards. + container = Container() container.registerAdamantServices() - container.registerAdamantAccountStory() - container.registerAdamantLoginStory() - container.registerAdamantChatsStory() - container.registerAdamantSettingsStory() - accountService = container.resolve(AccountService.self) notificationService = container.resolve(NotificationsService.self) @@ -48,20 +49,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } if let tabbar = self.window!.rootViewController as? UITabBarController { - let account = router.get(story: .Account).instantiateInitialViewController()! - let chats = router.get(story: .Chats).instantiateInitialViewController()! - let settings = router.get(story: .Settings).instantiateInitialViewController()! - + let accountRoot = router.get(scene: AdamantScene.Account.account) + let account = UINavigationController(rootViewController: accountRoot) + account.tabBarItem.title = String.adamantLocalized.tabItems.account + account.tabBarItem.image = #imageLiteral(resourceName: "wallet_tab") + + let chatListRoot = router.get(scene: AdamantScene.Chats.chatList) + let chatList = UINavigationController(rootViewController: chatListRoot) + chatList.tabBarItem.title = String.adamantLocalized.tabItems.chats + chatList.tabBarItem.image = #imageLiteral(resourceName: "chats_tab") + + let settingsRoot = router.get(scene: AdamantScene.Settings.settings) + let settings = UINavigationController(rootViewController: settingsRoot) + settings.tabBarItem.title = String.adamantLocalized.tabItems.settings + settings.tabBarItem.image = #imageLiteral(resourceName: "settings_tab") + + account.tabBarItem.badgeColor = UIColor.adamantPrimary - chats.tabBarItem.badgeColor = UIColor.adamantPrimary + chatList.tabBarItem.badgeColor = UIColor.adamantPrimary settings.tabBarItem.badgeColor = UIColor.adamantPrimary - tabbar.setViewControllers([account, chats, settings], animated: false) + tabbar.setViewControllers([account, chatList, settings], animated: false) } // MARK: 3. Initiate login - self.window!.rootViewController?.present(router.get(scene: .Login), animated: false, completion: nil) + let login = router.get(scene: AdamantScene.Login.login) + self.window!.rootViewController?.present(login, animated: false, completion: nil) // MARK: 4 Autoupdate @@ -180,4 +194,6 @@ extension AppDelegate { return } } + + completionHandler(.noData) }} diff --git a/Adamant/Assets/l18n/en.lproj/InfoPlist.strings b/Adamant/Assets/l18n/en.lproj/InfoPlist.strings new file mode 100644 index 000000000..3e17b555a --- /dev/null +++ b/Adamant/Assets/l18n/en.lproj/InfoPlist.strings @@ -0,0 +1,15 @@ +/* The user-visible name of the bundle; used by Siri and visible on the Home screen in iOS. */ +"CFBundleDisplayName" = "Adamant"; + +/* The short name of the bundle, which may be displayed to users in situations such as the absence of a value for CFBundleDisplayName. This name should be less than 16 characters long. */ +"CFBundleName" = "Adamant"; + +/* Camera authorization reason */ +"NSCameraUsageDescription" = "The camera needed to scan QR codes for addresses and passphrases"; + +/* Readonly access to photolibrary reason */ +"NSPhotoLibraryAddUsageDescription" = "Saving generated QR codes with passphrases and addresses"; + +/* ReadWrite access to photolibrary reason */ +"NSPhotoLibraryUsageDescription" = "Reading QR codes with passphrases and addresses"; + diff --git a/Adamant/Assets/l18n/en.lproj/Localizable.strings b/Adamant/Assets/l18n/en.lproj/Localizable.strings new file mode 100644 index 000000000..38bfb3f46 --- /dev/null +++ b/Adamant/Assets/l18n/en.lproj/Localizable.strings @@ -0,0 +1,377 @@ +/* Product name */ +"ADAMANT" = "ADAMANT"; + +/* ApiService: Bad internal application error, report a bug. Using %@ as error description */ +"AccountServiceError.Internal errorFormat" = "Internal error: %@"; + +/* Login: user typed in invalid passphrase */ +"AccountServiceError.InvalidPassphrase" = "Wrong passphrase"; + +/* Login: user not logged error */ +"AccountServiceError.UserNotLogged" = "User not logged"; + +/* Login: user typed in wrong passphrase */ +"AccountServiceError.WrongPassphrase" = "Wrong passphrase: %@"; + +/* Account tab: Confirm logout alert: Logout (Ok) button */ +"AccountTab.ConfirmLogout.Logout" = "Logout"; + +/* Account tab: Confirm logout alert */ +"AccountTab.ConfirmLogout.MessageFormat" = "Logout from %@?"; + +/* Account tab: Inform user that sending tokens not allowed by Apple until the end of ICO */ +"AccountTab.TransferBlocked.Message" = "Due to Apple restrictions, sending tokens is not allowed until the end of the ICO. For now, you can send tokens using WebApp at msg.adamant.im"; + +/* Account tab: Balance row title */ +"AccountTab.Row.Balance" = "Balance"; + +/* Account tab: 'Invest in ICO' button */ +"AccountTab.Row.JoinIco" = "Join the ICO"; + +/* Account tab: 'Logout' button */ +"AccountTab.Row.Logout" = "Logout"; + +/* Account tab: 'Send tokens' button */ +"AccountTab.Row.SendTokens" = "Send Tokens"; + +/* Account tab: Account section title. */ +"AccountTab.Section.Account" = "Account"; + +/* Account tab: Actions section title */ +"AccountTab.Section.Actions" = "Actions"; + +/* Account tab: Wallet section title */ +"AccountTab.Section.Wallet" = "Wallet"; + +/* Account tab: 'Transfer not allowed' alert 'go to WebApp button' */ +"AccountTab.TransferBlocked.GoToPWA" = "msg.adamant.im"; + +/* Account tab: 'Transfer not allowed' alert title */ +"AccountTab.TransferBlocked.Title" = "Sorry!"; + +/* Account page: scene title */ +"AccountTab.Title" = "Account"; + +/* ApiService: Account not found error. Using %@ for address. */ +"ApiService.Error.AccountNotFoundFormat" = "Account not found: %@"; + +/* ApiService: Bad internal application error, report a bug. Using %@ for error description */ +"ApiService.Error.InternalErrorFormat" = "Internal error: %@"; + +/* ApiService: No connection message. Generally bad network. */ +"ApiService.Error.NoConnection" = "No connection to the Internet"; + +/* ApiService: Remote server returned an error. Using %@ for error description */ +"ApiService.Error.RemoteServerErrorFormat" = "Remote server error: %@"; + +/* ApiService: User not logged error */ +"ApiService.Error.UserNotLogged" = "User not logged"; + +/* Serious internal error: Failed to build endpoint url */ +"ApiService.InternalError.EndpointBuildFailed" = "Endpoint build failed. Report a bug"; + +/* Serious internal error: Failed to sign transaction */ +"ApiService.InternalError.FailedTransactionSigning" = "Transaction failed"; + +/* Serious internal error: Error parsing response */ +"ApiService.InternalError.ParsingFailed" = "Parsing failed. Report a bug"; + +/* Unknown internal error */ +"ApiService.InternalError.UnknownError" = "Unknown error. Report a bug"; + +/* ChatList: scene title */ +"ChatListPage.Title" = "Chats"; + +/* Chat: Notify user about bad internal error. Usually this should be reported as a bug. Using %@ for error description */ +"ChatScene.Error.InternalErrorFormat" = "Internal error: %@. Report a bug"; + +/* Chat: Notify user that message cannot be empty */ +"ChatScene.Error.MessageIsEmpty" = "Message is empty!"; + +/* Chat: Message is too long */ +"ChatScene.Error.MessageTooLong" = "Message is too long"; + +/* Chat: Notify user that he doesn't have money to pay a message fee */ +"ChatScene.Error.NotEnoughMoney" = "You don't have enought money to send a message"; + +/* Chat: Notify user about server error. Using %@ for error description */ +"ChatScene.Error.RemoteServerErrorFormat" = "Remote error: %@. Report a bug"; + +/* Chat: message input placeholder */ +"ChatScene.NewMessage.Placeholder" = "New message"; + +/* Chat: Send message button */ +"ChatScene.Send" = "Send"; + +/* Known contacts: Adamant ICO message */ +"Chats.IcoMessage" = "ou have a possibility to invest in ICO of ADAMANT, the most secure and anonymous messenger. Earlier you participate, better offer you will get. Learn more on Adamant.im website or in the Whitepaper. To invest, go to Wallet→Invest in the ICO, or follow a website page Adamant.im/ico/. If you still have any questions, you can ask them by replying to this message. We are eager to answer quickly, but sometimes delays for a couple of hours are possible.\\nAfter you invest and receive ADM tokens, we recommend to keep them as long as possible. All of unsold tokens during ICO will be distributed among users wallets, adding 5% monthly. Additional info is on Adamant.im website and in the Whitepaper."; + +/* Known contacts: Adamant pre ICO message */ +"Chats.PreIcoMessage" = "You have a possibility to invest in ADAMANT, the most secure and anonymous messenger. Now is a Pre-ICO stage — the most profitable for investors. Learn more on Adamant.im website or in the Whitepaper. To participate just reply to this message and we will assist. We are eager to answer quickly, but sometimes delays for a couple of hours are possible.\\nAfter you invest and receive ADM tokens, we recommend to keep them as long as possible. All of unsold tokens during ICO will be distributed among users wallets, adding 5% monthly. Additional info is on Adamant.im website and in the Whitepaper."; + +/* Known contacts: Adamant welcome message */ +"Chats.WelcomeMessage" = "Welcome to ADAMANT, the most secure and anonymous messenger. You are credited with bounty tokens, which you can use to get acquainted with the messenger.\\nRemember, your security and anonymity is up to you also. Do not follow links you receive, otherwise your IP can be compromised. Do not trust browser extensions. Better to share your ADM address personally, but not using other messengers. Keep your secret passphrase secure. Set a password on your device or logout before leaving.\\nLearn more about security and anonymity at https://adamant.im/staysecured/.\\n\\nDo not reply to this message, it is a system account."; + +/* Login: Notify user, that he disabled camera in settings, and need to authorize application. */ +"LoginScene.Error.AuthorizeCamera" = "You need to authorize Adamant to use device's Camera"; + +/* Login: No network error. */ +"LoginScene.Error.NoInternet" = "No connection to the Internet"; + +/* Login: notify user that he is trying to login without a passphrase */ +"LoginScene.Error.NoPassphrase" = "Enter a passphrase"; + +/* Login: Notify user that device not supported by QR reader */ +"LoginScene.Error.QrNotSupported" = "QR code reading not supported by the device"; + +/* Login: Notify user that scanned QR doesn't contains a passphrase. */ +"LoginScene.Error.WrongQr" = "QR code does not contains a valid passphrase"; + +/* Login: notify user that we are trying to log in */ +"LoginScene.LoggingInProgress" = "Logging in..."; + +/* Login: Login into previous account with biometry or pincode */ +"LoginScene.LoginIntoAdamant" = "Login into Adamant"; + +/* Login: generate new passphrase button */ +"LoginScene.Row.Generate" = "Generate new passphrase"; + +/* Login: Login button */ +"LoginScene.Row.Login" = "Login"; + +/* Login: Passphrase placeholder */ +"LoginScene.Row.Passphrase.Placeholder" = "Passphrase"; + +/* Login: Login with pincode button */ +"LoginScene.Row.Pincode" = "Login with PIN-code"; + +/* Login: Login with QR button. */ +"LoginScene.Row.Qr" = "Login with QR-code"; + +/* Login: security alert, notify user that he must save his new passphrase */ +"LoginScene.Row.SavePassphraseAlert" = "Save the passphrase for new Wallet and Messenger account. There is no login to enter Wallet, only the passphrase needed. If lost, no way to recover it."; + +/* Login: a small hint for a user, that he can tap on passphrase to save it */ +"LoginScene.Row.TapToSave" = "Tap to save"; + +/* Login: login with existing passphrase section */ +"LoginScene.Section.Login" = "Login"; + +/* Login: Create new account section */ +"LoginScene.Section.NewAccount" = "New account"; + +/* New chat: Recipient address placeholder. Note that address text field always shows U letter, so you can left this line blank. */ +"NewChatScene.Address.Placeholder" = ""; + +/* New chat: Notify user that specified address (%@) not found. Using %@ for address */ +"NewChatScene.Error.AddressNotFoundFormat" = "Address not found: %@"; + +/* New chat: Notify user that he did enter invalid address */ +"NewChatScene.Error.InvalidAddress" = "Enter a valid address"; + +/* New chat: Notify user that he can't start chat with himself */ +"NewChatScene.Error.OwnAddress" = "You don't need an encrypted anonymous chat to talk to yourself"; + +/* New chat: Remote server returned an error. Using %@ for error description */ +"NewChatScene.Error.RemoteServerFormat" = "Remote server error: %@. Report a bug"; + +/* New Chat: Notify user that scanned QR doesn't contains an address */ +"NewChatScene.Error.WrongQr" = "QR code does not contains a valid address"; + +/* New chat: Scan QR with address button */ +"NewChatScene.ScanQr" = "Scan QR-code"; + +/* New chat: scene title */ +"NewChatScene.Title" = "New Chat"; + +/* Notifications: New message notification title */ +"NotificationsService.NewMessage.Title" = "New Message"; + +/* Notifications: New transfer transaction title */ +"NotificationsService.NewTransfer.Title" = "New Transfer"; + +/* Notifications: User has disabled notifications. Head him into settings */ +"NotificationsService.NotificationsDisabled" = "Notifications disabled. You can enable notifications in Settings"; + +/* Pinpad: Ask user to create new pin */ +"Pinpad.EnterNewPin" = "Enter new pin-code"; + +/* Pinpad: Ask user to repeat new pin */ +"Pinpad.ReenterPin" = "Re-enter your pin-code"; + +/* QRGenerator: user typed in wrong invalid */ +"QrGeneratorScene.Error.InvalidPassphrase" = "Enter a valid passphrase"; + +/* QRGenerator: Bad Internal generator error message format. Using %@ for error description */ +"QrGeneratorScene.Error.InternalErrorFormat" = "Internal error: %@. Report a bug"; + +/* QRGenerator: Passphrase textview placeholder */ +"QrGeneratorScene.Passphrase.Placeholder" = "Passphrase"; + +/* QRGenerator: small 'Tap to save' tooltip under generated QR */ +"QrGeneratorScene.TapToSave" = "Tap to save"; + +/* QRGenerator: scene title */ +"QrGeneratorScene.Title" = "QR Generator"; + +/* Config: turn off 'Stay Logged In' confirmation */ +"SettingsPage.DoNotStayLoggedIn" = "Do not stay logged in"; + +/* Config: Authorization reason for turning biometry off */ +"SettingsPage.DoNotUseBiometry" = "Do not use biometry to log in"; + +/* Config: Generate QR with passphrase row */ +"SettingsPage.Row.GenerateQr" = "Generate QR with passphrase"; + +/* Config: Show notifications */ +"SettingsPage.Row.Notifications" = "Notifications"; + +/* Config: Stay logged option */ +"SettingsPage.Row.StayLoggedIn" = "Stay logged in"; + +/* Config: Version row */ +"SettingsPage.Row.Version" = "Version"; + +/* Config: Application Info section */ +"SettingsPage.Section.ApplicationInfo" = "Application"; + +/* Config: Settings section */ +"SettingsPage.Section.Settings" = "Settings"; + +/* Config: Utilities section */ +"SettingsPage.Section.Utilities" = "Utilities"; + +/* Config: scene title */ +"SettingsPage.Title" = "Settings"; + +/* Config: Authorization reason for turning biometry on */ +"SettingsPage.UseBiometry" = "Use biometry"; + +/* Shared alert 'Cancel' button. Used anywhere */ +"Shared.Cancel" = "Cancel"; + +/* Shared alert notification: message about item copied to pasteboard. */ +"Shared.CopiedToPasteboard" = "Copied to Pasteboard!"; + +/* Shared alert 'Copy' button. Used anywhere. Used for copy-paste info. */ +"Shared.CopyToPasteboard" = "Copy to Pasteboard"; + +/* Shared alert Done message. Used anywhere */ +"Shared.Done" = "Done"; + +/* Shared alert 'Error' title. Used anywhere */ +"Shared.Error" = "Error"; + +/* Shared alert 'Generate QR' button. Used to generate QR codes with addresses and passphrases. Used with sharing and saving, anywhere. */ +"Shared.GenerateQRCode" = "Generate QR code"; + +/* Shared alert 'Ok' button. Used anywhere */ +"Shared.Ok" = "Ok"; + +/* Shared alert 'Save' button. Used anywhere */ +"Shared.Save" = "Save"; + +/* Shared alert 'Settings' button. Used to go to system Settings app, on application settings page. Should be same as Settings application title. */ +"Shared.Settings" = "Settings"; + +/* Shared alert 'Share' button. Used anywhere for presenting standart iOS 'Share' menu. */ +"Shared.Share" = "Share"; + +/* Main tab bar: Account page */ +"Tabs.Account" = "Account"; + +/* Main tab bar: Chats page */ +"Tabs.Chats" = "Chats"; + +/* Main tab bar: Settings page */ +"Tabs.Settings" = "Settings"; + +/* Transaction details: amount row. */ +"TransactionDetailsScene.Row.Amount" = "Amount"; + +/* Transaction details: Block id row. */ +"TransactionDetailsScene.Row.Block" = "Block"; + +/* Transaction details: confirmations row. */ +"TransactionDetailsScene.Row.Confirmations" = "Confirmations"; + +/* Transaction details: date row. */ +"TransactionDetailsScene.Row.Date" = "Date"; + +/* Transaction details: 'Open transaction in explorer' row. */ +"TransactionDetailsScene.Row.Explorer" = "Open in Explorer"; + +/* Transaction details: fee row. */ +"TransactionDetailsScene.Row.Fee" = "Fee"; + +/* Transaction details: sender row. */ +"TransactionDetailsScene.Row.From" = "From"; + +/* Transaction details: Id row. */ +"TransactionDetailsScene.Row.Id" = "Id"; + +/* Transaction details: recipient row. */ +"TransactionDetailsScene.Row.To" = "To"; + +/* Export transaction: 'Share transaction summary' button */ +"TransactionDetailsScene.Share.Summary" = "Summary"; + +/* Export transaction: 'Share transaction URL' button */ +"TransactionDetailsScene.Share.URL" = "URL"; + +/* Transfer: transfer amount placeholder */ +"TransferScene.Amount.Placeholder" = "to send"; + +/* Transfer: Address not found error */ +"TransferScene.Error.AddressNotFound" = "Address not found"; + +/* Transfer: Address validation error */ +"TransferScene.Error.InvalidAddress" = "Enter a valid address"; + +/* Transfer: Amount is hiegher that user's total money notification */ +"TransferScene.Error.NotEnoughtMoney" = "You don't have that money"; + +/* Transfer: Amount is zero, or even negative notification */ +"TransferScene.Error.TooLittleMoney" = "You should send more money"; + +/* Transfer: recipient address placeholder */ +"TransferScene.Recipient.Placeholder" = "of the recipient"; + +/* Transfer: amount of adamant to transfer. */ +"TransferScene.Row.Amount" = "Amount"; + +/* Transfer: logged user balance. */ +"TransferScene.Row.Balance" = "Balance"; + +/* Transfer: maximum amount to transfer: available account money substracting transfer fee. */ +"TransferScene.Row.MaxToTransfer" = "Max to transfer"; + +/* Transfer: recipient address */ +"TransferScene.Row.Recipient" = "Address"; + +/* Transfer: Send button */ +"TransferScene.Row.Send" = "Send"; + +/* Transfer: total amount of transaction: money to transfer adding fee */ +"TransferScene.Row.Total" = "Total"; + +/* Transfer: transfer fee */ +"TransferScene.Row.TransactionFee" = "Fee"; + +/* Transfer: 'Transfer info' section */ +"TransferScene.Section.TransferInfo" = "Transfer Info"; + +/* Transfer: 'Your wallet' section */ +"TransferScene.Section.YourWallet" = "Your Wallet"; + +/* Transfer: Confirm transfer alert: Send tokens button */ +"TransferScene.Send" = "Send"; + +/* 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$@?"; + +/* Transfer: Processing message */ +"TransferScene.SendingFundsProgress" = "Sending funds..."; + +/* Transfer: Tokens transfered successfully message */ +"TransferScene.TransferSuccessMessage" = "Done!"; diff --git a/Adamant/Assets/Base.lproj/Localizable.stringsdict b/Adamant/Assets/l18n/en.lproj/Localizable.stringsdict similarity index 89% rename from Adamant/Assets/Base.lproj/Localizable.stringsdict rename to Adamant/Assets/l18n/en.lproj/Localizable.stringsdict index 7c8e3072f..df7859949 100644 --- a/Adamant/Assets/Base.lproj/Localizable.stringsdict +++ b/Adamant/Assets/l18n/en.lproj/Localizable.stringsdict @@ -2,7 +2,7 @@ - You have %d new message(s) + NotificationsService.NewMessage.BodyFormat NSStringLocalizedFormatKey You have %#@messages@ @@ -18,7 +18,7 @@ %d new messages - You have %d new transfer(s) + NotificationsService.NewTransfer.BodyFormat NSStringLocalizedFormatKey You have %#@transfers@ diff --git a/Adamant/Assets/l18n/ru.lproj/InfoPlist.strings b/Adamant/Assets/l18n/ru.lproj/InfoPlist.strings new file mode 100644 index 000000000..2fc51ea3b --- /dev/null +++ b/Adamant/Assets/l18n/ru.lproj/InfoPlist.strings @@ -0,0 +1,15 @@ +/* The user-visible name of the bundle; used by Siri and visible on the Home screen in iOS. */ +"CFBundleDisplayName" = "Адамант"; + +/* The short name of the bundle, which may be displayed to users in situations such as the absence of a value for CFBundleDisplayName. This name should be less than 16 characters long. */ +"CFBundleName" = "Адамант"; + +/* Camera authorization reason */ +"NSCameraUsageDescription" = "Чтение QR-кодов с адресами и паролями"; + +/* Readonly access to photolibrary reason */ +"NSPhotoLibraryAddUsageDescription" = "Сохранение созданных QR-кодов"; + +/* ReadWrite access to photolibrary reason */ +"NSPhotoLibraryUsageDescription" = "Сканирование QR-кодов с адресами и паролями со сделанных ранее фотографий"; + diff --git a/Adamant/Assets/l18n/ru.lproj/Localizable.strings b/Adamant/Assets/l18n/ru.lproj/Localizable.strings new file mode 100644 index 000000000..1f85acb36 --- /dev/null +++ b/Adamant/Assets/l18n/ru.lproj/Localizable.strings @@ -0,0 +1,377 @@ +/* Product name */ +"ADAMANT" = "АДАМАНТ"; + +/* ApiService: Bad internal application error, report a bug. Using %@ as error description */ +"AccountServiceError.Internal errorFormat" = "Внутренняя ошибка: %@"; + +/* Login: user typed in invalid passphrase */ +"AccountServiceError.InvalidPassphrase" = "Неправильный пароль"; + +/* Login: user not logged error */ +"AccountServiceError.UserNotLogged" = "Не выполнен вход"; + +/* Login: user typed in wrong passphrase */ +"AccountServiceError.WrongPassphrase" = "Неправильный пароль: %@"; + +/* Account tab: Confirm logout alert: Logout (Ok) button */ +"AccountTab.ConfirmLogout.Logout" = "Выход"; + +/* Account tab: Confirm logout alert */ +"AccountTab.ConfirmLogout.MessageFormat" = "Выйти из %@?"; + +/* Account tab: Inform user that sending tokens not allowed by Apple until the end of ICO */ +"AccountTab.TransferBlocked.Message" = "Ввиду ограничений Apple, отправка токенов отключена до конца ICO. В данный момент вы можете отправлять токены с помощью WebApp по адресу msg.adamant.im"; + +/* Account tab: Balance row title */ +"AccountTab.Row.Balance" = "Баланс"; + +/* Account tab: 'Invest in ICO' button */ +"AccountTab.Row.JoinIco" = "Учавствовать в ICO"; + +/* Account tab: 'Logout' button */ +"AccountTab.Row.Logout" = "Выход"; + +/* Account tab: 'Send tokens' button */ +"AccountTab.Row.SendTokens" = "Отправить токены"; + +/* Account tab: Account section title. */ +"AccountTab.Section.Account" = "Аккаунт"; + +/* Account tab: Actions section title */ +"AccountTab.Section.Actions" = "Действия"; + +/* Account tab: Wallet section title */ +"AccountTab.Section.Wallet" = "Кошелёк"; + +/* Account tab: 'Transfer not allowed' alert 'go to WebApp button' */ +"AccountTab.TransferBlocked.GoToPWA" = "msg.adamant.im"; + +/* Account tab: 'Transfer not allowed' alert title */ +"AccountTab.TransferBlocked.Title" = "Ой!"; + +/* Account page: scene title */ +"AccountTab.Title" = "Аккаунт"; + +/* ApiService: Account not found error. Using %@ for address. */ +"ApiService.Error.AccountNotFoundFormat" = "Аккаун не найден: %@"; + +/* ApiService: Bad internal application error, report a bug. Using %@ for error description */ +"ApiService.Error.InternalErrorFormat" = "Внутренняя ошибка: %@"; + +/* ApiService: No connection message. Generally bad network. */ +"ApiService.Error.NoConnection" = "Нет соединения с сетью"; + +/* ApiService: Remote server returned an error. Using %@ for error description */ +"ApiService.Error.RemoteServerErrorFormat" = "Ошибка на сервере: %@"; + +/* ApiService: User not logged error */ +"ApiService.Error.UserNotLogged" = "Не выполнен вход"; + +/* Serious internal error: Failed to build endpoint url */ +"ApiService.InternalError.EndpointBuildFailed" = "Endpoint build failed. Report a bug"; + +/* Serious internal error: Failed to sign transaction */ +"ApiService.InternalError.FailedTransactionSigning" = "Transaction failed"; + +/* Serious internal error: Error parsing response */ +"ApiService.InternalError.ParsingFailed" = "Parsing failed. Report a bug"; + +/* Unknown internal error */ +"ApiService.InternalError.UnknownError" = "Неизвестная ошибка"; + +/* ChatList: scene title */ +"ChatListPage.Title" = "Чаты"; + +/* Chat: Notify user about bad internal error. Usually this should be reported as a bug. Using %@ for error description */ +"ChatScene.Error.InternalErrorFormat" = "Internal error: %@. Report a bug"; + +/* Chat: Notify user that message cannot be empty */ +"ChatScene.Error.MessageIsEmpty" = "Сообщение пустое!"; + +/* Chat: Message is too long */ +"ChatScene.Error.MessageTooLong" = "Сообщение слишком длинное"; + +/* Chat: Notify user that he doesn't have money to pay a message fee */ +"ChatScene.Error.NotEnoughMoney" = "Недостаточно токенов для отправки сообщения"; + +/* Chat: Notify user about server error. Using %@ for error description */ +"ChatScene.Error.RemoteServerErrorFormat" = "Remote error: %@. Report a bug"; + +/* Chat: message input placeholder */ +"ChatScene.NewMessage.Placeholder" = "Сообщение"; + +/* Chat: Send message button */ +"ChatScene.Send" = "Отправить"; + +/* Known contacts: Adamant ICO message */ +"Chats.IcoMessage" = "У вас есть возможность инвестировать в ICO самого защищенного мессенджера АДАМАНТ. Более раннее участие — более выгодные условия. Подробная информация об условиях на сайте Adamant.im и в Белой книге проекта. Если вы уже ознакомились с нашим сайтом и Белой книгой, но у вас остались вопросы, вы можете задать их, ответив на это сообщение. Мы стараемся реагировать быстро, но иногда возможны задержки до нескольких часов.\\nВажно! Пожалуйста, убедитесь, что вы сохранили пароль к этому аккаунту — выйдите из аккаунта и войдите в него снова. Лучше всего иметь запись пароля и на бумаге. Однако помните, только вы несете ответственность за сохранность вашего пароля. Его невозможно восстановить. А если он попадет в другие руки, ваши деньги будут украдены. Отнеситесь к этому вопросу так серьезно, как если бы цена токенов на вашем кошельке когда-то станет равна миллиарду долларов.\\nЧтобы инвестировать, перейдите в этом месседжере на вкладку Кошелек и нажмите кнопку Инвестировать в ICO, или откройте в браузере веб-страницу Adamant.im/ico/. В форме для инвестирования укажите ваш адрес АДАМАНТа — на него будут перечислены токены ADM. При переходе из мессенджера он будет вставлен автоматически, в другом случае вернитесь в мессенджер и кликните на блок Ваш адрес, он будет скопирован в буфер обмена. Укажите криптовалюту, в которой вы хотите инвестировать, и какую сумму. Вы увидите сколько ADM-токенов вы получите, с учетом скидки за объем: 20–30 ETH: +20%, 30–50 ETH: +30%, 50–90 ETH: +40%, 90+ ETH: +50%. Нажмите кнопку Купить токены АДАМАНТ. Вы получите уникальный адрес, на который необходимо выполнить перевод. Как только на него поступит ожидаемая сумма и транзакция будет подтверждена, вы получите токены ADM. Транзакцию можно выполнить с любого кошелька, включая биржи. Необязятельно переводить точную сумму с учетом комиссии, платеж все равно будет засчитан. Остались вопросы? Спрашивайте.\\nПосле того, как вы инвестируете и получите токены ADM, мы рекомендуем вам хранить их в кошельке как можно дольше. Ежемесячно все нераспроданные на ICO токены будут распределяться по кошелькам пользователей, увеличивая балансы на 5%. Подробную информацию можно получить на сайте Adamant.im и в Белой книге Проекта."; + +/* Known contacts: Adamant pre ICO message */ +"Chats.PreIcoMessage" = "Сегодня последний день, когда мы принимаем инвестиции по минимальной цене 0.001 ETH за токен ADM до начала публичного сейла ICO — 30.01. Это самые выгодные условия для инвесторов. Минимальная инвестиция — эквивалент 2 ETH (на этапе публичного сейла ограничений по минимальной сумме нет, но цена токена начинается с 0.002 ETH). Подробная информация о проекте АДАМАНТ на сайте Adamant.im и в Белой книге. Для участия достаточно ответить на это сообщение, и мы вам поможем."; + +/* Known contacts: Adamant welcome message */ +"Chats.WelcomeMessage" = "Добро пожаловать в самый анонимный и безопасный мессенджер АДАМАНТ. Вам начислены приветственные токены, которые вы можете использовать для ознакомления с мессенджером.\\nПомните, что безопасность и анонимность зависит и от вас самих. Не переходите по ссылкам, которые вы получаете в чатах, иначе ваш IP-адрес может быть определен. Не доверяйте расширениям браузера. Лучше всего передавать ваш ADM-адрес собеседникам лично, а не через другие мессенджеры. Храните вашу секретную фразу (пароль) от аккаунта в тайне. Установите на ваше устройство пароль или закрываете вкладку браузера после завершения диалогов.\\nБолее полную информацию о безопасности и анонимности читайте на странице https://adamant.im/ru-staysecured/\\n\\nНе отвечайте на это сообщение, этот адрес служебный."; + +/* Login: Notify user, that he disabled camera in settings, and need to authorize application. */ +"LoginScene.Error.AuthorizeCamera" = "Для чтения QR кодов необходимо разрешить доступ к камере"; + +/* Login: No network error. */ +"LoginScene.Error.NoInternet" = "Нет соединения с сетью"; + +/* Login: notify user that he is trying to login without a passphrase */ +"LoginScene.Error.NoPassphrase" = "Введите пароль"; + +/* Login: Notify user that device not supported by QR reader */ +"LoginScene.Error.QrNotSupported" = "На этом устройстве не поддерживается чтение QR кодов"; + +/* Login: Notify user that scanned QR doesn't contains a passphrase. */ +"LoginScene.Error.WrongQr" = "QR код не содержит пароля"; + +/* Login: notify user that we are trying to log in */ +"LoginScene.LoggingInProgress" = "Входим…"; + +/* Login: Login into previous account with biometry or pincode */ +"LoginScene.LoginIntoAdamant" = "Вход в Адамант"; + +/* Login: generate new passphrase button */ +"LoginScene.Row.Generate" = "Создать новый пароль"; + +/* Login: Login button */ +"LoginScene.Row.Login" = "Вход"; + +/* Login: Passphrase placeholder */ +"LoginScene.Row.Passphrase.Placeholder" = "Пароль"; + +/* Login: Login with pincode button */ +"LoginScene.Row.Pincode" = "Войти с PIN-кодом"; + +/* Login: Login with QR button. */ +"LoginScene.Row.Qr" = "Войти с QR-кодом"; + +/* Login: security alert, notify user that he must save his new passphrase */ +"LoginScene.Row.SavePassphraseAlert" = "Это пароль для нового Кошелька и Мессенджера. Сохраните его! Для входа в Кошелек не нужно логина, только этот пароль. Восстановление пароля невозможно."; + +/* Login: a small hint for a user, that he can tap on passphrase to save it */ +"LoginScene.Row.TapToSave" = "Нажмите чтобы сохранить"; + +/* Login: login with existing passphrase section */ +"LoginScene.Section.Login" = "Вход"; + +/* Login: Create new account section */ +"LoginScene.Section.NewAccount" = "Новый аккаунт"; + +/* New chat: Recipient address placeholder. Note that address text field always shows U letter, so you can left this line blank. */ +"NewChatScene.Address.Placeholder" = ""; + +/* New chat: Notify user that specified address (%@) not found. Using %@ for address */ +"NewChatScene.Error.AddressNotFoundFormat" = "Адрес не найден: %@"; + +/* New chat: Notify user that he did enter invalid address */ +"NewChatScene.Error.InvalidAddress" = "Неверный адрес"; + +/* New chat: Notify user that he can't start chat with himself */ +"NewChatScene.Error.OwnAddress" = "Вам не нужен анонимный чат, чтобы говорить с самим собой"; + +/* New chat: Remote server returned an error. Using %@ for error description */ +"NewChatScene.Error.RemoteServerFormat" = "Remote server error: %@. Report a bug"; + +/* New Chat: Notify user that scanned QR doesn't contains an address */ +"NewChatScene.Error.WrongQr" = "QR код не содержит адреса"; + +/* New chat: Scan QR with address button */ +"NewChatScene.ScanQr" = "Сканировать QR-код"; + +/* New chat: scene title */ +"NewChatScene.Title" = "Новый Чат"; + +/* Notifications: New message notification title */ +"NotificationsService.NewMessage.Title" = "Новое Сообщение"; + +/* Notifications: New transfer transaction title */ +"NotificationsService.NewTransfer.Title" = "Новый перевод"; + +/* Notifications: User has disabled notifications. Head him into settings */ +"NotificationsService.NotificationsDisabled" = "Уведомления отключены. Вы можете включить их в Настройках"; + +/* Pinpad: Ask user to create new pin */ +"Pinpad.EnterNewPin" = "Введите новый PIN-код"; + +/* Pinpad: Ask user to repeat new pin */ +"Pinpad.ReenterPin" = "Введите PIN-код ещё раз"; + +/* QRGenerator: user typed in wrong invalid */ +"QrGeneratorScene.Error.InvalidPassphrase" = "Введите корректный пароль"; + +/* QRGenerator: Bad Internal generator error message format. Using %@ for error description */ +"QrGeneratorScene.Error.InternalErrorFormat" = "Internal error: %@. Report a bug"; + +/* QRGenerator: Passphrase textview placeholder */ +"QrGeneratorScene.Passphrase.Placeholder" = "Пароль"; + +/* QRGenerator: small 'Tap to save' tooltip under generated QR */ +"QrGeneratorScene.TapToSave" = "Нажмите для сохранения"; + +/* QRGenerator: scene title */ +"QrGeneratorScene.Title" = "QR Генератор"; + +/* Config: turn off 'Stay Logged In' confirmation */ +"SettingsPage.DoNotStayLoggedIn" = "Выходить из системы при выходе из приложения"; + +/* Config: Authorization reason for turning biometry off */ +"SettingsPage.DoNotUseBiometry" = "Отключить вход с биометрией"; + +/* Config: Generate QR with passphrase row */ +"SettingsPage.Row.GenerateQr" = "Создать QR-код с паролем"; + +/* Config: Show notifications */ +"SettingsPage.Row.Notifications" = "Уведомления"; + +/* Config: Stay logged option */ +"SettingsPage.Row.StayLoggedIn" = "Оставаться в системе"; + +/* Config: Version row */ +"SettingsPage.Row.Version" = "Версия"; + +/* Config: Application Info section */ +"SettingsPage.Section.ApplicationInfo" = "Приложение"; + +/* Config: Settings section */ +"SettingsPage.Section.Settings" = "Безопасность"; + +/* Config: Utilities section */ +"SettingsPage.Section.Utilities" = "Утилиты"; + +/* Config: scene title */ +"SettingsPage.Title" = "Настройки"; + +/* Config: Authorization reason for turning biometry on */ +"SettingsPage.UseBiometry" = "Использовать биометрию для входа в систему"; + +/* Shared alert 'Cancel' button. Used anywhere */ +"Shared.Cancel" = "Отмена"; + +/* Shared alert notification: message about item copied to pasteboard. */ +"Shared.CopiedToPasteboard" = "Скопировано!"; + +/* Shared alert 'Copy' button. Used anywhere. Used for copy-paste info. */ +"Shared.CopyToPasteboard" = "Копировать"; + +/* Shared alert Done message. Used anywhere */ +"Shared.Done" = "Готово"; + +/* Shared alert 'Error' title. Used anywhere */ +"Shared.Error" = "Ошибка"; + +/* Shared alert 'Generate QR' button. Used to generate QR codes with addresses and passphrases. Used with sharing and saving, anywhere. */ +"Shared.GenerateQRCode" = "Создать QR-код"; + +/* Shared alert 'Ok' button. Used anywhere */ +"Shared.Ok" = "Ок"; + +/* Shared alert 'Save' button. Used anywhere */ +"Shared.Save" = "Сохранить"; + +/* Shared alert 'Settings' button. Used to go to system Settings app, on application settings page. Should be same as Settings application title. */ +"Shared.Settings" = "Настройки"; + +/* Shared alert 'Share' button. Used anywhere for presenting standart iOS 'Share' menu. */ +"Shared.Share" = "Поделиться"; + +/* Main tab bar: Account page */ +"Tabs.Account" = "Аккаунт"; + +/* Main tab bar: Chats page */ +"Tabs.Chats" = "Чаты"; + +/* Main tab bar: Settings page */ +"Tabs.Settings" = "Настройки"; + +/* Transaction details: amount row. */ +"TransactionDetailsScene.Row.Amount" = "Количество"; + +/* Transaction details: Block id row. */ +"TransactionDetailsScene.Row.Block" = "Блок"; + +/* Transaction details: confirmations row. */ +"TransactionDetailsScene.Row.Confirmations" = "Подтверждения"; + +/* Transaction details: date row. */ +"TransactionDetailsScene.Row.Date" = "Дата"; + +/* Transaction details: 'Open transaction in explorer' row. */ +"TransactionDetailsScene.Row.Explorer" = "Открыть в Explorer"; + +/* Transaction details: fee row. */ +"TransactionDetailsScene.Row.Fee" = "Комиссия"; + +/* Transaction details: sender row. */ +"TransactionDetailsScene.Row.From" = "Отправитель"; + +/* Transaction details: Id row. */ +"TransactionDetailsScene.Row.Id" = "Id"; + +/* Transaction details: recipient row. */ +"TransactionDetailsScene.Row.To" = "Получатель"; + +/* Export transaction: 'Share transaction summary' button */ +"TransactionDetailsScene.Share.Summary" = "Сводка"; + +/* Export transaction: 'Share transaction URL' button */ +"TransactionDetailsScene.Share.URL" = "URL"; + +/* Transfer: transfer amount placeholder */ +"TransferScene.Amount.Placeholder" = "для перевода"; + +/* Transfer: Address not found error */ +"TransferScene.Error.AddressNotFound" = "Адрес не найден"; + +/* Transfer: Address validation error */ +"TransferScene.Error.InvalidAddress" = "Введите корректный адрес"; + +/* Transfer: Amount is hiegher that user's total money notification */ +"TransferScene.Error.NotEnoughtMoney" = "У вас нет такого количества токенов"; + +/* Transfer: Amount is zero, or even negative notification */ +"TransferScene.Error.TooLittleMoney" = "Вам следует отправить больше токенов"; + +/* Transfer: recipient address placeholder */ +"TransferScene.Recipient.Placeholder" = "получателя"; + +/* Transfer: amount of adamant to transfer. */ +"TransferScene.Row.Amount" = "Количество"; + +/* Transfer: logged user balance. */ +"TransferScene.Row.Balance" = "Баланс"; + +/* Transfer: maximum amount to transfer: available account money substracting transfer fee. */ +"TransferScene.Row.MaxToTransfer" = "Максимум"; + +/* Transfer: recipient address */ +"TransferScene.Row.Recipient" = "Адрес"; + +/* Transfer: Send button */ +"TransferScene.Row.Send" = "Отправить"; + +/* Transfer: total amount of transaction: money to transfer adding fee */ +"TransferScene.Row.Total" = "Итого"; + +/* Transfer: transfer fee */ +"TransferScene.Row.TransactionFee" = "Комиссия"; + +/* Transfer: 'Transfer info' section */ +"TransferScene.Section.TransferInfo" = "Перевод"; + +/* Transfer: 'Your wallet' section */ +"TransferScene.Section.YourWallet" = "Ваш кошелёк"; + +/* Transfer: Confirm transfer alert: Send tokens button */ +"TransferScene.Send" = "Отправить"; + +/* 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$@?"; + +/* Transfer: Processing message */ +"TransferScene.SendingFundsProgress" = "Отправляем токены..."; + +/* Transfer: Tokens transfered successfully message */ +"TransferScene.TransferSuccessMessage" = "Готово!"; diff --git a/Adamant/Assets/ru.lproj/Localizable.stringsdict b/Adamant/Assets/l18n/ru.lproj/Localizable.stringsdict similarity index 91% rename from Adamant/Assets/ru.lproj/Localizable.stringsdict rename to Adamant/Assets/l18n/ru.lproj/Localizable.stringsdict index 78a262f35..db18a6a6a 100644 --- a/Adamant/Assets/ru.lproj/Localizable.stringsdict +++ b/Adamant/Assets/l18n/ru.lproj/Localizable.stringsdict @@ -2,7 +2,7 @@ - You have %d new message(s) + NotificationsService.NewMessage.BodyFormat NSStringLocalizedFormatKey У вас %#@messages@ @@ -20,7 +20,7 @@ %d новых сообщения - You have %d new transfer(s) + NotificationsService.NewTransfer.BodyFormat NSStringLocalizedFormatKey У вас %#@transfers@ diff --git a/Adamant/Assets/ru.lproj/InfoPlist.strings b/Adamant/Assets/ru.lproj/InfoPlist.strings deleted file mode 100644 index 2cd714a50..000000000 --- a/Adamant/Assets/ru.lproj/InfoPlist.strings +++ /dev/null @@ -1,15 +0,0 @@ -/* (No Comment) */ -"CFBundleDisplayName" = "Адамант"; - -/* (No Comment) */ -"CFBundleName" = ""; - -/* (No Comment) */ -"NSCameraUsageDescription" = "Необходим доступ к Камере для чтения QR-кодов с адресами и паролями"; - -/* (No Comment) */ -"NSPhotoLibraryAddUsageDescription" = "Необходим доступ к Фотографиям для сохранения созданных QR-кодов"; - -/* (No Comment) */ -"NSPhotoLibraryUsageDescription" = "Необходим доступ к Фотографиям для сканирования QR-кодов с адресами и паролями со сделанных ранее фотографий"; - diff --git a/Adamant/Helpers/MyLittlePinpad+adamant.swift b/Adamant/Helpers/MyLittlePinpad+adamant.swift index 000d2d0d9..f1fe8ed06 100644 --- a/Adamant/Helpers/MyLittlePinpad+adamant.swift +++ b/Adamant/Helpers/MyLittlePinpad+adamant.swift @@ -11,8 +11,8 @@ import MyLittlePinpad extension String.adamantLocalized { struct pinpad { - static let createPin = NSLocalizedString("Enter new pin", comment: "Pinpad: Ask user to create new pin") - static let reenterPin = NSLocalizedString("Re-enter new pin", comment: "Pinpad: Ask user to repeat new pin") + static let createPin = NSLocalizedString("Pinpad.EnterNewPin", comment: "Pinpad: Ask user to create new pin") + static let reenterPin = NSLocalizedString("Pinpad.ReenterPin", comment: "Pinpad: Ask user to repeat new pin") } } diff --git a/Adamant/Helpers/String+localized.swift b/Adamant/Helpers/String+localized.swift index c43e2252c..607db518f 100644 --- a/Adamant/Helpers/String+localized.swift +++ b/Adamant/Helpers/String+localized.swift @@ -18,17 +18,17 @@ extension String { struct alert { // MARK: Buttons - static let cancel = NSLocalizedString("Cancel", comment: "Shared alert 'Cancel' button. Used anywhere") - static let ok = NSLocalizedString("Ok", comment: "Shared alert 'Ok' button. Used anywhere") - static let save = NSLocalizedString("Save", comment: "Shared alert 'Save' button. Used anywhere") - static let settings = NSLocalizedString("Settings", comment: "Shared alert 'Settings' button. Used to go to system Settings app, on application settings page. Should be same as Settings application title.") + static let cancel = NSLocalizedString("Shared.Cancel", comment: "Shared alert 'Cancel' button. Used anywhere") + static let ok = NSLocalizedString("Shared.Ok", comment: "Shared alert 'Ok' button. Used anywhere") + static let save = NSLocalizedString("Shared.Save", comment: "Shared alert 'Save' button. Used anywhere") + static let settings = NSLocalizedString("Shared.Settings", comment: "Shared alert 'Settings' button. Used to go to system Settings app, on application settings page. Should be same as Settings application title.") // MARK: Titles and messages - static let error = NSLocalizedString("Error", comment: "Shared alert 'Error' title. Used anywhere") - static let done = NSLocalizedString("Done", comment: "Shared alert Done message. Used anywhere") + static let error = NSLocalizedString("Shared.Error", comment: "Shared alert 'Error' title. Used anywhere") + static let done = NSLocalizedString("Shared.Done", comment: "Shared alert Done message. Used anywhere") // MARK: Notifications - static let copiedToPasteboardNotification = NSLocalizedString("Copied to Pasteboard", comment: "Shared alert notification: message about item copied to pasteboard.") + static let copiedToPasteboardNotification = NSLocalizedString("Shared.CopiedToPasteboard", comment: "Shared alert notification: message about item copied to pasteboard.") } private init() { } diff --git a/Adamant/Info.plist b/Adamant/Info.plist index c1429a287..3949de9fc 100644 --- a/Adamant/Info.plist +++ b/Adamant/Info.plist @@ -17,17 +17,17 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.3.1 + 0.3.2 CFBundleVersion - 17 + 18 LSRequiresIPhoneOS NSCameraUsageDescription - The camera needed to scan QR codes - addresses and passphrases + The camera needed to scan QR codes for addresses and passphrases NSPhotoLibraryAddUsageDescription - Saving generated QR codes - passphrases and addresses + Saving generated QR codes with passphrases and addresses NSPhotoLibraryUsageDescription - Reading QR codes - passphrases and addresses + Reading QR codes with passphrases and addresses UIAppFonts Exo+2_100_normal.ttf diff --git a/Adamant/ServiceProtocols/AccountService.swift b/Adamant/ServiceProtocols/AccountService.swift index 10cf8010c..a9e4efc65 100644 --- a/Adamant/ServiceProtocols/AccountService.swift +++ b/Adamant/ServiceProtocols/AccountService.swift @@ -55,19 +55,19 @@ enum AccountServiceError { var localized: String { switch self { case .userNotLogged: - return NSLocalizedString("User not logged", comment: "Login: user not logged error") + return NSLocalizedString("AccountServiceError.UserNotLogged", comment: "Login: user not logged error") case .invalidPassphrase: - return NSLocalizedString("Wrong passphrase", comment: "Login: user typed in wrong passphrase") + return NSLocalizedString("AccountServiceError.InvalidPassphrase", comment: "Login: user typed in invalid passphrase") case .wrongPassphrase: - return NSLocalizedString("Wrong passphrase", comment: "Login: user typed in wrong passphrase") + return NSLocalizedString("AccountServiceError.WrongPassphrase", comment: "Login: user typed in wrong passphrase") case .apiError(let error): return error.localized case .internalError(let message, _): - return String.localizedStringWithFormat(NSLocalizedString("Internal error: %@, report this as a bug", comment: "ApiService: Bad internal application error, report a bug"), message) + return String.localizedStringWithFormat(NSLocalizedString("AccountServiceError.Internal errorFormat", comment: "ApiService: Bad internal application error, report a bug. Using %@ as error description"), message) } } } diff --git a/Adamant/ServiceProtocols/ApiService.swift b/Adamant/ServiceProtocols/ApiService.swift index fa233e451..4243c3b26 100644 --- a/Adamant/ServiceProtocols/ApiService.swift +++ b/Adamant/ServiceProtocols/ApiService.swift @@ -23,13 +23,13 @@ enum ApiServiceError: Error { var localized: String { switch self { case .notLogged: - return NSLocalizedString("User not logged", comment: "ApiService: User not logged error") + return NSLocalizedString("ApiService.Error.UserNotLogged", comment: "ApiService: User not logged error") case .accountNotFound: - return NSLocalizedString("Account not found: %@", comment: "ApiService: Account not found error, appending account info.") + return NSLocalizedString("ApiService.Error.AccountNotFoundFormat", comment: "ApiService: Account not found error. Using %@ for address.") case .serverError(error: let error): - return String.localizedStringWithFormat(NSLocalizedString("Remote Server error: %@", comment: "ApiService: Remote server returned an error"), error) + return String.localizedStringWithFormat(NSLocalizedString("ApiService.Error.RemoteServerErrorFormat", comment: "ApiService: Remote server returned an error. Using %@ for error description"), error) case .internalError(let msg, let error): let message: String @@ -41,10 +41,10 @@ enum ApiServiceError: Error { message = msg } - return String.localizedStringWithFormat(NSLocalizedString("Internal error: %@, report this as a bug", comment: "ApiService: Bad internal application error, report a bug"), message) + return String.localizedStringWithFormat(NSLocalizedString("ApiService.Error.InternalErrorFormat", comment: "ApiService: Bad internal application error, report a bug. Using %@ for error description"), message) case .networkError(error: _): - return NSLocalizedString("No connection", comment: "ApiService: No connection message. Generally bad network.") + return NSLocalizedString("ApiService.Error.NoConnection", comment: "ApiService: No connection message. Generally bad network.") } } } diff --git a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift index 8b7ba8324..f028cf4ca 100644 --- a/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift +++ b/Adamant/ServiceProtocols/DataProviders/ChatsProvider.swift @@ -75,11 +75,11 @@ protocol ChatsProvider: DataProvider { var readedLastHeight: Int64? { get } // MARK: - Getting chats and messages - func getChatroomsController() -> NSFetchedResultsController? - func getChatController(for chatroom: Chatroom) -> NSFetchedResultsController? + func getChatroomsController() -> NSFetchedResultsController + func getChatController(for chatroom: Chatroom) -> NSFetchedResultsController /// Unread messages controller. Sections by chatroom. - func getUnreadMessagesController() -> NSFetchedResultsController? + func getUnreadMessagesController() -> NSFetchedResultsController /// Returns asociated with account chatroom, or create new, in viewContext func chatroomWith(_ account: CoreDataAccount) -> Chatroom diff --git a/Adamant/ServiceProtocols/DialogService.swift b/Adamant/ServiceProtocols/DialogService.swift index 8761577f0..59e142419 100644 --- a/Adamant/ServiceProtocols/DialogService.swift +++ b/Adamant/ServiceProtocols/DialogService.swift @@ -28,9 +28,9 @@ enum ShareType { } extension String.adamantLocalized.alert { - static let copyToPasteboard = NSLocalizedString("Copy to Pasteboard", comment: "Shared alert 'Copy' button. Used anywhere. Used for copy-paste info.") - static let share = NSLocalizedString("Share", comment: "Shared alert 'Share' button. Used anywhere for presenting standart iOS 'Share' menu.") - static let generateQr = NSLocalizedString("Generate QR Code", comment: "Shared alert 'Generate QR' button. Used to generate QR codes with addresses and passphrases. Used with sharing and saving, anywhere.") + static let copyToPasteboard = NSLocalizedString("Shared.CopyToPasteboard", comment: "Shared alert 'Copy' button. Used anywhere. Used for copy-paste info.") + static let share = NSLocalizedString("Shared.Share", comment: "Shared alert 'Share' button. Used anywhere for presenting standart iOS 'Share' menu.") + static let generateQr = NSLocalizedString("Shared.GenerateQRCode", comment: "Shared alert 'Generate QR' button. Used to generate QR codes with addresses and passphrases. Used with sharing and saving, anywhere.") } enum ShareContentType { diff --git a/Adamant/ServiceProtocols/NotificationsService.swift b/Adamant/ServiceProtocols/NotificationsService.swift index f922e5bbc..82cfb0dd5 100644 --- a/Adamant/ServiceProtocols/NotificationsService.swift +++ b/Adamant/ServiceProtocols/NotificationsService.swift @@ -10,13 +10,13 @@ import Foundation extension String.adamantLocalized { struct notifications { - static let notificationsDisabled = NSLocalizedString("Notifications disabled. You can reenable notifications in Settings", comment: "Notifications: User has disabled notifications. Head him into settings") + static let notificationsDisabled = NSLocalizedString("NotificationsService.NotificationsDisabled", comment: "Notifications: User has disabled notifications. Head him into settings") - static let newMessageTitle = NSLocalizedString("New message", comment: "Notifications: New message notification title") - static let newMessageBody = NSLocalizedString("You have %d new message(s)", comment: "Notifications: new messages notification body") + static let newMessageTitle = NSLocalizedString("NotificationsService.NewMessage.Title", comment: "Notifications: New message notification title") + static let newMessageBody = NSLocalizedString("NotificationsService.NewMessage.BodyFormat", comment: "Notifications: new messages notification body. Using %d for amount") - static let newTransferTitle = NSLocalizedString("New transfer", comment: "Notifications: New transfer transaction title") - static let newTransferBody = NSLocalizedString("You have %d new transfer(s)", comment: "Notifications: New transfer notification body") + static let newTransferTitle = NSLocalizedString("NotificationsService.NewTransfer.Title", comment: "Notifications: New transfer transaction title") + static let newTransferBody = NSLocalizedString("NotificationsService.NewTransfer.BodyFormat", comment: "Notifications: New transfer notification body. Using %d for amount") private init() {} } diff --git a/Adamant/ServiceProtocols/Router.swift b/Adamant/ServiceProtocols/Router.swift index 87c14d426..78ff7a3f3 100644 --- a/Adamant/ServiceProtocols/Router.swift +++ b/Adamant/ServiceProtocols/Router.swift @@ -7,46 +7,22 @@ // import UIKit +import Swinject -// MARK: - Adamant Story struct -struct AdamantStory: Equatable, Hashable { - let name: String - - init(_ name: String) { - self.name = name - } - - static func ==(lhs: AdamantStory, rhs: AdamantStory) -> Bool { - return lhs.name == rhs.name - } - - var hashValue: Int { - return name.hashValue &* 171717 - } -} // MARK: - Adamant Scene -struct AdamantScene: Equatable, Hashable { +struct AdamantScene { let identifier: String - let story: AdamantStory + let factory: (Resolver) -> UIViewController - init(story: AdamantStory, identifier: String) { - self.story = story + init(identifier: String, factory: @escaping (Resolver) -> UIViewController) { self.identifier = identifier - } - - static func ==(lhs: AdamantScene, rhs: AdamantScene) -> Bool { - return lhs.identifier == rhs.identifier - } - - var hashValue: Int { - return identifier.hashValue ^ story.hashValue &* 717171 + self.factory = factory } } // MARK: - Adamant Router protocol Router: class { - func get(story: AdamantStory) -> UIStoryboard func get(scene: AdamantScene) -> UIViewController } diff --git a/Adamant/Services/AdamantAccountService.swift b/Adamant/Services/AdamantAccountService.swift index 1d27ef587..858b1d3c1 100644 --- a/Adamant/Services/AdamantAccountService.swift +++ b/Adamant/Services/AdamantAccountService.swift @@ -145,13 +145,7 @@ extension AdamantAccountService: AccountService { stateSemaphore.wait() switch state { - case .notLogged: - fallthrough - - case .isLoggingIn: - fallthrough - - case .updating: + case .notLogged, .isLoggingIn, .updating: stateSemaphore.signal() return diff --git a/Adamant/Services/ApiService/AdamantApiService.swift b/Adamant/Services/ApiService/AdamantApiService.swift index 03d1c0a33..f1d30783f 100644 --- a/Adamant/Services/ApiService/AdamantApiService.swift +++ b/Adamant/Services/ApiService/AdamantApiService.swift @@ -33,16 +33,16 @@ class AdamantApiService: ApiService { var localized: String { switch self { case .endpointBuildFailed: - return NSLocalizedString("Endpoint build failed", comment: "Serious internal error: Failed to build endpoint url") + return NSLocalizedString("ApiService.InternalError.EndpointBuildFailed", comment: "Serious internal error: Failed to build endpoint url") case .signTransactionFailed: - return NSLocalizedString("Failed transaction signing", comment: "Serious internal error: Failed to sign transaction") + return NSLocalizedString("ApiService.InternalError.FailedTransactionSigning", comment: "Serious internal error: Failed to sign transaction") case .parsingFailed: - return NSLocalizedString("Parsing failed", comment: "Serious internal error: Error parsing response") + return NSLocalizedString("ApiService.InternalError.ParsingFailed", comment: "Serious internal error: Error parsing response") case .unknownError: - return NSLocalizedString("Unknown error", comment: "Unknown internal error") + return NSLocalizedString("ApiService.InternalError.UnknownError", comment: "Unknown internal error") } } } diff --git a/Adamant/Services/DataProviders/AdamantAccountsProvider.swift b/Adamant/Services/DataProviders/AdamantAccountsProvider.swift index 67fa6da68..65ce214ef 100644 --- a/Adamant/Services/DataProviders/AdamantAccountsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantAccountsProvider.swift @@ -34,12 +34,12 @@ private enum Contacts { var messages: [String:String] { switch self { case .adamantBountyWallet: - return ["chats.welcome_message": NSLocalizedString("Welcome to ADAMANT, the most secure and anonymous messenger. You are credited with bounty tokens, which you can use to get acquainted with the messenger.\nRemember, your security and anonymity is up to you also. Do not follow links you receive, otherwise your IP can be compromised. Do not trust browser extensions. Better to share your ADM address personally, but not using other messengers. Keep your secret passphrase secure. Set a password on your device or logout before leaving.\nLearn more about security and anonymity at https://adamant.im/staysecured/.\n\nDo not reply to this message, it is a system account.", comment: "Known contacts: Adamant welcome message")] + return ["chats.welcome_message": NSLocalizedString("Chats.WelcomeMessage", comment: "Known contacts: Adamant welcome message")] case .adamantIco: return [ - "chats.preico_message": NSLocalizedString("You have a possibility to invest in ADAMANT, the most secure and anonymous messenger. Now is a Pre-ICO stage — the most profitable for investors. Learn more on Adamant.im website or in the Whitepaper. To participate just reply to this message and we will assist. We are eager to answer quickly, but sometimes delays for a couple of hours are possible.\nAfter you invest and receive ADM tokens, we recommend to keep them as long as possible. All of unsold tokens during ICO will be distributed among users wallets, adding 5% monthly. Additional info is on Adamant.im website and in the Whitepaper.", comment: "Known contacts: Adamant pre ICO message"), - "chats.ico_message": NSLocalizedString("You have a possibility to invest in ICO of ADAMANT, the most secure and anonymous messenger. Earlier you participate, better offer you will get. Learn more on Adamant.im website or in the Whitepaper. To invest, go to Wallet→Invest in the ICO, or follow a website page Adamant.im/ico/. If you still have any questions, you can ask them by replying to this message. We are eager to answer quickly, but sometimes delays for a couple of hours are possible.\nAfter you invest and receive ADM tokens, we recommend to keep them as long as possible. All of unsold tokens during ICO will be distributed among users wallets, adding 5% monthly. Additional info is on Adamant.im website and in the Whitepaper.", comment: "Known contacts: Adamant ICO message") + "chats.preico_message": NSLocalizedString("Chats.PreIcoMessage", comment: "Known contacts: Adamant pre ICO message"), + "chats.ico_message": NSLocalizedString("Chats.IcoMessage", comment: "Known contacts: Adamant ICO message") ] } } diff --git a/Adamant/Services/DataProviders/AdamantChatsProvider.swift b/Adamant/Services/DataProviders/AdamantChatsProvider.swift index 53b7acb72..59650bc18 100644 --- a/Adamant/Services/DataProviders/AdamantChatsProvider.swift +++ b/Adamant/Services/DataProviders/AdamantChatsProvider.swift @@ -34,13 +34,28 @@ class AdamantChatsProvider: ChatsProvider { // MARK: Lifecycle init() { NotificationCenter.default.addObserver(forName: Notification.Name.adamantUserLoggedIn, object: nil, queue: nil) { [weak self] notification in - self?.update() + guard let store = self?.securedStore else { + return + } + + guard let loggedAddress = notification.userInfo?[AdamantUserInfoKey.AccountService.loggedAccountAddress] as? String else { + store.remove(StoreKey.chatProvider.address) + store.remove(StoreKey.chatProvider.receivedLastHeight) + store.remove(StoreKey.chatProvider.readedLastHeight) + return + } - if let address = notification.userInfo?[AdamantUserInfoKey.AccountService.loggedAccountAddress] as? String { - self?.securedStore.set(address, for: StoreKey.chatProvider.address) + if let savedAddress = self?.securedStore.get(StoreKey.chatProvider.address), savedAddress == loggedAddress { + if let raw = store.get(StoreKey.chatProvider.readedLastHeight), let h = Int64(raw) { + self?.readedLastHeight = h + } } else { - print("Can't get logged address.") + store.remove(StoreKey.chatProvider.receivedLastHeight) + store.remove(StoreKey.chatProvider.readedLastHeight) + store.set(loggedAddress, for: StoreKey.chatProvider.address) } + + self?.update() } NotificationCenter.default.addObserver(forName: Notification.Name.adamantUserLoggedOut, object: nil, queue: nil) { [weak self] _ in @@ -100,8 +115,11 @@ extension AdamantChatsProvider { private func reset(notify: Bool) { let prevState = self.state setState(.updating, previous: prevState, notify: false) // Block update calls + receivedLastHeight = nil readedLastHeight = nil + securedStore.remove(StoreKey.chatProvider.receivedLastHeight) + securedStore.remove(StoreKey.chatProvider.readedLastHeight) let chatrooms = NSFetchRequest(entityName: Chatroom.entityName) let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) @@ -170,10 +188,20 @@ extension AdamantChatsProvider { userInfo: [AdamantUserInfoKey.ChatProvider.lastMessageHeight:h]) } - self?.readedLastHeight = self?.receivedLastHeight + if let h = self?.receivedLastHeight { + self?.readedLastHeight = h + } else { + self?.readedLastHeight = 0 + } - if let h = self?.receivedLastHeight, let store = self?.securedStore { - store.set(String(h), for: StoreKey.chatProvider.receivedLastHeight) + if let store = self?.securedStore { + if let h = self?.receivedLastHeight { + store.set(String(h), for: StoreKey.chatProvider.receivedLastHeight) + } + + if let h = self?.readedLastHeight, h > 0 { + store.set(String(h), for: StoreKey.chatProvider.readedLastHeight) + } } } } @@ -344,24 +372,18 @@ extension AdamantChatsProvider { // MARK: - Getting messages extension AdamantChatsProvider { - func getChatroomsController() -> NSFetchedResultsController? { + func getChatroomsController() -> NSFetchedResultsController { let request: NSFetchRequest = NSFetchRequest(entityName: Chatroom.entityName) request.sortDescriptors = [NSSortDescriptor(key: "updatedAt", ascending: false)] request.predicate = NSPredicate(format: "partner!=nil") let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: stack.container.viewContext, sectionNameKeyPath: nil, cacheName: nil) - do { - try controller.performFetch() - return controller - } catch { - print("Error fetching request: \(error)") - return nil - } + return controller } - func getChatController(for chatroom: Chatroom) -> NSFetchedResultsController? { + func getChatController(for chatroom: Chatroom) -> NSFetchedResultsController { guard let context = chatroom.managedObjectContext else { - return nil + fatalError() } let request: NSFetchRequest = NSFetchRequest(entityName: ChatTransaction.entityName) @@ -369,13 +391,7 @@ extension AdamantChatsProvider { request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)] let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil) - do { - try controller.performFetch() - return controller - } catch { - print("Error fetching request: \(error)") - return nil - } + return controller } func chatroomWith(_ account: CoreDataAccount) -> Chatroom { @@ -400,21 +416,14 @@ extension AdamantChatsProvider { return chatroom } - func getUnreadMessagesController() -> NSFetchedResultsController? { + func getUnreadMessagesController() -> NSFetchedResultsController { let request = NSFetchRequest(entityName: ChatTransaction.entityName) request.predicate = NSPredicate(format: "isUnread == true") request.sortDescriptors = [NSSortDescriptor.init(key: "date", ascending: false)] let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: stack.container.viewContext, sectionNameKeyPath: "chatroom.partner.address", cacheName: nil) - controller.section - do { - try controller.performFetch() - return controller - } catch { - print("Error fetching unread messages: \(error)") - return nil - } + return controller } } @@ -633,12 +642,6 @@ extension AdamantChatsProvider { chatroom.hasUnreadMessages = true trs.forEach({$0.isUnread = true}) } - } else { - let msgs = Dictionary(grouping: newChatTransactions, by: ({ (t: ChatTransaction) -> Chatroom in t.chatroom!})) - for (chatroom, trs) in msgs { - chatroom.hasUnreadMessages = true - trs.forEach({$0.isUnread = true}) - } } diff --git a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift index a5e1efaa6..06c762d77 100644 --- a/Adamant/Services/DataProviders/AdamantTransfersProvider.swift +++ b/Adamant/Services/DataProviders/AdamantTransfersProvider.swift @@ -179,7 +179,7 @@ extension AdamantTransfersProvider { let request = NSFetchRequest(entityName: TransferTransaction.entityName) request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)] let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: stack.container.viewContext, sectionNameKeyPath: nil, cacheName: nil) - try! controller.performFetch() + return controller } @@ -188,7 +188,7 @@ extension AdamantTransfersProvider { request.predicate = NSPredicate(format: "isUnread == true") request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)] let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: stack.container.viewContext, sectionNameKeyPath: nil, cacheName: nil) - try! controller.performFetch() + return controller } diff --git a/Adamant/Services/SwinjectedRouter.swift b/Adamant/Services/SwinjectedRouter.swift index 45ac7a72f..1103b59ec 100644 --- a/Adamant/Services/SwinjectedRouter.swift +++ b/Adamant/Services/SwinjectedRouter.swift @@ -7,22 +7,12 @@ // import UIKit -import SwinjectStoryboard +import Swinject class SwinjectedRouter: Router { - private var storyboards = [AdamantStory: UIStoryboard]() - - func get(story: AdamantStory) -> UIStoryboard { - if let storyboard = storyboards[story] { - return storyboard - } else { - let storyboard = SwinjectStoryboard.create(name: story.name, bundle: nil) - storyboards[story] = storyboard - return storyboard - } - } + weak var container: Container? func get(scene: AdamantScene) -> UIViewController { - return get(story: scene.story).instantiateViewController(withIdentifier: scene.identifier) + return scene.factory(container!) } } diff --git a/Adamant/SharedCells/AccountCell.xib b/Adamant/SharedCells/AccountCell.xib new file mode 100644 index 000000000..d117f8d00 --- /dev/null +++ b/Adamant/SharedCells/AccountCell.xib @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Adamant/SharedCells/EurekaAccountRow.swift b/Adamant/SharedCells/EurekaAccountRow.swift new file mode 100644 index 000000000..d4f427edc --- /dev/null +++ b/Adamant/SharedCells/EurekaAccountRow.swift @@ -0,0 +1,26 @@ +// +// EurekaAccountRow.swift +// Adamant +// +// Created by Anokhov Pavel on 17.03.2018. +// Copyright © 2018 Adamant. All rights reserved. +// + +import UIKit +import Eureka + +public class AccountCell: Cell, CellType { + @IBOutlet weak var avatarImageView: UIImageView! + @IBOutlet weak var addressLabel: UILabel! + + public override func update() { + addressLabel.text = row.value + } +} + +public final class AccountRow: Row, RowType { + required public init(tag: String?) { + super.init(tag: tag) + cellProvider = CellProvider(nibName: "AccountCell") + } +} diff --git a/Adamant/SharedCells/RoundAvatarTableViewCell.swift b/Adamant/SharedCells/RoundAvatarTableViewCell.swift deleted file mode 100644 index 140efdbf9..000000000 --- a/Adamant/SharedCells/RoundAvatarTableViewCell.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// RoundAvatarTableViewCell.swift -// Adamant -// -// Created by Anokhov Pavel on 08.01.2018. -// Copyright © 2018 Adamant. All rights reserved. -// - -import UIKit - -extension SharedCell { - static let RoundAvatarCell = SharedCell(cellIdentifier: "roundAvatar", - xibName: "RoundAvatarTableViewCell", - rowHeight: 72) -} - -class RoundAvatarTableViewCell: UITableViewCell { - - - // MARK: - IBOutlets - - @IBOutlet weak var avatarImageView: UIImageView! - @IBOutlet weak var mainTextLabel: UILabel! - @IBOutlet weak var detailsTextLabel: UILabel! - - - // MARK: - Properties - - var mainText: String? { - didSet { - if let text = mainText, text.count > 0 { - mainTextLabel.isHidden = false - mainTextLabel.text = text - } else { - mainTextLabel.isHidden = true - } - } - } - - var detailsText: String? { - didSet { - if let text = detailsText, text.count > 0 { - detailsTextLabel.isHidden = false - detailsTextLabel.text = text - } else { - detailsTextLabel.isHidden = true - } - } - } - - override func awakeFromNib() { - super.awakeFromNib() - // Initialization code - } - - override func setSelected(_ selected: Bool, animated: Bool) { - super.setSelected(selected, animated: animated) - - // Configure the view for the selected state - } - -} diff --git a/Adamant/SharedCells/RoundAvatarTableViewCell.xib b/Adamant/SharedCells/RoundAvatarTableViewCell.xib deleted file mode 100644 index 774f692ca..000000000 --- a/Adamant/SharedCells/RoundAvatarTableViewCell.xib +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - Exo2-Regular - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Adamant/Stories/Account/AccountDependencies.swift b/Adamant/Stories/Account/AccountDependencies.swift deleted file mode 100644 index 314d8b4a2..000000000 --- a/Adamant/Stories/Account/AccountDependencies.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// AccountDependencies.swift -// Adamant -// -// Created by Anokhov Pavel on 07.01.2018. -// Copyright © 2018 Adamant. All rights reserved. -// - -import Swinject - -extension Container { - func registerAdamantAccountStory() { - self.storyboardInitCompleted(AccountViewController.self) { r, c in - c.accountService = r.resolve(AccountService.self) - c.dialogService = r.resolve(DialogService.self) - c.router = r.resolve(Router.self) - } - self.storyboardInitCompleted(TransactionsViewController.self) { (r, c) in - c.cellFactory = r.resolve(CellFactory.self) - c.transfersProvider = r.resolve(TransfersProvider.self) - } - self.storyboardInitCompleted(TransferViewController.self) { (r, c) in - c.apiService = r.resolve(ApiService.self) - c.accountService = r.resolve(AccountService.self) - c.dialogService = r.resolve(DialogService.self) - } - self.storyboardInitCompleted(TransactionDetailsViewController.self) { (r, c) in - c.dialogService = r.resolve(DialogService.self) - } - } -} diff --git a/Adamant/Stories/Account/AccountRoutes.swift b/Adamant/Stories/Account/AccountRoutes.swift index a9539e552..8e782c2be 100644 --- a/Adamant/Stories/Account/AccountRoutes.swift +++ b/Adamant/Stories/Account/AccountRoutes.swift @@ -8,13 +8,22 @@ import Foundation -extension AdamantStory { - static let Account = AdamantStory("Account") -} - extension AdamantScene { - static let AccountDetails = AdamantScene(story: .Account, identifier: "AccountViewController") - static let TransactionsList = AdamantScene(story: .Account, identifier: "TransactionsViewController") - static let TransactionDetails = AdamantScene(story: .Account, identifier: "TransactionDetailsViewController") - static let Transfer = AdamantScene(story: .Account, identifier: "TransferViewController") + struct Account { + static let account = AdamantScene(identifier: "AccountViewController", factory: { r in + let c = AccountViewController() + c.accountService = r.resolve(AccountService.self) + c.dialogService = r.resolve(DialogService.self) + c.router = r.resolve(Router.self) + return c + }) + + static let transfer = AdamantScene(identifier: "TransferViewController", factory: { r in + let c = TransferViewController() + c.apiService = r.resolve(ApiService.self) + c.accountService = r.resolve(AccountService.self) + c.dialogService = r.resolve(DialogService.self) + return c + }) + } } diff --git a/Adamant/Stories/Account/AccountViewController.swift b/Adamant/Stories/Account/AccountViewController.swift index 8ea338780..25c9e5670 100644 --- a/Adamant/Stories/Account/AccountViewController.swift +++ b/Adamant/Stories/Account/AccountViewController.swift @@ -8,53 +8,98 @@ import UIKit import SafariServices +import Eureka // MARK: - Localization extension String.adamantLocalized { struct account { - static let rowBalance = NSLocalizedString("Balance", comment: "Wallet page: Balance row title") - static let rowSendTokens = NSLocalizedString("Send Tokens", comment: "Wallet page: 'Send tokens' button") - static let rowInvest = NSLocalizedString("Invest in ICO", comment: "Wallet page: 'Invest in ICO' button") - static let rowLogout = NSLocalizedString("Logout", comment: "Wallet page: 'Logout' button") + static let title = NSLocalizedString("AccountTab.Title", comment: "Account page: scene title") - static let sorryAlert = NSLocalizedString("Sorry!", comment: "Wallet page: 'Transfer not allowed' alert title") - static let webApp = NSLocalizedString("Go to msg.adamant.im", comment: "Wallet page: 'Transfer not allowed' alert 'go to WebApp button'") - static let transferNotAllowed = NSLocalizedString("Due to Apple restrictions, sending tokens is not allowed until the end of the ICO. For now, you can send tokens using WebApp at msg.adamant.im", comment: "Wallet page: Inform user that sending tokens not allowed by Apple until the end of ICO") - - static let sectionAccount = NSLocalizedString("Account", comment: "Wallet page: Account section title.") - static let sectionWallet = NSLocalizedString("Wallet", comment: "Wallet page: Wallet section title") - static let sectionActions = NSLocalizedString("Actions", comment: "Wallet page: Actions section title") + static let sorryAlert = NSLocalizedString("AccountTab.TransferBlocked.Title", comment: "Account tab: 'Transfer not allowed' alert title") + static let webApp = NSLocalizedString("AccountTab.TransferBlocked.GoToPWA", comment: "Account tab: 'Transfer not allowed' alert 'go to WebApp button'") + static let transferNotAllowed = NSLocalizedString("AccountTab.TransferBlocked.Message", comment: "Account tab: Inform user that sending tokens not allowed by Apple until the end of ICO") private init() { } } } fileprivate extension String.adamantLocalized.alert { - static let logoutMessageFormat = NSLocalizedString("Logout from %@?", comment: "Wallet page: Confirm logout alert") - static let logoutButton = NSLocalizedString("Logout", comment: "Wallet page: Confirm logout alert: Logout (Ok) button") + static let logoutMessageFormat = NSLocalizedString("AccountTab.ConfirmLogout.MessageFormat", comment: "Account tab: Confirm logout alert") + static let logoutButton = NSLocalizedString("AccountTab.ConfirmLogout.Logout", comment: "Account tab: Confirm logout alert: Logout (Ok) button") } // MARK: - -class AccountViewController: UIViewController { +class AccountViewController: FormViewController { // MARK: - Constants - private let cellIdentifier = "cell" - private let showTransactionsSegue = "showTransactions" - private let showTransferSegue = "showTransfer" - private let webAppUrl = URL.init(string: "https://msg.adamant.im") - private enum Sections: Int { - case account = 0, wallet, actions + + // MARK: - Rows & Sections + private enum Sections { + case account + case wallet + case actions - static let total = 3 + var localized: String { + switch self { + case .account: + return NSLocalizedString("AccountTab.Section.Account", comment: "Account tab: Account section title.") + + case .wallet: + return NSLocalizedString("AccountTab.Section.Wallet", comment: "Account tab: Wallet section title") + + case .actions: + return NSLocalizedString("AccountTab.Section.Actions", comment: "Account tab: Actions section title") + } + } } - private enum WalletRows: Int { - case balance, sendTokens, invest + private enum Rows { + case account + case balance + case sendTokens + case invest + case logout - static let total = 3 + var tag: String { + switch self { + case .account: + return "acc" + + case .balance: + return "balance" + + case .sendTokens: + return "sendTokens" + + case .invest: + return "invest" + + case .logout: + return "logout" + } + } + + var localized: String { + switch self { + case .account: + return "" + + case .balance: + return NSLocalizedString("AccountTab.Row.Balance", comment: "Account tab: Balance row title") + + case .sendTokens: + return NSLocalizedString("AccountTab.Row.SendTokens", comment: "Account tab: 'Send tokens' button") + + case .invest: + return NSLocalizedString("AccountTab.Row.JoinIco", comment: "Account tab: 'Invest in ICO' button") + + case .logout: + return NSLocalizedString("AccountTab.Row.Logout", comment: "Account tab: 'Logout' button") + } + } } @@ -64,272 +109,238 @@ class AccountViewController: UIViewController { var router: Router! - // MARK: - IBOutlets - @IBOutlet weak var tableView: UITableView! - - // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() - - tableView.delegate = self - tableView.dataSource = self - - NotificationCenter.default.addObserver(forName: .adamantUserLoggedIn, object: nil, queue: OperationQueue.main) { [weak self] _ in - self?.tableView.reloadData() - } - NotificationCenter.default.addObserver(forName: .adamantUserLoggedOut, object: nil, queue: OperationQueue.main) { [weak self] _ in - self?.tableView.reloadData() - } - NotificationCenter.default.addObserver(forName: .adamantAccountDataUpdated, object: nil, queue: OperationQueue.main) { [weak self] _ in - guard let account = self?.accountService.account, - let cell = self?.tableView.cellForRow(at: IndexPath(row: 0, section: 1)) else { - return - } - - cell.detailTextLabel?.text = AdamantUtilities.format(balance: account.balance) - } - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - if let indexPath = tableView.indexPathForSelectedRow { - tableView.deselectRow(at: indexPath, animated: animated) - } + navigationItem.title = String.adamantLocalized.account.title + navigationOptions = .Disabled - NotificationCenter.default.addObserver(forName: Notification.Name.adamantAccountDataUpdated, object: nil, queue: OperationQueue.main) { _ in - self.refreshBalanceCell() - } - } - - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - guard let identifier = segue.identifier else { - return - } - - switch identifier { - case showTransferSegue: - if let account = accountService.account, let vc = segue.destination as? TransferViewController { - vc.account = account - } + // MARK: Account Section + form +++ Section(Sections.account.localized) - default: - return - } - } - - deinit { - NotificationCenter.default.removeObserver(self) - } -} - - -// MARK: - UITableView -extension AccountViewController: UITableViewDataSource, UITableViewDelegate { - func numberOfSections(in tableView: UITableView) -> Int { - if accountService.account != nil { - return Sections.total - } else { - return 0 - } - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if accountService.account != nil, let sect = Sections(rawValue: section) { - switch sect { - case .account: return 1 - case .wallet: return WalletRows.total - case .actions: return 1 + <<< AccountRow() { + $0.tag = Rows.account.tag + $0.cell.height = {65} } - } else { - return 0 - } - } - - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - - if indexPath.section == 0 && indexPath.row == 0 { - return 65 - } - - return 44.5 - } - - func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { - return UIView() - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - guard let section = Sections(rawValue: indexPath.section) else { - return - } + .cellUpdate({ [weak self] (cell, row) in + cell.avatarImageView.tintColor = UIColor.adamantChatIcons + if let label = cell.addressLabel { + label.font = UIFont.adamantPrimary(size: 17) + label.textColor = UIColor.adamantPrimary + } + + row.value = self?.accountService.account?.address + }) + .onCellSelection({ [weak self] (_, row) in + guard let address = self?.accountService.account?.address else { + return + } + + let encodedAddress = AdamantUriTools.encode(request: AdamantUri.address(address: address, params: nil)) + + self?.dialogService.presentShareAlertFor(string: encodedAddress, + types: [.copyToPasteboard, .share, .generateQr(sharingTip: address)], + excludedActivityTypes: ShareContentType.address.excludedActivityTypes, + animated: true, + completion: { + guard let indexPath = row.indexPath else { + return + } + self?.tableView.deselectRow(at: indexPath, animated: true) + }) + }) - switch section { - case .account: - tableView.deselectRow(at: indexPath, animated: true) - - guard let address = self.accountService.account?.address else { - return + // MARK: Wallet section + +++ Section(Sections.wallet.localized) + // MARK: Balance + <<< LabelRow() { [weak self] in + $0.tag = Rows.balance.tag + $0.title = Rows.balance.localized + + if let balance = self?.accountService?.account?.balance { + $0.value = AdamantUtilities.format(balance: balance) + } } - - let encodedAddress = AdamantUriTools.encode(request: AdamantUri.address(address: address, params: nil)) - - dialogService.presentShareAlertFor(string: encodedAddress, - types: [.copyToPasteboard, .share, .generateQr(sharingTip: address)], - excludedActivityTypes: ShareContentType.address.excludedActivityTypes, - animated: true, - completion: nil) - - case .wallet: - guard let row = WalletRows(rawValue: indexPath.row) else { - return + .cellSetup({ (cell, _) in + cell.selectionStyle = .gray + }) + .cellUpdate({ (cell, _) in + if let label = cell.textLabel { + label.font = UIFont.adamantPrimary(size: 17) + label.textColor = UIColor.adamantPrimary + } + cell.accessoryType = .disclosureIndicator + }) + .onCellSelection({ [weak self] (_, _) in + guard let vc = self?.router.get(scene: AdamantScene.Transactions.transactions), let nav = self?.navigationController else { + return + } + + nav.pushViewController(vc, animated: true) + }) + + // MARK: Send tokens + <<< LabelRow() { + $0.tag = Rows.sendTokens.tag + $0.title = Rows.sendTokens.localized } - - switch row { - case .balance: - performSegue(withIdentifier: showTransactionsSegue, sender: nil) + .cellSetup({ (cell, _) in + cell.selectionStyle = .gray + }) + .cellUpdate({ (cell, _) in + if let label = cell.textLabel { + label.font = UIFont.adamantPrimary(size: 17) + label.textColor = UIColor.adamantPrimary + } - case .sendTokens: -// performSegue(withIdentifier: showTransferSegue, sender: nil) - // + cell.accessoryType = .disclosureIndicator + }) + .onCellSelection({ [weak self] (_, row) in let alert = UIAlertController(title: String.adamantLocalized.account.sorryAlert, message: String.adamantLocalized.account.transferNotAllowed, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: String.adamantLocalized.alert.cancel, style: .cancel, handler: nil)) + let cancel = UIAlertAction(title: String.adamantLocalized.alert.cancel, style: .cancel) { _ in + guard let indexPath = row.indexPath else { + return + } + + self?.tableView.deselectRow(at: indexPath, animated: true) + } + + alert.addAction(cancel) - if let url = self.webAppUrl { - alert.addAction(UIAlertAction(title: String.adamantLocalized.account.webApp, style: .default, handler: { [weak self] _ in + if let url = self?.webAppUrl { + let webApp = UIAlertAction(title: String.adamantLocalized.account.webApp, style: .default) { [weak self] _ in let safari = SFSafariViewController(url: url) safari.preferredControlTintColor = UIColor.adamantPrimary self?.present(safari, animated: true, completion: nil) - })) + } + alert.addAction(webApp) } - present(alert, animated: true, completion: { [weak self] in - self?.tableView.deselectRow(at: indexPath, animated: true) - }) - // + self?.present(alert, animated: true, completion: nil) + }) + + // MARK: ICO + <<< LabelRow() { + $0.tag = Rows.invest.tag + $0.title = Rows.invest.localized + } + .cellSetup({ (cell, _) in + cell.selectionStyle = .gray + }) + .cellUpdate({ (cell, _) in + if let label = cell.textLabel { + label.font = UIFont.adamantPrimary(size: 17) + label.textColor = UIColor.adamantPrimary + } - case .invest: - guard let address = accountService.account?.address, + cell.accessoryType = .disclosureIndicator + }) + .onCellSelection({ [weak self] (_, _) in + guard let address = self?.accountService.account?.address, let url = URL(string: "https://adamant.im/ico/?wallet=\(address)") else { return } let safari = SFSafariViewController(url: url) safari.preferredControlTintColor = UIColor.adamantPrimary - present(safari, animated: true, completion: nil) - return - } - - case .actions: - guard let address = accountService.account?.address else { - return - } + self?.present(safari, animated: true, completion: nil) + }) + + // MARK: Actions section + +++ Section(Sections.actions.localized) - let alert = UIAlertController(title: String.localizedStringWithFormat(String.adamantLocalized.alert.logoutMessageFormat, address), message: nil, preferredStyle: .alert) - let cancel = UIAlertAction(title: String.adamantLocalized.alert.cancel, style: .cancel) { [weak self] _ in - self?.tableView.deselectRow(at: indexPath, animated: true) + <<< LabelRow() { + $0.tag = Rows.logout.tag + $0.title = Rows.logout.localized } - let logout = UIAlertAction(title: String.adamantLocalized.alert.logoutButton, style: .default) { [weak self] _ in - self?.accountService.logout() - if let vc = self?.router.get(scene: .Login) { - self?.dialogService.present(vc, animated: true, completion: nil) + .cellSetup({ (cell, _) in + cell.selectionStyle = .gray + }) + .cellUpdate({ (cell, _) in + if let label = cell.textLabel { + label.font = UIFont.adamantPrimary(size: 17) + label.textColor = UIColor.adamantPrimary } - } - - alert.addAction(cancel) - alert.addAction(logout) - present(alert, animated: true, completion: nil) + + cell.accessoryType = .disclosureIndicator + }) + .onCellSelection({ [weak self] (_, row) in + guard let address = self?.accountService.account?.address else { + return + } + + let alert = UIAlertController(title: String.localizedStringWithFormat(String.adamantLocalized.alert.logoutMessageFormat, address), message: nil, preferredStyle: .alert) + let cancel = UIAlertAction(title: String.adamantLocalized.alert.cancel, style: .cancel) { _ in + guard let indexPath = row.indexPath else { + return + } + + self?.tableView.deselectRow(at: indexPath, animated: true) + } + let logout = UIAlertAction(title: String.adamantLocalized.alert.logoutButton, style: .default) { [weak self] _ in + self?.accountService.logout() + if let vc = self?.router.get(scene: AdamantScene.Login.login) { + self?.dialogService.present(vc, animated: true, completion: nil) + } + } + + alert.addAction(cancel) + alert.addAction(logout) + self?.present(alert, animated: true, completion: nil) + }) + + + // MARK: Notifications + NotificationCenter.default.addObserver(forName: .adamantUserLoggedIn, object: nil, queue: OperationQueue.main) { [weak self] _ in + self?.refreshBalanceCell() } + NotificationCenter.default.addObserver(forName: .adamantUserLoggedOut, object: nil, queue: OperationQueue.main) { [weak self] _ in + self?.refreshBalanceCell() + } + + NotificationCenter.default.addObserver(forName: .adamantAccountDataUpdated, object: nil, queue: OperationQueue.main) { [weak self] _ in + self?.refreshBalanceCell() + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if let indexPath = tableView.indexPathForSelectedRow { + tableView.deselectRow(at: indexPath, animated: animated) + } + } + + deinit { + NotificationCenter.default.removeObserver(self) } } -// MARK: - UITableView Cells +// MARK: - Other extension AccountViewController { - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let account = accountService.account, - let section = Sections(rawValue: indexPath.section) else { - return UITableViewCell(style: .default, reuseIdentifier: nil) - } + private func refreshBalanceCell() { + let address: String? + let balance: String? - let cell: UITableViewCell - if let c = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) { - cell = c + if let account = accountService.account { + address = account.address + balance = AdamantUtilities.format(balance: account.balance) } else { - cell = UITableViewCell(style: .value1, reuseIdentifier: cellIdentifier) - cell.accessoryType = .disclosureIndicator - cell.textLabel?.font = UIFont.adamantPrimary(size: 17) - cell.detailTextLabel?.font = UIFont.adamantPrimary(size: 17) - - cell.textLabel?.textColor = UIColor.adamantPrimary - cell.detailTextLabel?.textColor = UIColor.adamantPrimary - - cell.imageView?.tintColor = UIColor.adamantChatIcons - } - - switch section { - case .account: - cell.textLabel?.text = account.address - cell.detailTextLabel?.text = nil - cell.imageView?.image = #imageLiteral(resourceName: "account") - - case .wallet: - guard let row = WalletRows(rawValue: indexPath.row) else { - break - } - - switch row { - case .balance: - cell.textLabel?.text = String.adamantLocalized.account.rowBalance - cell.detailTextLabel?.text = AdamantUtilities.format(balance: account.balance) - cell.imageView?.image = nil - - case .sendTokens: - cell.textLabel?.text = String.adamantLocalized.account.rowSendTokens - cell.detailTextLabel?.text = nil - cell.imageView?.image = nil - - case .invest: - cell.textLabel?.text = String.adamantLocalized.account.rowInvest - cell.detailTextLabel?.text = nil - cell.imageView?.image = nil - } - - case .actions: - cell.textLabel?.text = String.adamantLocalized.account.rowLogout - cell.detailTextLabel?.text = nil - cell.imageView?.image = nil + address = nil + balance = nil } - return cell - } - - func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - guard let sect = Sections(rawValue: section) else { - return nil + if let row: AccountRow = form.rowBy(tag: Rows.account.tag) { + row.value = address + row.reload() } - switch sect { - case .account: - return String.adamantLocalized.account.sectionAccount - - case .wallet: - return String.adamantLocalized.account.sectionWallet - - case .actions: - return String.adamantLocalized.account.sectionActions + if let row: LabelRow = form.rowBy(tag: Rows.balance.tag) { + row.value = balance + row.reload() } } - - private func refreshBalanceCell() { - guard let account = accountService.account, - let cell = tableView.cellForRow(at: IndexPath(row: WalletRows.balance.rawValue, section: Sections.wallet.rawValue)) else { - return - } - - cell.detailTextLabel?.text = AdamantUtilities.format(balance: account.balance) - } } diff --git a/Adamant/Stories/Account/AccountViewController.xib b/Adamant/Stories/Account/AccountViewController.xib new file mode 100644 index 000000000..0dc66efdf --- /dev/null +++ b/Adamant/Stories/Account/AccountViewController.xib @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Adamant/Stories/Account/Base.lproj/Account.storyboard b/Adamant/Stories/Account/Base.lproj/Account.storyboard deleted file mode 100644 index e4b16c796..000000000 --- a/Adamant/Stories/Account/Base.lproj/Account.storyboard +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Adamant/Stories/Account/TransferViewController.swift b/Adamant/Stories/Account/TransferViewController.swift index 5cbc9370d..d10661728 100644 --- a/Adamant/Stories/Account/TransferViewController.swift +++ b/Adamant/Stories/Account/TransferViewController.swift @@ -13,24 +13,24 @@ import Eureka // MARK: - Localization extension String.adamantLocalized { struct transfer { - static let addressPlaceholder = NSLocalizedString("of the recipient", comment: "Transfer: recipient address placeholder") - static let amountPlaceholder = NSLocalizedString("to send", comment: "Transfer: transfer amount placeholder") + static let addressPlaceholder = NSLocalizedString("TransferScene.Recipient.Placeholder", comment: "Transfer: recipient address placeholder") + static let amountPlaceholder = NSLocalizedString("TransferScene.Amount.Placeholder", comment: "Transfer: transfer amount placeholder") - static let addressValidationError = NSLocalizedString("Please enter a valid recipient address", comment: "Transfer: Address validation error") - static let amountZeroError = NSLocalizedString("You should send more money", comment: "Transfer: Amount is zero, or even negative notification") - static let amountTooHigh = NSLocalizedString("You don't have that much money", comment: "Transfer: Amount is hiegher that user's total money notification") - static let accountNotFound = NSLocalizedString("Address not found", comment: "Transfer: Address not found error") + static let addressValidationError = NSLocalizedString("TransferScene.Error.InvalidAddress", comment: "Transfer: Address validation error") + static let amountZeroError = NSLocalizedString("TransferScene.Error.TooLittleMoney", comment: "Transfer: Amount is zero, or even negative notification") + static let amountTooHigh = NSLocalizedString("TransferScene.Error.NotEnoughtMoney", comment: "Transfer: Amount is hiegher that user's total money notification") + static let accountNotFound = NSLocalizedString("TransferScene.Error.AddressNotFound", comment: "Transfer: Address not found error") - static let transferProcessingMessage = NSLocalizedString("Sending funds...", comment: "Transfer: Processing message") - static let transferSuccess = NSLocalizedString("Funds sended!", comment: "Transfer: Tokens transfered successfully message") + static let transferProcessingMessage = NSLocalizedString("TransferScene.SendingFundsProgress", comment: "Transfer: Processing message") + static let transferSuccess = NSLocalizedString("TransferScene.TransferSuccessMessage", comment: "Transfer: Tokens transfered successfully message") private init() { } } } fileprivate extension String.adamantLocalized.alert { - static let confirmSendMessageFormat = NSLocalizedString("Send %1$@ to %2$@?", comment: "Transfer: Confirm transfer X tokens to Y 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'.") - static let send = NSLocalizedString("Send", comment: "Transfer: Confirm transfer alert: Send tokens button") + static let confirmSendMessageFormat = NSLocalizedString("TransferScene.SendConfirmFormat", comment: "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'.") + static let send = NSLocalizedString("TransferScene.Send", comment: "Transfer: Confirm transfer alert: Send tokens button") } @@ -62,13 +62,13 @@ class TransferViewController: FormViewController { var localized: String { switch self { - case .balance: return NSLocalizedString("Balance", comment: "Transfer: logged user balance.") - case .amount: return NSLocalizedString("Amount", comment: "Transfer: amount of adamant to transfer.") - case .maxToTransfer: return NSLocalizedString("Max to transfer", comment: "Transfer: maximum amount to transfer: available account money substracting transfer fee.") - case .address: return NSLocalizedString("Address", comment: "Transfer: recipient address") - case .fee: return NSLocalizedString("Transaction fee", comment: "Transfer: transfer fee") - case .total: return NSLocalizedString("Total", comment: "Transfer: total amount of transaction: money to transfer adding fee") - case .sendButton: return NSLocalizedString("Send Funds", comment: "Transfer: Send button") + case .balance: return NSLocalizedString("TransferScene.Row.Balance", comment: "Transfer: logged user balance.") + case .amount: return NSLocalizedString("TransferScene.Row.Amount", comment: "Transfer: amount of adamant to transfer.") + case .maxToTransfer: return NSLocalizedString("TransferScene.Row.MaxToTransfer", comment: "Transfer: maximum amount to transfer: available account money substracting transfer fee.") + case .address: return NSLocalizedString("TransferScene.Row.Recipient", comment: "Transfer: recipient address") + case .fee: return NSLocalizedString("TransferScene.Row.TransactionFee", comment: "Transfer: transfer fee") + case .total: return NSLocalizedString("TransferScene.Row.Total", comment: "Transfer: total amount of transaction: money to transfer adding fee") + case .sendButton: return NSLocalizedString("TransferScene.Row.Send", comment: "Transfer: Send button") } } } @@ -79,8 +79,8 @@ class TransferViewController: FormViewController { var localized: String { switch self { - case .wallet: return NSLocalizedString("Your wallet", comment: "Transfer: 'Your wallet' section") - case .transferInfo: return NSLocalizedString("Transfer Info", comment: "Transfer: 'Transfer info' section") + case .wallet: return NSLocalizedString("TransferScene.Section.YourWallet", comment: "Transfer: 'Your wallet' section") + case .transferInfo: return NSLocalizedString("TransferScene.Section.TransferInfo", comment: "Transfer: 'Transfer info' section") } } } diff --git a/Adamant/Stories/Account/ru.lproj/Account.strings b/Adamant/Stories/Account/ru.lproj/Account.strings deleted file mode 100644 index 6c30b60b6..000000000 --- a/Adamant/Stories/Account/ru.lproj/Account.strings +++ /dev/null @@ -1,18 +0,0 @@ -/* Class = "UINavigationItem"; title = "Transactions"; ObjectID = "63z-aT-Hem"; */ -"63z-aT-Hem.title" = "Транзакции"; - -/* Class = "UIBarButtonItem"; title = "Send"; ObjectID = "EEV-td-OPY"; */ -"EEV-td-OPY.title" = "Отправить"; - -/* Class = "UINavigationItem"; title = "Transaction"; ObjectID = "FG4-t0-tQk"; */ -"FG4-t0-tQk.title" = "Транзакция"; - -/* Class = "UITabBarItem"; title = "Wallet"; ObjectID = "OLR-zA-lhu"; */ -"OLR-zA-lhu.title" = "Кошелёк"; - -/* Class = "UINavigationItem"; title = "Account"; ObjectID = "dte-Ko-zLE"; */ -"dte-Ko-zLE.title" = "Аккаунт"; - -/* Class = "UINavigationItem"; title = "Send Tokens"; ObjectID = "qwn-Er-hr0"; */ -"qwn-Er-hr0.title" = "Отправить токены"; - diff --git a/Adamant/Stories/Chats/Base.lproj/Chats.storyboard b/Adamant/Stories/Chats/Base.lproj/Chats.storyboard deleted file mode 100644 index f2f134f5d..000000000 --- a/Adamant/Stories/Chats/Base.lproj/Chats.storyboard +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Adamant/Stories/Chats/ChatsListViewController.swift b/Adamant/Stories/Chats/ChatListViewController.swift similarity index 69% rename from Adamant/Stories/Chats/ChatsListViewController.swift rename to Adamant/Stories/Chats/ChatListViewController.swift index bbe97f47a..388af0bc8 100644 --- a/Adamant/Stories/Chats/ChatsListViewController.swift +++ b/Adamant/Stories/Chats/ChatListViewController.swift @@ -1,5 +1,5 @@ // -// ChatsListViewController.swift +// ChatListViewController.swift // Adamant // // Created by Anokhov Pavel on 12.01.2018. @@ -9,11 +9,21 @@ import UIKit import CoreData -class ChatsListViewController: UIViewController { +extension String.adamantLocalized { + struct chatList { + static let title = NSLocalizedString("ChatListPage.Title", comment: "ChatList: scene title") + + private init() {} + } +} + +class ChatListViewController: UIViewController { + let cellIdentifier = "cell" + let cellHeight: CGFloat = 74.0 + // MARK: Dependencies var accountService: AccountService! var chatsProvider: ChatsProvider! - var cellFactory: CellFactory! var router: Router! // MARK: IBOutlet @@ -21,44 +31,33 @@ class ChatsListViewController: UIViewController { @IBOutlet weak var newChatButton: UIBarButtonItem! // MARK: Properties - let showChatSegue = "showChat" - let newChatSegue = "newChat" var chatsController: NSFetchedResultsController? var unreadController: NSFetchedResultsController? - let chatCell = SharedCell.ChatCell.cellIdentifier private var preservedMessagess = [String:String]() // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() + navigationItem.title = String.adamantLocalized.chatList.title + navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(newChat)) // MARK: TableView tableView.dataSource = self tableView.delegate = self - tableView.register(cellFactory.nib(for: SharedCell.ChatCell), forCellReuseIdentifier: chatCell) + tableView.register(UINib(nibName: "ChatTableViewCell", bundle: nil), forCellReuseIdentifier: cellIdentifier) - chatsController = chatsProvider.getChatroomsController() - chatsController?.delegate = self - unreadController = chatsProvider.getUnreadMessagesController() - unreadController?.delegate = self - - tableView.reloadData() + if self.accountService.account != nil { + initFetchedRequestControllers(provider: chatsProvider) + } // MARK: Login/Logout NotificationCenter.default.addObserver(forName: .adamantUserLoggedIn, object: nil, queue: OperationQueue.main) { [weak self] _ in - guard let controller = self?.chatsProvider.getChatroomsController() else { - return - } - - controller.delegate = self - self?.chatsController = controller - self?.tableView.reloadData() + self?.initFetchedRequestControllers(provider: self?.chatsProvider) } NotificationCenter.default.addObserver(forName: .adamantUserLoggedOut, object: nil, queue: OperationQueue.main) { [weak self] _ in - self?.chatsController = nil - self?.tableView.reloadData() + self?.initFetchedRequestControllers(provider: nil) } } @@ -73,29 +72,22 @@ class ChatsListViewController: UIViewController { } } - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - guard let identifier = segue.identifier else { - return - } + + // MARK: IB Actions + @IBAction func newChat(sender: Any) { + let controller = router.get(scene: AdamantScene.Chats.newChat) - switch identifier { - case showChatSegue: - if let chatroom = sender as? Chatroom, let vc = segue.destination as? ChatViewController { - prepareChatViewController(vc, chatroom: chatroom) - } - - case newChatSegue: - if let vc = segue.destination as? NewChatViewController { - vc.delegate = self - } else if let nav = segue.destination as? UINavigationController, let vc = nav.viewControllers.first as? NewChatViewController { - vc.delegate = self - } - - default: - return + if let c = controller as? NewChatViewController { + c.delegate = self + } else if let nav = controller as? UINavigationController, let c = nav.viewControllers.last as? NewChatViewController { + c.delegate = self } + + present(controller, animated: true, completion: nil) } + + // MARK: Helpers private func prepareChatViewController(_ vc: ChatViewController, chatroom: Chatroom) { if let account = accountService.account { vc.account = account @@ -105,11 +97,40 @@ class ChatsListViewController: UIViewController { vc.chatroom = chatroom vc.delegate = self } + + + /// - Parameter provider: nil to drop controllers and reset table + private func initFetchedRequestControllers(provider: ChatsProvider?) { + guard let provider = provider else { + chatsController = nil + unreadController = nil + tableView.reloadData() + return + } + + chatsController = provider.getChatroomsController() + unreadController = provider.getUnreadMessagesController() + + chatsController?.delegate = self + unreadController?.delegate = self + + do { + try chatsController?.performFetch() + try unreadController?.performFetch() + } catch { + chatsController = nil + unreadController = nil + print("There was an error performing fetch: \(error)") + } + + tableView.reloadData() + setBadgeValue(unreadController?.fetchedObjects?.count) + } } // MARK: - UITableView -extension ChatsListViewController: UITableViewDelegate, UITableViewDataSource { +extension ChatListViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if let f = chatsController?.fetchedObjects { return f.count @@ -119,7 +140,7 @@ extension ChatsListViewController: UITableViewDelegate, UITableViewDataSource { } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return SharedCell.ChatCell.defaultRowHeight + return cellHeight } func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { @@ -127,17 +148,23 @@ extension ChatsListViewController: UITableViewDelegate, UITableViewDataSource { } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if let chatroom = chatsController?.object(at: indexPath) { - performSegue(withIdentifier: showChatSegue, sender: chatroom) + if let chatroom = chatsController?.object(at: indexPath), let c = router.get(scene: AdamantScene.Chats.chat) as? ChatViewController { + prepareChatViewController(c, chatroom: chatroom) + + if let nav = navigationController { + nav.pushViewController(c, animated: true) + } else { + present(c, animated: true) + } } } } // MARK: - UITableView Cells -extension ChatsListViewController { +extension ChatListViewController { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: chatCell, for: indexPath) as! ChatTableViewCell + let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ChatTableViewCell cell.accessoryType = .disclosureIndicator cell.accountLabel.textColor = UIColor.adamantPrimary @@ -186,7 +213,7 @@ extension ChatsListViewController { // MARK: - NSFetchedResultsControllerDelegate -extension ChatsListViewController: NSFetchedResultsControllerDelegate { +extension ChatListViewController: NSFetchedResultsControllerDelegate { func controllerWillChangeContent(_ controller: NSFetchedResultsController) { if controller == chatsController { tableView.beginUpdates() @@ -194,24 +221,12 @@ extension ChatsListViewController: NSFetchedResultsControllerDelegate { } func controllerDidChangeContent(_ controller: NSFetchedResultsController) { - switch controller { case let c where c == chatsController: tableView.endUpdates() case let c where c == unreadController: - let item: UITabBarItem - if let i = navigationController?.tabBarItem { - item = i - } else { - item = tabBarItem - } - - if let count = controller.fetchedObjects?.count, count > 0 { - item.badgeValue = String(count) - } else { - item.badgeValue = nil - } + setBadgeValue(controller.fetchedObjects?.count) default: break @@ -255,12 +270,12 @@ extension ChatsListViewController: NSFetchedResultsControllerDelegate { // MARK: - NewChatViewControllerDelegate -extension ChatsListViewController: NewChatViewControllerDelegate { +extension ChatListViewController: NewChatViewControllerDelegate { func newChatController(_ controller: NewChatViewController, didSelectAccount account: CoreDataAccount) { let chatroom = self.chatsProvider.chatroomWith(account) DispatchQueue.main.async { - if let vc = self.router.get(scene: .Chat) as? ChatViewController { + if let vc = self.router.get(scene: AdamantScene.Chats.chat) as? ChatViewController { self.prepareChatViewController(vc, chatroom: chatroom) self.navigationController?.pushViewController(vc, animated: false) @@ -292,7 +307,7 @@ extension ChatsListViewController: NewChatViewControllerDelegate { // MARK: - ChatViewControllerDelegate -extension ChatsListViewController: ChatViewControllerDelegate { +extension ChatListViewController: ChatViewControllerDelegate { func preserveMessage(_ message: String, forAddress address: String) { preservedMessagess[address] = message } @@ -311,8 +326,25 @@ extension ChatsListViewController: ChatViewControllerDelegate { } -// MARK: - Current chat -extension ChatsListViewController { +// MARK: - Tools +extension ChatListViewController { + /// TabBar item badge + func setBadgeValue(_ value: Int?) { + let item: UITabBarItem + if let i = navigationController?.tabBarItem { + item = i + } else { + item = tabBarItem + } + + if let value = value, value > 0 { + item.badgeValue = String(value) + } else { + item.badgeValue = nil + } + } + + /// Current chat func presentedChatroom() -> Chatroom? { // Showing another page guard tabBarController?.selectedViewController == self else { diff --git a/Adamant/Stories/Chats/ChatListViewController.xib b/Adamant/Stories/Chats/ChatListViewController.xib new file mode 100644 index 000000000..ff82632dd --- /dev/null +++ b/Adamant/Stories/Chats/ChatListViewController.xib @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Adamant/SharedCells/ChatTableViewCell.swift b/Adamant/Stories/Chats/ChatTableViewCell.swift similarity index 89% rename from Adamant/SharedCells/ChatTableViewCell.swift rename to Adamant/Stories/Chats/ChatTableViewCell.swift index fda2d705a..2d0c3cfd4 100644 --- a/Adamant/SharedCells/ChatTableViewCell.swift +++ b/Adamant/Stories/Chats/ChatTableViewCell.swift @@ -9,12 +9,6 @@ import UIKit import FreakingSimpleRoundImageView -extension SharedCell { - static let ChatCell = SharedCell(cellIdentifier: "chatCell", - xibName: "ChatTableViewCell", - rowHeight: 74) -} - class ChatTableViewCell: UITableViewCell { // MARK: - IBOutlets @IBOutlet weak var avatarImageView: RoundImageView! diff --git a/Adamant/SharedCells/ChatTableViewCell.xib b/Adamant/Stories/Chats/ChatTableViewCell.xib similarity index 100% rename from Adamant/SharedCells/ChatTableViewCell.xib rename to Adamant/Stories/Chats/ChatTableViewCell.xib diff --git a/Adamant/Stories/Chats/ChatViewController.swift b/Adamant/Stories/Chats/ChatViewController.swift index 9083e91a9..679f5f90a 100644 --- a/Adamant/Stories/Chats/ChatViewController.swift +++ b/Adamant/Stories/Chats/ChatViewController.swift @@ -13,16 +13,15 @@ import CoreData // MARK: - Localization extension String.adamantLocalized { struct chat { - static let sendButton = NSLocalizedString("Send", comment: "Chat: Send message button") - static let messageInputPlaceholder = NSLocalizedString("New message", comment: "Chat: message input placeholder") - static let estimatedFeeFormat = NSLocalizedString("~%@", comment: "Chat: input bar: Estimated fee") + static let sendButton = NSLocalizedString("ChatScene.Send", comment: "Chat: Send message button") + static let messageInputPlaceholder = NSLocalizedString("ChatScene.NewMessage.Placeholder", comment: "Chat: message input placeholder") - static let messageIsEmpty = NSLocalizedString("Message is empty", comment: "Chat: Notify user that message cannot be empty") - static let messageTooLong = NSLocalizedString("Message is too long", comment: "Chat: Message is too long") - static let notEnoughMoney = NSLocalizedString("You don't have enough money to send a message", comment: "Chat: Notify user that he doesn't have money to pay a message fee") + static let messageIsEmpty = NSLocalizedString("ChatScene.Error.MessageIsEmpty", comment: "Chat: Notify user that message cannot be empty") + static let messageTooLong = NSLocalizedString("ChatScene.Error.MessageTooLong", comment: "Chat: Message is too long") + static let notEnoughMoney = NSLocalizedString("ChatScene.Error.NotEnoughMoney", comment: "Chat: Notify user that he doesn't have money to pay a message fee") - static let internalErrorFormat = NSLocalizedString("Internal error: %@. You should report this bug.", comment: "Chat: Notify user about bad internal error. Usually this should be reported as a bug.") - static let serverErrorFormat = NSLocalizedString("Remote server error: %@", comment: "Chat: Notify user about server error.") + static let internalErrorFormat = NSLocalizedString("ChatScene.Error.InternalErrorFormat", comment: "Chat: Notify user about bad internal error. Usually this should be reported as a bug. Using %@ for error description") + static let serverErrorFormat = NSLocalizedString("ChatScene.Error.RemoteServerErrorFormat", comment: "Chat: Notify user about server error. Using %@ for error description") private init() { } } @@ -66,6 +65,7 @@ class ChatViewController: MessagesViewController { // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "•••", style: .plain, target: self, action: #selector(properties)) // MARK: 1. Initial configuration @@ -89,6 +89,12 @@ class ChatViewController: MessagesViewController { controller.delegate = self self?.chatController = controller + + do { + try controller.performFetch() + } catch { + print("There was an error performing fetch: \(error)") + } if let collection = self?.messagesCollectionView { DispatchQueue.main.async { @@ -193,7 +199,7 @@ extension ChatViewController { return } - let text = String.localizedStringWithFormat(String.adamantLocalized.chat.estimatedFeeFormat, AdamantUtilities.format(balance: fee)) + let text = "~\(AdamantUtilities.format(balance: fee))" prevFee = fee feeLabel.title = text diff --git a/Adamant/Stories/Chats/ChatsDependencies.swift b/Adamant/Stories/Chats/ChatsDependencies.swift deleted file mode 100644 index 88cbb367c..000000000 --- a/Adamant/Stories/Chats/ChatsDependencies.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// ChatsDependencies.swift -// Adamant -// -// Created by Anokhov Pavel on 12.01.2018. -// Copyright © 2018 Adamant. All rights reserved. -// - -import Swinject - -extension Container { - func registerAdamantChatsStory() { - self.storyboardInitCompleted(ChatsListViewController.self) { r, c in - c.accountService = r.resolve(AccountService.self) - c.chatsProvider = r.resolve(ChatsProvider.self) - c.cellFactory = r.resolve(CellFactory.self) - c.router = r.resolve(Router.self) - } - - self.storyboardInitCompleted(ChatViewController.self) { r, c in - c.chatsProvider = r.resolve(ChatsProvider.self) - c.dialogService = r.resolve(DialogService.self) - } - - self.storyboardInitCompleted(NewChatViewController.self) { r, c in - c.dialogService = r.resolve(DialogService.self) - c.accountService = r.resolve(AccountService.self) - c.accountsProvider = r.resolve(AccountsProvider.self) - } - } -} diff --git a/Adamant/Stories/Chats/ChatsRoutes.swift b/Adamant/Stories/Chats/ChatsRoutes.swift index f896bf0d9..88b3b2f65 100644 --- a/Adamant/Stories/Chats/ChatsRoutes.swift +++ b/Adamant/Stories/Chats/ChatsRoutes.swift @@ -6,14 +6,36 @@ // Copyright © 2018 Adamant. All rights reserved. // -import Foundation - -extension AdamantStory { - static let Chats = AdamantStory("Chats") -} +import UIKit extension AdamantScene { - static let ChatsList = AdamantScene(story: .Chats, identifier: "ChatsListViewController") - static let Chat = AdamantScene(story: .Chats, identifier: "ChatViewController") - static let NewChat = AdamantScene(story: .Chats, identifier: "NewChatViewController") + struct Chats { + static let chatList = AdamantScene(identifier: "ChatListViewController", factory: { r in + let c = ChatListViewController(nibName: "ChatListViewController", bundle: nil) + c.accountService = r.resolve(AccountService.self) + c.chatsProvider = r.resolve(ChatsProvider.self) + c.router = r.resolve(Router.self) + return c + }) + + static let chat = AdamantScene(identifier: "ChatViewController", factory: { r in + let c = ChatViewController() + c.chatsProvider = r.resolve(ChatsProvider.self) + c.dialogService = r.resolve(DialogService.self) + return c + }) + + static let newChat = AdamantScene(identifier: "NewChatViewController", factory: { r in + let c = NewChatViewController() + c.dialogService = r.resolve(DialogService.self) + c.accountService = r.resolve(AccountService.self) + c.accountsProvider = r.resolve(AccountsProvider.self) + + let navigator = UINavigationController(rootViewController: c) + + return navigator + }) + + private init() {} + } } diff --git a/Adamant/Stories/Chats/NewChatViewController.swift b/Adamant/Stories/Chats/NewChatViewController.swift index 912f2d5ed..1fcefa80c 100644 --- a/Adamant/Stories/Chats/NewChatViewController.swift +++ b/Adamant/Stories/Chats/NewChatViewController.swift @@ -14,15 +14,17 @@ import AVFoundation // MARK: - Localization extension String.adamantLocalized { struct newChat { - static let addressPlaceholder = NSLocalizedString("", comment: "New chat: Recipient address placeholder. Note that address text field always shows U letter, so you can left this line blank.") - static let scanQrButton = NSLocalizedString("Scan QR", comment: "New chat: Scan QR with address button") + static let title = NSLocalizedString("NewChatScene.Title", comment: "New chat: scene title") - static let specifyValidAddressMessage = NSLocalizedString("Please specify valid recipient address", comment: "New chat: Notify user that he did enter invalid address") - static let loggedUserAddressMessage = NSLocalizedString("You don't need an encrypted anonymous chat to talk to yourself", comment: "New chat: Notify user that he can't start chat with himself") + static let addressPlaceholder = NSLocalizedString("NewChatScene.Address.Placeholder", comment: "New chat: Recipient address placeholder. Note that address text field always shows U letter, so you can left this line blank.") + static let scanQrButton = NSLocalizedString("NewChatScene.ScanQr", comment: "New chat: Scan QR with address button") - static let wrongQrError = NSLocalizedString("QR code does not contains a valid adamant address", comment: "New Chat: Notify user that scanned QR doesn't contains an address") - static let addressNotFoundFormat = NSLocalizedString("Address %@ not found", comment: "New chat: Notify user that specified address (%@) not found") - static let serverErrorFormat = NSLocalizedString("%@", comment: "New chat: Remote server returned an error.") + static let specifyValidAddressMessage = NSLocalizedString("NewChatScene.Error.InvalidAddress", comment: "New chat: Notify user that he did enter invalid address") + static let loggedUserAddressMessage = NSLocalizedString("NewChatScene.Error.OwnAddress", comment: "New chat: Notify user that he can't start chat with himself") + + static let wrongQrError = NSLocalizedString("NewChatScene.Error.WrongQr", comment: "New Chat: Notify user that scanned QR doesn't contains an address") + static let addressNotFoundFormat = NSLocalizedString("NewChatScene.Error.AddressNotFoundFormat", comment: "New chat: Notify user that specified address (%@) not found. Using %@ for address") + static let serverErrorFormat = NSLocalizedString("NewChatScene.Error.RemoteServerFormat", comment: "New chat: Remote server returned an error. Using %@ for error description") private init() { } } @@ -80,6 +82,9 @@ class NewChatViewController: FormViewController { override func viewDidLoad() { super.viewDidLoad() + navigationItem.title = String.adamantLocalized.newChat.title + navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done)) + navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancel)) navigationOptions = .Disabled diff --git a/Adamant/Stories/Chats/ru.lproj/Chats.strings b/Adamant/Stories/Chats/ru.lproj/Chats.strings deleted file mode 100644 index 2e64e06a6..000000000 --- a/Adamant/Stories/Chats/ru.lproj/Chats.strings +++ /dev/null @@ -1,12 +0,0 @@ -/* Class = "UITabBarItem"; title = "Chats"; ObjectID = "Kcp-hK-Mxk"; */ -"Kcp-hK-Mxk.title" = "Чаты"; - -/* Class = "UINavigationItem"; title = "Chats"; ObjectID = "NNn-vk-aSk"; */ -"NNn-vk-aSk.title" = "Чаты"; - -/* Class = "UINavigationItem"; title = "New Chat"; ObjectID = "R9S-0N-t3f"; */ -"R9S-0N-t3f.title" = "Новый чат"; - -/* Class = "UIBarButtonItem"; title = "•••"; ObjectID = "v2Z-hT-nNQ"; */ -"v2Z-hT-nNQ.title" = "•••"; - diff --git a/Adamant/Stories/Login/Base.lproj/Login.storyboard b/Adamant/Stories/Login/Base.lproj/Login.storyboard deleted file mode 100644 index 7a8b3862e..000000000 --- a/Adamant/Stories/Login/Base.lproj/Login.storyboard +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Adamant/Stories/Login/LoginDependencies.swift b/Adamant/Stories/Login/LoginDependencies.swift deleted file mode 100644 index e622b9b4d..000000000 --- a/Adamant/Stories/Login/LoginDependencies.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// LoginDependencies.swift -// Adamant -// -// Created by Anokhov Pavel on 07.01.2018. -// Copyright © 2018 Adamant. All rights reserved. -// - -import Swinject - -extension Container { - func registerAdamantLoginStory() { - self.storyboardInitCompleted(LoginViewController.self) { r, c in - c.accountService = r.resolve(AccountService.self) - c.adamantCore = r.resolve(AdamantCore.self) - c.dialogService = r.resolve(DialogService.self) - c.localAuth = r.resolve(LocalAuthentication.self) - } - } -} diff --git a/Adamant/Stories/Login/LoginRoutes.swift b/Adamant/Stories/Login/LoginRoutes.swift index 2824be56a..979374a4e 100644 --- a/Adamant/Stories/Login/LoginRoutes.swift +++ b/Adamant/Stories/Login/LoginRoutes.swift @@ -8,10 +8,17 @@ import Foundation -extension AdamantStory { - static let Login = AdamantStory("Login") -} - extension AdamantScene { - static let Login = AdamantScene(story: .Login, identifier: "LoginViewController") + struct Login { + static let login = AdamantScene(identifier: "LoginViewController", factory: { r in + let c = LoginViewController() + c.accountService = r.resolve(AccountService.self) + c.adamantCore = r.resolve(AdamantCore.self) + c.dialogService = r.resolve(DialogService.self) + c.localAuth = r.resolve(LocalAuthentication.self) + return c + }) + + private init() {} + } } diff --git a/Adamant/Stories/Login/LoginViewController.swift b/Adamant/Stories/Login/LoginViewController.swift index 484df670d..8d07f146c 100644 --- a/Adamant/Stories/Login/LoginViewController.swift +++ b/Adamant/Stories/Login/LoginViewController.swift @@ -13,17 +13,17 @@ import MyLittlePinpad // MARK: - Localization extension String.adamantLocalized { struct login { - static let loggingInProgressMessage = NSLocalizedString("Logging in", comment: "Login: notify user that we a logging in") + static let loggingInProgressMessage = NSLocalizedString("LoginScene.LoggingInProgress", comment: "Login: notify user that we are trying to log in") - static let loginIntoPrevAccount = NSLocalizedString("Login into Adamant", comment: "Login: Login into previous account with biometry or pincode") + static let loginIntoPrevAccount = NSLocalizedString("LoginScene.LoginIntoAdamant", comment: "Login: Login into previous account with biometry or pincode") - static let wrongQrError = NSLocalizedString("QR code does not contains a valid passphrase", comment: "Login: Notify user that scanned QR doesn't contains a passphrase.") - static let noNetworkError = NSLocalizedString("No connection with The Internet", comment: "Login: No network error.") + static let wrongQrError = NSLocalizedString("LoginScene.Error.WrongQr", comment: "Login: Notify user that scanned QR doesn't contains a passphrase.") + static let noNetworkError = NSLocalizedString("LoginScene.Error.NoInternet", comment: "Login: No network error.") - static let cameraNotAuthorized = NSLocalizedString("You need to authorize Adamant to use device's Camera", comment: "Login: Notify user, that he disabled camera in settings, and need to authorize application.") - static let cameraNotSupported = NSLocalizedString("QR codes reading not supported by the current device", comment: "Login: Notify user that device not supported by QR reader") + static let cameraNotAuthorized = NSLocalizedString("LoginScene.Error.AuthorizeCamera", comment: "Login: Notify user, that he disabled camera in settings, and need to authorize application.") + static let cameraNotSupported = NSLocalizedString("LoginScene.Error.QrNotSupported", comment: "Login: Notify user that device not supported by QR reader") - static let emptyPassphraseAlert = NSLocalizedString("Enter a passphrase!", comment: "Login: notify user that he is trying to login without a passphrase") + static let emptyPassphraseAlert = NSLocalizedString("LoginScene.Error.NoPassphrase", comment: "Login: notify user that he is trying to login without a passphrase") private init() {} } @@ -42,10 +42,10 @@ class LoginViewController: FormViewController { var localized: String { switch self { case .login: - return NSLocalizedString("Login", comment: "Login: login with existing passphrase section") + return NSLocalizedString("LoginScene.Section.Login", comment: "Login: login with existing passphrase section") case .newAccount: - return NSLocalizedString("New account", comment: "Login: Create new account section") + return NSLocalizedString("LoginScene.Section.NewAccount", comment: "Login: Create new account section") } } @@ -70,25 +70,25 @@ class LoginViewController: FormViewController { var localized: String { switch self { case .passphrase: - return NSLocalizedString("Passphrase", comment: "Login: Passphrase placeholder") + return NSLocalizedString("LoginScene.Row.Passphrase.Placeholder", comment: "Login: Passphrase placeholder") case .loginButton: - return NSLocalizedString("Login", comment: "Login: Login button") + return NSLocalizedString("LoginScene.Row.Login", comment: "Login: Login button") case .loginWithQr: - return NSLocalizedString("Login with QR", comment: "Login: Login with QR button.") + return NSLocalizedString("LoginScene.Row.Qr", comment: "Login: Login with QR button.") case .loginWithPin: - return NSLocalizedString("Login with Pincode", comment: "Login: Login with pincode button") + return NSLocalizedString("LoginScene.Row.Pincode", comment: "Login: Login with pincode button") case .saveYourPassphraseAlert: - return NSLocalizedString("Save the passphrase for new Wallet and Messenger account. There is no login to enter Wallet, only the passphrase needed. If lost, no way to recover it", comment: "Login: security alert, notify user that he must save his new passphrase") + return NSLocalizedString("LoginScene.Row.SavePassphraseAlert", comment: "Login: security alert, notify user that he must save his new passphrase") case .generateNewPassphraseButton: - return NSLocalizedString("Generate new passphrase", comment: "Login: generate new passphrase button") + return NSLocalizedString("LoginScene.Row.Generate", comment: "Login: generate new passphrase button") case .tapToSaveHint: - return NSLocalizedString("Tap to save", comment: "Login: a small hint for a user, that he can tap on passphrase to save it") + return NSLocalizedString("LoginScene.Row.TapToSave", comment: "Login: a small hint for a user, that he can tap on passphrase to save it") case .newPassphrase: return "" diff --git a/Adamant/Stories/Login/ru.lproj/Login.strings b/Adamant/Stories/Login/ru.lproj/Login.strings deleted file mode 100644 index 874e8a453..000000000 --- a/Adamant/Stories/Login/ru.lproj/Login.strings +++ /dev/null @@ -1 +0,0 @@ -/* No Localized Strings */ diff --git a/Adamant/Stories/Settings/Base.lproj/Settings.storyboard b/Adamant/Stories/Settings/Base.lproj/Settings.storyboard deleted file mode 100644 index b9631eff8..000000000 --- a/Adamant/Stories/Settings/Base.lproj/Settings.storyboard +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Adamant/Stories/Settings/QRGeneratorViewController.swift b/Adamant/Stories/Settings/QRGeneratorViewController.swift index f0a2388cd..88e8d54ba 100644 --- a/Adamant/Stories/Settings/QRGeneratorViewController.swift +++ b/Adamant/Stories/Settings/QRGeneratorViewController.swift @@ -14,11 +14,13 @@ import Photos // MARK: - Localization extension String.adamantLocalized { struct qrGenerator { - static let tapToSaveTip = NSLocalizedString("Tap to save", comment: "QRGenerator: small 'Tap to save' tooltip under generated QR") - static let passphrasePlaceholder = NSLocalizedString("Passphrase", comment: "QRGenerator: Passphrase textview placeholder") + static let title = NSLocalizedString("QrGeneratorScene.Title", comment: "QRGenerator: scene title") - static let wrongPassphraseError = NSLocalizedString("Enter correct passphrase", comment: "QRGenerator: user typed in wrong invalid") - static let internalError = NSLocalizedString("Internal error: %@", comment: "QRGenerator: Bad Internal generator error message format") + static let tapToSaveTip = NSLocalizedString("QrGeneratorScene.TapToSave", comment: "QRGenerator: small 'Tap to save' tooltip under generated QR") + static let passphrasePlaceholder = NSLocalizedString("QrGeneratorScene.Passphrase.Placeholder", comment: "QRGenerator: Passphrase textview placeholder") + + static let wrongPassphraseError = NSLocalizedString("QrGeneratorScene.Error.InvalidPassphrase", comment: "QRGenerator: user typed in wrong invalid") + static let internalError = NSLocalizedString("QrGeneratorScene.Error.InternalErrorFormat", comment: "QRGenerator: Bad Internal generator error message format. Using %@ for error description") private init() {} } @@ -60,6 +62,7 @@ class QRGeneratorViewController: FormViewController { // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() + navigationItem.title = String.adamantLocalized.qrGenerator.title navigationOptions = .Disabled self.tableView.showsVerticalScrollIndicator = false diff --git a/Adamant/Stories/Settings/SettingsDependencies.swift b/Adamant/Stories/Settings/SettingsDependencies.swift deleted file mode 100644 index 9a26090f9..000000000 --- a/Adamant/Stories/Settings/SettingsDependencies.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// SettingsDependencies.swift -// Adamant -// -// Created by Anokhov Pavel on 04.02.2018. -// Copyright © 2018 Adamant. All rights reserved. -// - -import Swinject - -extension Container { - func registerAdamantSettingsStory() { - self.storyboardInitCompleted(SettingsViewController.self) { r, c in - c.dialogService = r.resolve(DialogService.self) - c.accountService = r.resolve(AccountService.self) - c.localAuth = r.resolve(LocalAuthentication.self) - c.notificationsService = r.resolve(NotificationsService.self) - } - - self.storyboardInitCompleted(QRGeneratorViewController.self) { r, c in - c.dialogService = r.resolve(DialogService.self) - } - } -} diff --git a/Adamant/Stories/Settings/SettingsRoutes.swift b/Adamant/Stories/Settings/SettingsRoutes.swift index a63dd939a..7cc5772b4 100644 --- a/Adamant/Stories/Settings/SettingsRoutes.swift +++ b/Adamant/Stories/Settings/SettingsRoutes.swift @@ -6,13 +6,26 @@ // Copyright © 2018 Adamant. All rights reserved. // -import Foundation - -extension AdamantStory { - static let Settings = AdamantStory("Settings") -} +import UIKit extension AdamantScene { - static let SettingsPage = AdamantScene(story: .Settings, identifier: "SettingsTableViewController") - static let QRGenerator = AdamantScene(story: .Settings, identifier: "QRGeneratorViewController") + struct Settings { + static let settings = AdamantScene(identifier: "SettingsTableViewController", factory: { r in + let c = SettingsViewController() + c.dialogService = r.resolve(DialogService.self) + c.accountService = r.resolve(AccountService.self) + c.localAuth = r.resolve(LocalAuthentication.self) + c.notificationsService = r.resolve(NotificationsService.self) + c.router = r.resolve(Router.self) + return c + }) + + static let qRGenerator = AdamantScene(identifier: "QRGeneratorViewController", factory: { r in + let c = QRGeneratorViewController() + c.dialogService = r.resolve(DialogService.self) + return c + }) + + private init() {} + } } diff --git a/Adamant/Stories/Settings/SettingsViewController+StayIn.swift b/Adamant/Stories/Settings/SettingsViewController+StayIn.swift index e6113e648..d8dc9b7d6 100644 --- a/Adamant/Stories/Settings/SettingsViewController+StayIn.swift +++ b/Adamant/Stories/Settings/SettingsViewController+StayIn.swift @@ -232,10 +232,8 @@ extension SettingsViewController: PinpadViewControllerDelegate { switch pinpadRequest { // MARK: User canceled turning on StayIn - case PinpadRequest.createPin?: - fallthrough - - case PinpadRequest.reenterPin(pin: _)?: + case PinpadRequest.createPin?, + PinpadRequest.reenterPin(pin: _)?: if let row: SwitchRow = form.rowBy(tag: Rows.stayLoggedIn.tag) { row.value = false row.updateCell() diff --git a/Adamant/Stories/Settings/SettingsViewController.swift b/Adamant/Stories/Settings/SettingsViewController.swift index 3844876e0..f96663e66 100644 --- a/Adamant/Stories/Settings/SettingsViewController.swift +++ b/Adamant/Stories/Settings/SettingsViewController.swift @@ -12,16 +12,15 @@ import MyLittlePinpad extension String.adamantLocalized { struct settings { - static let stayInTurnOff = NSLocalizedString("Do not stay logged in", comment: "Config: turn off 'Stay Logged In' confirmation") - static let biometryOnReason = NSLocalizedString("Use biometry to log in", comment: "Config: Authorization reason for turning biometry on") - static let biometryOffReason = NSLocalizedString("Do not use biometry to log in", comment: "Config: Authorization reason for turning biometry off") + static let title = NSLocalizedString("SettingsPage.Title", comment: "Config: scene title") + + static let stayInTurnOff = NSLocalizedString("SettingsPage.DoNotStayLoggedIn", comment: "Config: turn off 'Stay Logged In' confirmation") + static let biometryOnReason = NSLocalizedString("SettingsPage.UseBiometry", comment: "Config: Authorization reason for turning biometry on") + static let biometryOffReason = NSLocalizedString("SettingsPage.DoNotUseBiometry", comment: "Config: Authorization reason for turning biometry off") } } class SettingsViewController: FormViewController { - - private let qrGeneratorSegue = "passphraseToQR" - // MARK: Sections & Rows enum Sections { case settings @@ -31,13 +30,13 @@ class SettingsViewController: FormViewController { var localized: String { switch self { case .settings: - return NSLocalizedString("Settings", comment: "Config: Settings section") + return NSLocalizedString("SettingsPage.Section.Settings", comment: "Config: Settings section") case .applicationInfo: - return NSLocalizedString("Application info", comment: "Config: Application Info section") + return NSLocalizedString("SettingsPage.Section.ApplicationInfo", comment: "Config: Application Info section") case .utilities: - return NSLocalizedString("Utilities", comment: "Config: Utilities section") + return NSLocalizedString("SettingsPage.Section.Utilities", comment: "Config: Utilities section") } } } @@ -52,19 +51,19 @@ class SettingsViewController: FormViewController { var localized: String { switch self { case .version: - return NSLocalizedString("Version", comment: "Config: Version row") + return NSLocalizedString("SettingsPage.Row.Version", comment: "Config: Version row") case .qrPassphraseGenerator: - return NSLocalizedString("Generate QR with passphrase", comment: "Config: Generate QR with passphrase row") + return NSLocalizedString("SettingsPage.Row.GenerateQr", comment: "Config: Generate QR with passphrase row") case .stayLoggedIn: - return NSLocalizedString("Stay Logged in", comment: "Config: Stay logged option") + return NSLocalizedString("SettingsPage.Row.StayLoggedIn", comment: "Config: Stay logged option") case .biometry: return "" case .notifications: - return NSLocalizedString("Notifications", comment: "Config: Show notifications") + return NSLocalizedString("SettingsPage.Row.Notifications", comment: "Config: Show notifications") } } @@ -85,6 +84,7 @@ class SettingsViewController: FormViewController { var dialogService: DialogService! var localAuth: LocalAuthentication! var notificationsService: NotificationsService! + var router: Router! // MARK: Properties @@ -95,6 +95,7 @@ class SettingsViewController: FormViewController { // MARK: Lifetime override func viewDidLoad() { super.viewDidLoad() + self.navigationItem.title = String.adamantLocalized.settings.title navigationOptions = .Disabled showBiometryRow = accountService.hasStayInAccount @@ -174,10 +175,10 @@ class SettingsViewController: FormViewController { }.cellSetup({ (cell, _) in cell.selectionStyle = .gray }).onCellSelection({ [weak self] (_, _) in - guard let segue = self?.qrGeneratorSegue else { + guard let nav = self?.navigationController, let vc = self?.router.get(scene: AdamantScene.Settings.qRGenerator) else { return } - self?.performSegue(withIdentifier: segue, sender: nil) + nav.pushViewController(vc, animated: true) }).cellUpdate({ (cell, _) in if let label = cell.textLabel { label.font = UIFont.adamantPrimary(size: 17) diff --git a/Adamant/Stories/Settings/ru.lproj/Settings.strings b/Adamant/Stories/Settings/ru.lproj/Settings.strings deleted file mode 100644 index dd861a520..000000000 --- a/Adamant/Stories/Settings/ru.lproj/Settings.strings +++ /dev/null @@ -1,9 +0,0 @@ -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "81x-yd-4F2"; */ -"81x-yd-4F2.title" = "Настройки"; - -/* Class = "UITabBarItem"; title = "Settings"; ObjectID = "Igt-Ig-iLK"; */ -"Igt-Ig-iLK.title" = "Настройки"; - -/* Class = "UINavigationItem"; title = "QR Passphrase"; ObjectID = "YBG-RB-W62"; */ -"YBG-RB-W62.title" = "QR-пароль"; - diff --git a/Adamant/Stories/Shared/SharedRoutes.swift b/Adamant/Stories/Shared/SharedRoutes.swift new file mode 100644 index 000000000..92f49d411 --- /dev/null +++ b/Adamant/Stories/Shared/SharedRoutes.swift @@ -0,0 +1,17 @@ +// +// SharedRoutes.swift +// Adamant +// +// Created by Anokhov Pavel on 17.03.2018. +// Copyright © 2018 Adamant. All rights reserved. +// + +import Foundation + +extension AdamantScene { + struct Shared { + static let shareQr = AdamantScene(identifier: "ShareQrViewController", factory: { _ in + return ShareQrViewController(nibName: "ShareQrViewController", bundle: nil) + }) + } +} diff --git a/Adamant/Stories/Account/TransactionDetailsViewController.swift b/Adamant/Stories/Transactions/TransactionDetailsViewController.swift similarity index 79% rename from Adamant/Stories/Account/TransactionDetailsViewController.swift rename to Adamant/Stories/Transactions/TransactionDetailsViewController.swift index 40e7d7bab..d071af1db 100644 --- a/Adamant/Stories/Account/TransactionDetailsViewController.swift +++ b/Adamant/Stories/Transactions/TransactionDetailsViewController.swift @@ -12,8 +12,8 @@ import SafariServices // MARK: - Localization extension String.adamantLocalized.alert { - static let exportUrlButton = NSLocalizedString("URL", comment: "Export transaction: 'Share transaction URL' button") - static let exportSummaryButton = NSLocalizedString("Summary", comment: "Export transaction: 'Share transaction summary' button") + static let exportUrlButton = NSLocalizedString("TransactionDetailsScene.Share.URL", comment: "Export transaction: 'Share transaction URL' button") + static let exportSummaryButton = NSLocalizedString("TransactionDetailsScene.Share.Summary", comment: "Export transaction: 'Share transaction summary' button") } @@ -34,15 +34,15 @@ class TransactionDetailsViewController: UIViewController { var localized: String { switch self { - case .transactionNumber: return NSLocalizedString("Id", comment: "Transaction details: Id row.") - case .from: return NSLocalizedString("From", comment: "Transaction details: sender row.") - case .to: return NSLocalizedString("To", comment: "Transaction details: recipient row.") - case .date: return NSLocalizedString("Date", comment: "Transaction details: date row.") - case .amount: return NSLocalizedString("Amount", comment: "Transaction details: amount row.") - case .fee: return NSLocalizedString("Fee", comment: "Transaction details: fee row.") - case .confirmations: return NSLocalizedString("Confirmations", comment: "Transaction details: confirmations row.") - case .block: return NSLocalizedString("Block", comment: "Transaction details: Block id row.") - case .openInExplorer: return NSLocalizedString("Open in Explorer", comment: "Transaction details: 'Open transaction in explorer' row.") + case .transactionNumber: return NSLocalizedString("TransactionDetailsScene.Row.Id", comment: "Transaction details: Id row.") + case .from: return NSLocalizedString("TransactionDetailsScene.Row.From", comment: "Transaction details: sender row.") + case .to: return NSLocalizedString("TransactionDetailsScene.Row.To", comment: "Transaction details: recipient row.") + case .date: return NSLocalizedString("TransactionDetailsScene.Row.Date", comment: "Transaction details: date row.") + case .amount: return NSLocalizedString("TransactionDetailsScene.Row.Amount", comment: "Transaction details: amount row.") + case .fee: return NSLocalizedString("TransactionDetailsScene.Row.Fee", comment: "Transaction details: fee row.") + case .confirmations: return NSLocalizedString("TransactionDetailsScene.Row.Confirmations", comment: "Transaction details: confirmations row.") + case .block: return NSLocalizedString("TransactionDetailsScene.Row.Block", comment: "Transaction details: Block id row.") + case .openInExplorer: return NSLocalizedString("TransactionDetailsScene.Row.Explorer", comment: "Transaction details: 'Open transaction in explorer' row.") } } } @@ -57,9 +57,9 @@ class TransactionDetailsViewController: UIViewController { // MARK: - IBOutlets @IBOutlet weak var tableView: UITableView! - @IBOutlet weak var shareButton: UIBarButtonItem! override func viewDidLoad() { + navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(share)) tableView.dataSource = self tableView.delegate = self diff --git a/Adamant/Stories/Transactions/TransactionDetailsViewController.xib b/Adamant/Stories/Transactions/TransactionDetailsViewController.xib new file mode 100644 index 000000000..6f359e85e --- /dev/null +++ b/Adamant/Stories/Transactions/TransactionDetailsViewController.xib @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Adamant/SharedCells/TransactionTableViewCell.swift b/Adamant/Stories/Transactions/TransactionTableViewCell.swift similarity index 84% rename from Adamant/SharedCells/TransactionTableViewCell.swift rename to Adamant/Stories/Transactions/TransactionTableViewCell.swift index b5c3eb0ff..1930e6b33 100644 --- a/Adamant/SharedCells/TransactionTableViewCell.swift +++ b/Adamant/Stories/Transactions/TransactionTableViewCell.swift @@ -8,12 +8,6 @@ import UIKit -extension SharedCell { - static let TransactionCell = SharedCell(cellIdentifier: "transactionCell", - xibName: "TransactionTableViewCell", - rowHeight: 90) -} - class TransactionTableViewCell: UITableViewCell { enum TransactionType { diff --git a/Adamant/SharedCells/TransactionTableViewCell.xib b/Adamant/Stories/Transactions/TransactionTableViewCell.xib similarity index 100% rename from Adamant/SharedCells/TransactionTableViewCell.xib rename to Adamant/Stories/Transactions/TransactionTableViewCell.xib diff --git a/Adamant/Stories/Transactions/TransactionsRoutes.swift b/Adamant/Stories/Transactions/TransactionsRoutes.swift new file mode 100644 index 000000000..cadb08796 --- /dev/null +++ b/Adamant/Stories/Transactions/TransactionsRoutes.swift @@ -0,0 +1,27 @@ +// +// TransactionsRoutes.swift +// Adamant +// +// Created by Anokhov Pavel on 17.03.2018. +// Copyright © 2018 Adamant. All rights reserved. +// + +import Foundation + +extension AdamantScene { + struct Transactions { + static let transactions = AdamantScene(identifier: "TransactionsViewController", factory: { r in + let c = TransactionsViewController(nibName: "TransactionsViewController", bundle: nil) + c.accountService = r.resolve(AccountService.self) + c.transfersProvider = r.resolve(TransfersProvider.self) + c.router = r.resolve(Router.self) + return c + }) + + static let transactionDetails = AdamantScene(identifier: "TransactionDetailsViewController", factory: { r in + let c = TransactionDetailsViewController(nibName: "TransactionDetailsViewController", bundle: nil) + c.dialogService = r.resolve(DialogService.self) + return c + }) + } +} diff --git a/Adamant/Stories/Account/TransactionsViewController.swift b/Adamant/Stories/Transactions/TransactionsViewController.swift similarity index 75% rename from Adamant/Stories/Account/TransactionsViewController.swift rename to Adamant/Stories/Transactions/TransactionsViewController.swift index e95474c71..0d5a30adb 100644 --- a/Adamant/Stories/Account/TransactionsViewController.swift +++ b/Adamant/Stories/Transactions/TransactionsViewController.swift @@ -10,13 +10,16 @@ import UIKit import CoreData class TransactionsViewController: UIViewController { + let cellIdentifier = "cell" + let cellHeight: CGFloat = 90.0 + // MARK: - Dependencies - var cellFactory: CellFactory! + var accountService: AccountService! var transfersProvider: TransfersProvider! + var router: Router! // MARK: - Properties var controller: NSFetchedResultsController? - let transactionDetailsSegue = "showTransactionDetails" // MARK: - IBOutlets @@ -28,15 +31,21 @@ class TransactionsViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - controller = transfersProvider.transfersController() - controller?.delegate = self - + if accountService.account != nil { + initFetchedResultController(provider: transfersProvider) + } - tableView.register(cellFactory.nib(for: .TransactionCell), forCellReuseIdentifier: SharedCell.TransactionCell.cellIdentifier) + tableView.register(UINib.init(nibName: "TransactionTableViewCell", bundle: nil), forCellReuseIdentifier: cellIdentifier) tableView.dataSource = self tableView.delegate = self - tableView.reloadData() + NotificationCenter.default.addObserver(forName: Notification.Name.adamantUserLoggedIn, object: nil, queue: nil) { [weak self] notification in + self?.initFetchedResultController(provider: self?.transfersProvider) + } + + NotificationCenter.default.addObserver(forName: Notification.Name.adamantUserLoggedOut, object: nil, queue: nil) { [weak self] _ in + self?.initFetchedResultController(provider: nil) + } } override func viewWillAppear(_ animated: Bool) { @@ -45,13 +54,21 @@ class TransactionsViewController: UIViewController { tableView.deselectRow(at: indexPath, animated: animated) } } - - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - if segue.identifier == transactionDetailsSegue, - let vc = segue.destination as? TransactionDetailsViewController, - let transaction = sender as? TransferTransaction { - vc.transaction = transaction + + + /// - Parameter provider: nil to drop and reset + private func initFetchedResultController(provider: TransfersProvider?) { + controller = transfersProvider.transfersController() + controller?.delegate = self + + do { + try controller?.performFetch() + } catch { + print("There was an error performing fetch: \(error)") + controller = nil } + + tableView.reloadData() } } @@ -101,7 +118,7 @@ extension TransactionsViewController: UITableViewDataSource, UITableViewDelegate } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return SharedCell.TransactionCell.defaultRowHeight + return cellHeight } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -110,11 +127,16 @@ extension TransactionsViewController: UITableViewDataSource, UITableViewDelegate return } - performSegue(withIdentifier: transactionDetailsSegue, sender: transaction) + guard let controller = router.get(scene: AdamantScene.Transactions.transactionDetails) as? TransactionDetailsViewController else { + return + } + + controller.transaction = transaction + navigationController?.pushViewController(controller, animated: true) } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: SharedCell.TransactionCell.cellIdentifier, for: indexPath) as? TransactionTableViewCell, + guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TransactionTableViewCell, let transfer = controller?.object(at: indexPath) else { // TODO: Display & Log error return UITableViewCell(style: .default, reuseIdentifier: "cell") diff --git a/Adamant/Stories/Transactions/TransactionsViewController.xib b/Adamant/Stories/Transactions/TransactionsViewController.xib new file mode 100644 index 000000000..08b854797 --- /dev/null +++ b/Adamant/Stories/Transactions/TransactionsViewController.xib @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Adamant/SwinjectDependencies.swift b/Adamant/SwinjectDependencies.swift index ede87f41c..5f2340cd7 100644 --- a/Adamant/SwinjectDependencies.swift +++ b/Adamant/SwinjectDependencies.swift @@ -7,7 +7,6 @@ // import Swinject -import SwinjectStoryboard // MARK: - Resources private struct AdamantResources { @@ -22,8 +21,6 @@ private struct AdamantResources { // MARK: - Services extension Container { func registerAdamantServices() { - - // MARK: - Standalone services // MARK: AdamantCore self.register(AdamantCore.self) { _ in @@ -35,7 +32,11 @@ extension Container { }.inObjectScope(.container) // MARK: Router - self.register(Router.self) { _ in SwinjectedRouter() }.inObjectScope(.container) + self.register(Router.self) { _ in + let router = SwinjectedRouter() + router.container = self + return router + }.inObjectScope(.container) // MARK: CellFactory self.register(CellFactory.self) { _ in AdamantCellFactory() }.inObjectScope(.container) diff --git a/Podfile b/Podfile index be3ce2d3d..d8882353c 100644 --- a/Podfile +++ b/Podfile @@ -7,11 +7,8 @@ target 'Adamant' do pod 'Alamofire' # Network pod 'KeychainAccess' # Keychain pod 'RNCryptor' # Cryptor + pod 'Swinject' # Swinject DI - # Swinject DI - pod 'Swinject' - pod 'SwinjectStoryboard' - # UI pod 'FreakingSimpleRoundImageView' # Round avatars pod 'FTIndicator' # Notifications and activity indicator diff --git a/Podfile.lock b/Podfile.lock index a42e597cc..ce4dbbc91 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,7 +1,7 @@ PODS: - Alamofire (4.7.0) - EFQRCode (4.2.1) - - Eureka (4.1.0) + - Eureka (4.1.1) - FreakingSimpleRoundImageView (1.0.2) - FTIndicator (1.2.6): - FTIndicator/FTNotificationIndicator (= 1.2.6) @@ -16,8 +16,6 @@ PODS: - QRCodeReader.swift (8.1.1) - RNCryptor (5.0.2) - Swinject (2.2.0) - - SwinjectStoryboard (1.1.2): - - Swinject (~> 2.1) DEPENDENCIES: - Alamofire @@ -31,12 +29,11 @@ DEPENDENCIES: - QRCodeReader.swift - RNCryptor - Swinject - - SwinjectStoryboard SPEC CHECKSUMS: Alamofire: 907e0a98eb68cdb7f9d1f541a563d6ac5dc77b25 EFQRCode: f3fd67049faa07adb575495c05d72a34e407a940 - Eureka: 5770ef95bbbbe52f8abbc0d9bea6ee8282c50209 + Eureka: b88fb930e42c79f8c03c373d0fcdc28c1d6c50ed FreakingSimpleRoundImageView: e87b0ef24c9b95b74b1c2cab5b03832dbabd4873 FTIndicator: d2a7c204341b7b0165b6dbeb5bf6979c6d7d4bb7 KeychainAccess: 94c5540b32eabf7bc32bfb976a268e8ea05fd6da @@ -45,8 +42,7 @@ SPEC CHECKSUMS: QRCodeReader.swift: b164a681887de276d405ff02bce854d82cd6360b RNCryptor: 59b9f92e9fa8a1af74c32dc6b1af5e30ff5e1b64 Swinject: 4ca48bca7de5f398229a26abe1ac902b9f3ee541 - SwinjectStoryboard: 92e75763c3299cdf627bef98a5180dc894864336 -PODFILE CHECKSUM: f56801713dedd8f5b2d1ec95f822fbbce22dc294 +PODFILE CHECKSUM: 870cc099d296984357fb6053beeb8a6ff5acc92c COCOAPODS: 1.4.0