diff --git a/Ring/.swiftlint.yml b/Ring/.swiftlint.yml index 07e9a6570ea605f2952d28a5365273b4a02888ad..fab1ed2ed3e64f3ffc0722ee19d30b6f539f2a6f 100644 --- a/Ring/.swiftlint.yml +++ b/Ring/.swiftlint.yml @@ -1,4 +1,5 @@ disabled_rules: # rule identifiers to exclude from running + - todo opt_in_rules: # some rules are only opt-in - empty_count diff --git a/Ring/Ring.xcodeproj/project.pbxproj b/Ring/Ring.xcodeproj/project.pbxproj index 48fd347894f864b33fc0896babe55def4f5e77ad..21d30157563cf02a681edece78dbd3630389b31d 100644 --- a/Ring/Ring.xcodeproj/project.pbxproj +++ b/Ring/Ring.xcodeproj/project.pbxproj @@ -18,7 +18,7 @@ 0273C2FF1E0C438F00CF00BA /* AccountAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0273C2FE1E0C438F00CF00BA /* AccountAdapterDelegate.swift */; }; 0273C3051E0C68B100CF00BA /* CreateProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0273C3031E0C68B100CF00BA /* CreateProfileViewController.swift */; }; 0273C3061E0C68B100CF00BA /* CreateRingAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0273C3041E0C68B100CF00BA /* CreateRingAccountViewController.swift */; }; - 0273C3081E0C68BF00CF00BA /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0273C3071E0C68BF00CF00BA /* RoundedButton.swift */; }; + 0273C3081E0C68BF00CF00BA /* DesignableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0273C3071E0C68BF00CF00BA /* DesignableButton.swift */; }; 029CE9D71E1D8C860000C8E1 /* ServiceEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 029CE9D61E1D8C860000C8E1 /* ServiceEventTests.swift */; }; 02AED8191DD4C4B100F740BA /* librestbed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 02AED8181DD4C4B100F740BA /* librestbed.a */; }; 02B22DFC1DF755BB000358C9 /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B22DFA1DF755BB000358C9 /* AccountModel.swift */; }; @@ -30,9 +30,8 @@ 02DD80C81E1EAD70009A3510 /* AccountConfigModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DD80C71E1EAD70009A3510 /* AccountConfigModel.swift */; }; 02DD80CA1E1EAF1A009A3510 /* AccountCredentialsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DD80C91E1EAF1A009A3510 /* AccountCredentialsModel.swift */; }; 02DD80CD1E1EB2E4009A3510 /* ConfigKeyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DD80CC1E1EB2E4009A3510 /* ConfigKeyModel.swift */; }; - 02E1A0251DDE4ABA00D75B59 /* BoolStringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043866371D2304A700E06CE2 /* BoolStringExtension.swift */; }; + 02E1A0251DDE4ABA00D75B59 /* String+Bool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043866371D2304A700E06CE2 /* String+Bool.swift */; }; 043866211D218B1100E06CE2 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 043866201D218B1100E06CE2 /* AudioToolbox.framework */; }; - 043866361D22D06500E06CE2 /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043866351D22D06500E06CE2 /* AccountTableViewCell.swift */; }; 043999F71D1C2D9D00E99CD9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043999F61D1C2D9D00E99CD9 /* AppDelegate.swift */; }; 04399A031D1C2D9D00E99CD9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04399A021D1C2D9D00E99CD9 /* Images.xcassets */; }; 04399A111D1C2D9D00E99CD9 /* RingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04399A101D1C2D9D00E99CD9 /* RingTests.swift */; }; @@ -94,6 +93,10 @@ 04399B151D1C341A00E99CD9 /* libyaml-cpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */; }; 1A1E476D1F0E808500EA9A36 /* Reusable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A1E476C1F0E808500EA9A36 /* Reusable.framework */; }; 1A1E476F1F0E894600EA9A36 /* SwiftyBeaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A1E476E1F0E894600EA9A36 /* SwiftyBeaver.framework */; }; + 1A3D28A71F0EB9DB00B524EE /* Bool+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3D28A61F0EB9DB00B524EE /* Bool+String.swift */; }; + 1A3D28A91F0EBF0200B524EE /* UIView+Ring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3D28A81F0EBF0200B524EE /* UIView+Ring.swift */; }; + 1A8306331F0EDAA50099D98C /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8306321F0EDAA50099D98C /* AccountTableViewCell.swift */; }; + 1AABA7461F0FE9C000739605 /* UIColor+Ring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AABA7451F0FE9C000739605 /* UIColor+Ring.swift */; }; 1ABE07BA1F0C16F100D36361 /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABE07B91F0C16F100D36361 /* ContactViewModel.swift */; }; 1ABE07BC1F0C22CC00D36361 /* WalkthroughStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1ABE07BB1F0C22CC00D36361 /* WalkthroughStoryboard.storyboard */; }; 1ABE07D21F0D8FE800D36361 /* Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABE07D01F0D8FE800D36361 /* Images.swift */; }; @@ -113,7 +116,6 @@ 563AEC771EA664C0003A5641 /* RegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 563AEC761EA664C0003A5641 /* RegistrationResponse.m */; }; 564775831EE5CFC500A0C855 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 564775811EE5CFC500A0C855 /* Realm.framework */; }; 564775841EE5CFC500A0C855 /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 564775821EE5CFC500A0C855 /* RealmSwift.framework */; }; - 564C44591E8D7F8F000F92B1 /* LocalizedStringTableNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C44581E8D7F8F000F92B1 /* LocalizedStringTableNames.swift */; }; 564C445B1E8EA44E000F92B1 /* Durations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C445A1E8EA44E000F92B1 /* Durations.swift */; }; 564C44601E943C37000F92B1 /* NameRegistrationAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 564C445F1E943C37000F92B1 /* NameRegistrationAdapter.mm */; }; 564C44621E943DE6000F92B1 /* NameService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C44611E943DE6000F92B1 /* NameService.swift */; }; @@ -128,7 +130,7 @@ 56AC64DF1E804ECC00EA1AA9 /* SwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC64DE1E804ECC00EA1AA9 /* SwitchCell.swift */; }; 56AC64E11E80542300EA1AA9 /* TextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC64E01E80542300EA1AA9 /* TextFieldCell.swift */; }; 56AC64E31E805F0200EA1AA9 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC64E21E805F0200EA1AA9 /* TextCell.swift */; }; - 56AC650E1E85694D00EA1AA9 /* RoundedTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC650D1E85694D00EA1AA9 /* RoundedTextField.swift */; }; + 56AC650E1E85694D00EA1AA9 /* DesignableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC650D1E85694D00EA1AA9 /* DesignableTextField.swift */; }; 56BBC99F1ED714CB00CDAF8B /* MessagesAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC99E1ED714CB00CDAF8B /* MessagesAdapter.mm */; }; 56BBC9A21ED714DF00CDAF8B /* MessagesAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9A01ED714DF00CDAF8B /* MessagesAdapterDelegate.swift */; }; 56BBC9A31ED714DF00CDAF8B /* ConversationsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9A11ED714DF00CDAF8B /* ConversationsService.swift */; }; @@ -150,7 +152,6 @@ 56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9DE1EDDC9D300CDAF8B /* LookupNameResponse.m */; }; 56BBC9E01EDDC9E600CDAF8B /* ConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9AF1ED7155700CDAF8B /* ConversationViewModel.swift */; }; 56BBC9E31EDDCC8100CDAF8B /* ConversationSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9E21EDDCC8100CDAF8B /* ConversationSection.swift */; }; - 56BBC9E71EDE1DDF00CDAF8B /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9E61EDE1DDF00CDAF8B /* Colors.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -187,7 +188,7 @@ 0273C3001E0C445200CF00BA /* RingPrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RingPrefixHeader.pch; path = Ring/RingPrefixHeader.pch; sourceTree = "<group>"; }; 0273C3031E0C68B100CF00BA /* CreateProfileViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateProfileViewController.swift; sourceTree = "<group>"; }; 0273C3041E0C68B100CF00BA /* CreateRingAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateRingAccountViewController.swift; sourceTree = "<group>"; }; - 0273C3071E0C68BF00CF00BA /* RoundedButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = "<group>"; }; + 0273C3071E0C68BF00CF00BA /* DesignableButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableButton.swift; sourceTree = "<group>"; }; 028568301DF610A9003A8D8D /* RingTests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RingTests-Bridging-Header.h"; sourceTree = "<group>"; }; 029CE9D61E1D8C860000C8E1 /* ServiceEventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceEventTests.swift; sourceTree = "<group>"; }; 02AED8181DD4C4B100F740BA /* librestbed.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = librestbed.a; path = ../DEPS/x86_64/lib/librestbed.a; sourceTree = "<group>"; }; @@ -201,8 +202,7 @@ 02DD80C91E1EAF1A009A3510 /* AccountCredentialsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountCredentialsModel.swift; sourceTree = "<group>"; }; 02DD80CC1E1EB2E4009A3510 /* ConfigKeyModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigKeyModel.swift; sourceTree = "<group>"; }; 043866201D218B1100E06CE2 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; - 043866351D22D06500E06CE2 /* AccountTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountTableViewCell.swift; sourceTree = "<group>"; }; - 043866371D2304A700E06CE2 /* BoolStringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoolStringExtension.swift; sourceTree = "<group>"; }; + 043866371D2304A700E06CE2 /* String+Bool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Bool.swift"; sourceTree = "<group>"; }; 043999F31D1C2D9D00E99CD9 /* Ring.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ring.app; sourceTree = BUILT_PRODUCTS_DIR; }; 043999F61D1C2D9D00E99CD9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 04399A021D1C2D9D00E99CD9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; @@ -274,6 +274,10 @@ 04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libyaml-cpp.a"; path = "../fat/lib/libyaml-cpp.a"; sourceTree = "<group>"; }; 1A1E476C1F0E808500EA9A36 /* Reusable.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Reusable.framework; path = Carthage/Build/iOS/Reusable.framework; sourceTree = "<group>"; }; 1A1E476E1F0E894600EA9A36 /* SwiftyBeaver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyBeaver.framework; path = Carthage/Build/iOS/SwiftyBeaver.framework; sourceTree = "<group>"; }; + 1A3D28A61F0EB9DB00B524EE /* Bool+String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bool+String.swift"; sourceTree = "<group>"; }; + 1A3D28A81F0EBF0200B524EE /* UIView+Ring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Ring.swift"; sourceTree = "<group>"; }; + 1A8306321F0EDAA50099D98C /* AccountTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountTableViewCell.swift; sourceTree = "<group>"; }; + 1AABA7451F0FE9C000739605 /* UIColor+Ring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Ring.swift"; sourceTree = "<group>"; }; 1ABE07B91F0C16F100D36361 /* ContactViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = "<group>"; }; 1ABE07BB1F0C22CC00D36361 /* WalkthroughStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = WalkthroughStoryboard.storyboard; sourceTree = "<group>"; }; 1ABE07D01F0D8FE800D36361 /* Images.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Images.swift; sourceTree = "<group>"; }; @@ -296,7 +300,6 @@ 563AEC761EA664C0003A5641 /* RegistrationResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegistrationResponse.m; sourceTree = "<group>"; }; 564775811EE5CFC500A0C855 /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = Carthage/Build/iOS/Realm.framework; sourceTree = "<group>"; }; 564775821EE5CFC500A0C855 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RealmSwift.framework; path = Carthage/Build/iOS/RealmSwift.framework; sourceTree = "<group>"; }; - 564C44581E8D7F8F000F92B1 /* LocalizedStringTableNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizedStringTableNames.swift; sourceTree = "<group>"; }; 564C445A1E8EA44E000F92B1 /* Durations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Durations.swift; sourceTree = "<group>"; }; 564C445E1E943C37000F92B1 /* NameRegistrationAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NameRegistrationAdapter.h; sourceTree = "<group>"; }; 564C445F1E943C37000F92B1 /* NameRegistrationAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NameRegistrationAdapter.mm; sourceTree = "<group>"; }; @@ -312,15 +315,13 @@ 56AC64DE1E804ECC00EA1AA9 /* SwitchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchCell.swift; sourceTree = "<group>"; }; 56AC64E01E80542300EA1AA9 /* TextFieldCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldCell.swift; sourceTree = "<group>"; }; 56AC64E21E805F0200EA1AA9 /* TextCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = "<group>"; }; - 56AC650D1E85694D00EA1AA9 /* RoundedTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundedTextField.swift; sourceTree = "<group>"; }; + 56AC650D1E85694D00EA1AA9 /* DesignableTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesignableTextField.swift; sourceTree = "<group>"; }; 56BBC99D1ED714CB00CDAF8B /* MessagesAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessagesAdapter.h; sourceTree = "<group>"; }; 56BBC99E1ED714CB00CDAF8B /* MessagesAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MessagesAdapter.mm; sourceTree = "<group>"; }; 56BBC9A01ED714DF00CDAF8B /* MessagesAdapterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesAdapterDelegate.swift; sourceTree = "<group>"; }; 56BBC9A11ED714DF00CDAF8B /* ConversationsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationsService.swift; sourceTree = "<group>"; }; 56BBC9A51ED7151500CDAF8B /* MessageModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageModel.swift; sourceTree = "<group>"; }; 56BBC9A71ED7152300CDAF8B /* SmartlistViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmartlistViewController.swift; sourceTree = "<group>"; }; - 56BBC9A91ED7153800CDAF8B /* Global.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = Global.strings; path = Base.lproj/Global.strings; sourceTree = "<group>"; }; - 56BBC9AB1ED7154300CDAF8B /* Smartlist.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = Smartlist.strings; path = Base.lproj/Smartlist.strings; sourceTree = "<group>"; }; 56BBC9AE1ED7155700CDAF8B /* ConversationModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationModel.swift; sourceTree = "<group>"; }; 56BBC9AF1ED7155700CDAF8B /* ConversationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationViewModel.swift; sourceTree = "<group>"; }; 56BBC9B21ED7156500CDAF8B /* ConversationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationCell.swift; sourceTree = "<group>"; }; @@ -338,7 +339,6 @@ 56BBC9DD1EDDC9D300CDAF8B /* LookupNameResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LookupNameResponse.h; sourceTree = "<group>"; }; 56BBC9DE1EDDC9D300CDAF8B /* LookupNameResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LookupNameResponse.m; sourceTree = "<group>"; }; 56BBC9E21EDDCC8100CDAF8B /* ConversationSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationSection.swift; sourceTree = "<group>"; }; - 56BBC9E61EDE1DDF00CDAF8B /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -500,6 +500,7 @@ 02E1A0271DDE4C3900D75B59 /* Account */ = { isa = PBXGroup; children = ( + 1A8306321F0EDAA50099D98C /* AccountTableViewCell.swift */, 02B22DFA1DF755BB000358C9 /* AccountModel.swift */, 5516C29E1E71CEFF009D3D2D /* AccountModelHelper.swift */, 02B22DFB1DF755BB000358C9 /* CreateRingAccountViewModel.swift */, @@ -540,9 +541,8 @@ 043866341D22D04E00E06CE2 /* UI */ = { isa = PBXGroup; children = ( - 0273C3071E0C68BF00CF00BA /* RoundedButton.swift */, - 043866351D22D06500E06CE2 /* AccountTableViewCell.swift */, - 56AC650D1E85694D00EA1AA9 /* RoundedTextField.swift */, + 0273C3071E0C68BF00CF00BA /* DesignableButton.swift */, + 56AC650D1E85694D00EA1AA9 /* DesignableTextField.swift */, ); path = UI; sourceTree = "<group>"; @@ -551,7 +551,10 @@ isa = PBXGroup; children = ( 56BBC9BB1ED7161200CDAF8B /* Date+Helpers.swift */, - 043866371D2304A700E06CE2 /* BoolStringExtension.swift */, + 043866371D2304A700E06CE2 /* String+Bool.swift */, + 1A3D28A61F0EB9DB00B524EE /* Bool+String.swift */, + 1A3D28A81F0EBF0200B524EE /* UIView+Ring.swift */, + 1AABA7451F0FE9C000739605 /* UIColor+Ring.swift */, ); path = Extensions; sourceTree = "<group>"; @@ -589,7 +592,6 @@ 56BBC9AD1ED7154800CDAF8B /* Conversations */, 56BBC9A41ED7150200CDAF8B /* Messages */, 564C44571E8D7F68000F92B1 /* Constants */, - 56AC64D61E80121200EA1AA9 /* Internationalization */, 0273C3021E0C689600CF00BA /* Walkthrough */, 02EFCACF1E0C3DD600FD8ED1 /* Bridging */, 02E1A0271DDE4C3900D75B59 /* Account */, @@ -756,22 +758,11 @@ isa = PBXGroup; children = ( 1ABE07C51F0D862D00D36361 /* Generated */, - 56BBC9E61EDE1DDF00CDAF8B /* Colors.swift */, - 564C44581E8D7F8F000F92B1 /* LocalizedStringTableNames.swift */, 564C445A1E8EA44E000F92B1 /* Durations.swift */, ); path = Constants; sourceTree = "<group>"; }; - 56AC64D61E80121200EA1AA9 /* Internationalization */ = { - isa = PBXGroup; - children = ( - 56BBC9AB1ED7154300CDAF8B /* Smartlist.strings */, - 56BBC9A91ED7153800CDAF8B /* Global.strings */, - ); - name = Internationalization; - sourceTree = "<group>"; - }; 56AC64DD1E804EB500EA1AA9 /* Cells */ = { isa = PBXGroup; children = ( @@ -1038,14 +1029,16 @@ 557086521E8ADB9D001A7CE4 /* SystemAdapter.mm in Sources */, 5669A8031EAA58E6003C7B93 /* LinkDeviceToAccountViewController.swift in Sources */, 0273C3051E0C68B100CF00BA /* CreateProfileViewController.swift in Sources */, - 02E1A0251DDE4ABA00D75B59 /* BoolStringExtension.swift in Sources */, + 02E1A0251DDE4ABA00D75B59 /* String+Bool.swift in Sources */, 04399AAC1D1C304300E99CD9 /* AccountAdapter.mm in Sources */, + 1AABA7461F0FE9C000739605 /* UIColor+Ring.swift in Sources */, 02DD80C81E1EAD70009A3510 /* AccountConfigModel.swift in Sources */, 02B22E091DF7585F000358C9 /* DaemonService.swift in Sources */, 0273C3061E0C68B100CF00BA /* CreateRingAccountViewController.swift in Sources */, 56BBC99F1ED714CB00CDAF8B /* MessagesAdapter.mm in Sources */, 56BBC9CF1EDC5E7000CDAF8B /* MessageCell.swift in Sources */, 02C9B63F1E1D4E8C00F82F0C /* ServiceEvent.swift in Sources */, + 1A3D28A71F0EB9DB00B524EE /* Bool+String.swift in Sources */, 56BBC9D21EDC5E7000CDAF8B /* MessageViewModel.swift in Sources */, 02DD80CD1E1EB2E4009A3510 /* ConfigKeyModel.swift in Sources */, 56BBC9E31EDDCC8100CDAF8B /* ConversationSection.swift in Sources */, @@ -1059,14 +1052,13 @@ 56AC64E11E80542300EA1AA9 /* TextFieldCell.swift in Sources */, 1ABE07E21F0D924700D36361 /* Strings.swift in Sources */, 56AC64E31E805F0200EA1AA9 /* TextCell.swift in Sources */, - 56AC650E1E85694D00EA1AA9 /* RoundedTextField.swift in Sources */, + 56AC650E1E85694D00EA1AA9 /* DesignableTextField.swift in Sources */, 56BBC9BF1ED7168400CDAF8B /* SmartlistViewModel.swift in Sources */, 02B22E011DF755E5000358C9 /* MainTabBarViewController.swift in Sources */, 564C44641E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift in Sources */, 043999F71D1C2D9D00E99CD9 /* AppDelegate.swift in Sources */, 02B22DFC1DF755BB000358C9 /* AccountModel.swift in Sources */, 56AC64DF1E804ECC00EA1AA9 /* SwitchCell.swift in Sources */, - 56BBC9E71EDE1DDF00CDAF8B /* Colors.swift in Sources */, 56BBC9B91ED715FE00CDAF8B /* ContactModel.swift in Sources */, 56BBC9BA1ED715FE00CDAF8B /* ContactHelper.swift in Sources */, 04399AAE1D1C304300E99CD9 /* Utils.mm in Sources */, @@ -1075,17 +1067,18 @@ 563AEC771EA664C0003A5641 /* RegistrationResponse.m in Sources */, 02B22DFD1DF755BB000358C9 /* CreateRingAccountViewModel.swift in Sources */, 564C445B1E8EA44E000F92B1 /* Durations.swift in Sources */, + 1A8306331F0EDAA50099D98C /* AccountTableViewCell.swift in Sources */, 56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */, + 1A3D28A91F0EBF0200B524EE /* UIView+Ring.swift in Sources */, 1ABE07D21F0D8FE800D36361 /* Images.swift in Sources */, 56BBC9CD1EDC5E7000CDAF8B /* MessageAccessoryView.swift in Sources */, 02DD80CA1E1EAF1A009A3510 /* AccountCredentialsModel.swift in Sources */, - 0273C3081E0C68BF00CF00BA /* RoundedButton.swift in Sources */, + 0273C3081E0C68BF00CF00BA /* DesignableButton.swift in Sources */, 56BBC9BC1ED7161200CDAF8B /* Date+Helpers.swift in Sources */, 562FB6CD1EFAD18A00C61A78 /* ConversationViewController.swift in Sources */, 564C44621E943DE6000F92B1 /* NameService.swift in Sources */, 56BBC9E01EDDC9E600CDAF8B /* ConversationViewModel.swift in Sources */, 1ABE07BA1F0C16F100D36361 /* ContactViewModel.swift in Sources */, - 043866361D22D06500E06CE2 /* AccountTableViewCell.swift in Sources */, 04399AAD1D1C304300E99CD9 /* DRingAdapter.mm in Sources */, 0273C2FF1E0C438F00CF00BA /* AccountAdapterDelegate.swift in Sources */, 02B22DFF1DF755DB000358C9 /* AccountsService.swift in Sources */, @@ -1094,7 +1087,6 @@ 56BBC9A21ED714DF00CDAF8B /* MessagesAdapterDelegate.swift in Sources */, 56BBC9B41ED7156500CDAF8B /* ConversationCell.swift in Sources */, 564C44601E943C37000F92B1 /* NameRegistrationAdapter.mm in Sources */, - 564C44591E8D7F8F000F92B1 /* LocalizedStringTableNames.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Ring/Ring/UI/AccountTableViewCell.swift b/Ring/Ring/Account/AccountTableViewCell.swift similarity index 81% rename from Ring/Ring/UI/AccountTableViewCell.swift rename to Ring/Ring/Account/AccountTableViewCell.swift index 508c6692b269b576c14a8ae1e16bb1ec16987f25..866d74bf227cc910c0eb2043c5fbdceb4acf6c39 100644 --- a/Ring/Ring/UI/AccountTableViewCell.swift +++ b/Ring/Ring/Account/AccountTableViewCell.swift @@ -19,8 +19,9 @@ */ import UIKit +import Reusable -class AccountTableViewCell: UITableViewCell { +class AccountTableViewCell: UITableViewCell, NibReusable { // MARK: - Properties @IBOutlet weak var activeSwitch: UISwitch! @@ -29,15 +30,6 @@ class AccountTableViewCell: UITableViewCell { var account: AccountModel! - // MARK: - UITableViewCell - override func awakeFromNib() { - super.awakeFromNib() - } - - override func setSelected(_ selected: Bool, animated: Bool) { - super.setSelected(selected, animated: animated) - } - // MARK: - Actions @IBAction func switchAccountState(_ sender: UISwitch) { // account.isEnabled = sender.isOn diff --git a/Ring/Ring/Account/CreateRingAccountViewModel.swift b/Ring/Ring/Account/CreateRingAccountViewModel.swift index 97fd4bce987c49e0e71b529eee3fb80f9d69dd4a..7dbd1921c148e4f2e40375c0af27b155ef36e61d 100644 --- a/Ring/Ring/Account/CreateRingAccountViewModel.swift +++ b/Ring/Ring/Account/CreateRingAccountViewModel.swift @@ -140,17 +140,11 @@ class CreateRingAccountViewModel { .asObservable().map ({ status in switch status { case .lookingUp: - return NSLocalizedString("LookingForUsernameAvailability", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + return L10n.Createaccount.lookingForUsernameAvailability.smartString case .invalid: - return NSLocalizedString("InvalidUsername", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + return L10n.Createaccount.invalidUsername.smartString case .alreadyTaken: - return NSLocalizedString("UsernameAlreadyTaken", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + return L10n.Createaccount.usernameAlreadyTaken.smartString default: return "" } @@ -181,7 +175,7 @@ class CreateRingAccountViewModel { //Loookup name request observer self.username.asObservable().subscribe(onNext: { [unowned self] username in self.nameService.lookupName(withAccount: "", nameserver: "", name: username) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) //Name registration observer self.accountService @@ -200,7 +194,7 @@ class CreateRingAccountViewModel { name: self.username.value) } }) - .addDisposableTo(disposeBag) + .disposed(by: disposeBag) //Account creation state observer self.accountService @@ -215,7 +209,7 @@ class CreateRingAccountViewModel { } }, onError: { _ in self.accountCreationState.onError(AccountCreationError.unknown) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) } } @@ -238,34 +232,22 @@ extension AccountCreationError: LocalizedError { var title: String { switch self { case .generic: - return NSLocalizedString("AccountCannotBeFoundTitle", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + return L10n.Alerts.accountCannotBeFoundTitle.smartString case .network: - return NSLocalizedString("AccountNoNetworkTitle", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + return L10n.Alerts.accountNoNetworkTitle.smartString default: - return NSLocalizedString("AccountDefaultErrorTitle", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + return L10n.Alerts.accountDefaultErrorTitle.smartString } } var message: String { switch self { case .generic: - return NSLocalizedString("AcountCannotBeFoundMessage", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + return L10n.Alerts.accountDefaultErrorMessage.smartString case .network: - return NSLocalizedString("AccountNoNetworkMessage", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + return L10n.Alerts.accountNoNetworkMessage.smartString default: - return NSLocalizedString("AccountDefaultErrorMessage", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + return L10n.Alerts.accountDefaultErrorMessage.smartString } } } diff --git a/Ring/Ring/Constants/Durations.swift b/Ring/Ring/Constants/Durations.swift index d62b37d0e4c803bf7c443676a3319f4ef21807bb..bd0c87c81e91be3e261986a8409af869af9b8243 100644 --- a/Ring/Ring/Constants/Durations.swift +++ b/Ring/Ring/Constants/Durations.swift @@ -20,9 +20,16 @@ import Foundation -/** - Time interval between TextField events in seconds -*/ -let textFieldThrottlingDuration = 0.5 +public enum Durations { + case textFieldThrottlingDuration + case alertFlashDuration -let alertFlashDuration = 1.0 + var value: Double { + switch self { + case .textFieldThrottlingDuration: + return 0.5 + case .alertFlashDuration: + return 1.0 + } + } +} diff --git a/Ring/Ring/Constants/Generated/Strings.swift b/Ring/Ring/Constants/Generated/Strings.swift index f5a761061708e0b31ec0e7cdaea89490af84f8ba..bbbad24b00f7b145a71f67443fcfcb9194061ae9 100644 --- a/Ring/Ring/Constants/Generated/Strings.swift +++ b/Ring/Ring/Constants/Generated/Strings.swift @@ -13,64 +13,83 @@ private class RingStringsBundleToken {} // swiftlint:disable valid_docs enum L10n { - /// Account Added - static let accountAddedTitle = L10n.tr("AccountAddedTitle") - /// Can't find account - static let accountCannotBeFoundTitle = L10n.tr("AccountCannotBeFoundTitle") - /// The account couldn't be created. - static let accountDefaultErrorMessage = L10n.tr("AccountDefaultErrorMessage") - /// Unknown error - static let accountDefaultErrorTitle = L10n.tr("AccountDefaultErrorTitle") - /// Could not add account because Ring couldn't connect to the distributed network. Check your device connectivity. - static let accountNoNetworkMessage = L10n.tr("AccountNoNetworkMessage") - /// Can't connect to the network - static let accountNoNetworkTitle = L10n.tr("AccountNoNetworkTitle") - /// Account couldn't be found on the Ring network. Make sure it was exported on Ring from an existing device, and that provided credentials are correct. - static let acountCannotBeFoundMessage = L10n.tr("AcountCannotBeFoundMessage") - /// Choose strong password you will remember to protect your Ring account. - static let chooseStrongPassword = L10n.tr("ChooseStrongPassword") - /// Conversations - static let conversations = L10n.tr("Conversations") - /// Create a Ring account - static let createAccount = L10n.tr("CreateAccount") - /// Create your Ring account - static let createAccountFormTitle = L10n.tr("CreateAccountFormTitle") - /// Enter new username - static let enterNewUsernamePlaceholder = L10n.tr("EnterNewUsernamePlaceholder") - /// Home - static let homeTabBarTitle = L10n.tr("HomeTabBarTitle") - /// Invalid username - static let invalidUsername = L10n.tr("InvalidUsername") - /// Link this device to an account - static let linkDeviceButton = L10n.tr("LinkDeviceButton") - /// Looking for username availability... - static let lookingForUsernameAvailability = L10n.tr("LookingForUsernameAvailability") - /// New Password - static let newPasswordPlaceholder = L10n.tr("NewPasswordPlaceholder") - /// No results - static let noResults = L10n.tr("NoResults") - /// 6 characters minimum - static let passwordCharactersNumberError = L10n.tr("PasswordCharactersNumberError") - /// Passwords do not match - static let passwordNotMatchingError = L10n.tr("PasswordNotMatchingError") - /// Register public username (experimental) - static let registerPublicUsername = L10n.tr("RegisterPublicUsername") - /// Repeat new password - static let repeatPasswordPlaceholder = L10n.tr("RepeatPasswordPlaceholder") - /// Searching... - static let searching = L10n.tr("Searching") - /// User found - static let userFound = L10n.tr("UserFound") - /// Username already taken - static let usernameAlreadyTaken = L10n.tr("UsernameAlreadyTaken") - /// Adding account - static let waitCreateAccountTitle = L10n.tr("WaitCreateAccountTitle") - /// A Ring account allows you to reach people securely in peer to peer through fully distributed network - static let welcomeText = L10n.tr("WelcomeText") - /// Welcome to Ring - static let welcomeTitle = L10n.tr("WelcomeTitle") - /// Yesterday - static let yesterday = L10n.tr("Yesterday") + + enum Alerts { + /// Account Added + static let accountAddedTitle = L10n.tr("alerts.accountAddedTitle") + /// Can't find account + static let accountCannotBeFoundTitle = L10n.tr("alerts.accountCannotBeFoundTitle") + /// The account couldn't be created. + static let accountDefaultErrorMessage = L10n.tr("alerts.accountDefaultErrorMessage") + /// Unknown error + static let accountDefaultErrorTitle = L10n.tr("alerts.accountDefaultErrorTitle") + /// Could not add account because Ring couldn't connect to the distributed network. Check your device connectivity. + static let accountNoNetworkMessage = L10n.tr("alerts.accountNoNetworkMessage") + /// Can't connect to the network + static let accountNoNetworkTitle = L10n.tr("alerts.accountNoNetworkTitle") + /// Account couldn't be found on the Ring network. Make sure it was exported on Ring from an existing device, and that provided credentials are correct. + static let acountCannotBeFoundMessage = L10n.tr("alerts.acountCannotBeFoundMessage") + } + + enum Createaccount { + /// Choose strong password you will remember to protect your Ring account. + static let chooseStrongPassword = L10n.tr("createAccount.chooseStrongPassword") + /// Create your Ring account + static let createAccountFormTitle = L10n.tr("createAccount.createAccountFormTitle") + /// Enter new username + static let enterNewUsernamePlaceholder = L10n.tr("createAccount.enterNewUsernamePlaceholder") + /// Invalid username + static let invalidUsername = L10n.tr("createAccount.invalidUsername") + /// Loading... + static let loading = L10n.tr("createAccount.loading") + /// Looking for username availability... + static let lookingForUsernameAvailability = L10n.tr("createAccount.lookingForUsernameAvailability") + /// New Password + static let newPasswordPlaceholder = L10n.tr("createAccount.newPasswordPlaceholder") + /// 6 characters minimum + static let passwordCharactersNumberError = L10n.tr("createAccount.passwordCharactersNumberError") + /// Passwords do not match + static let passwordNotMatchingError = L10n.tr("createAccount.passwordNotMatchingError") + /// Register public username (experimental) + static let registerPublicUsername = L10n.tr("createAccount.registerPublicUsername") + /// Repeat new password + static let repeatPasswordPlaceholder = L10n.tr("createAccount.repeatPasswordPlaceholder") + /// Username already taken + static let usernameAlreadyTaken = L10n.tr("createAccount.usernameAlreadyTaken") + /// Adding account + static let waitCreateAccountTitle = L10n.tr("createAccount.waitCreateAccountTitle") + } + + enum Global { + /// Home + static let homeTabBarTitle = L10n.tr("global.homeTabBarTitle") + /// Ok + static let ok = L10n.tr("global.ok") + } + + enum Smartlist { + /// Conversations + static let conversations = L10n.tr("smartlist.conversations") + /// No results + static let noResults = L10n.tr("smartlist.noResults") + /// Searching... + static let searching = L10n.tr("smartlist.searching") + /// User found + static let userFound = L10n.tr("smartlist.userFound") + /// Yesterday + static let yesterday = L10n.tr("smartlist.yesterday") + } + + enum Welcome { + /// Create a Ring account + static let createAccount = L10n.tr("welcome.createAccount") + /// Link this device to an account + static let linkDeviceButton = L10n.tr("welcome.linkDeviceButton") + /// A Ring account allows you to reach people securely in peer to peer through fully distributed network + static let text = L10n.tr("welcome.text") + /// Welcome to Ring + static let title = L10n.tr("welcome.title") + } } struct LocalizableString { diff --git a/Ring/Ring/Contacts/ContactHelper.swift b/Ring/Ring/Contacts/ContactHelper.swift index c1f91b63acd27cfe3cb9ae18a586163f04709570..b55c883b4715c7483742e29ffec64c45a19eceaf 100644 --- a/Ring/Ring/Contacts/ContactHelper.swift +++ b/Ring/Ring/Contacts/ContactHelper.swift @@ -39,7 +39,7 @@ class ContactHelper { } else { userName.value = lookupNameResponse.address } - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) nameService.lookupAddress(withAccount: "", nameserver: "", address: ringId) diff --git a/Ring/Ring/Contacts/ContactViewModel.swift b/Ring/Ring/Contacts/ContactViewModel.swift index 15ef8b4f35ae0a5ce1ea96d337ad798026ddcbe5..c15eeebe8e16be3c2530d1b1538e150e2f3b8f98 100644 --- a/Ring/Ring/Contacts/ContactViewModel.swift +++ b/Ring/Ring/Contacts/ContactViewModel.swift @@ -73,7 +73,7 @@ class ContactViewModel { } else { self.userName.value = lookupNameResponse.address } - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) nameService.lookupAddress(withAccount: "", nameserver: "", address: self.contact.ringId) } diff --git a/Ring/Ring/Conversations/ConversationCell.swift b/Ring/Ring/Conversations/ConversationCell.swift index d3fd4fd62bfbafb54f8a96def7fddc72e97832dd..12082a6f9a7f5cd0fb2c7c0f568418b3f92d7e10 100644 --- a/Ring/Ring/Conversations/ConversationCell.swift +++ b/Ring/Ring/Conversations/ConversationCell.swift @@ -20,8 +20,9 @@ import UIKit import RxSwift +import Reusable -class ConversationCell: UITableViewCell { +class ConversationCell: UITableViewCell, NibReusable { @IBOutlet weak var profileImage: UIImageView! @IBOutlet weak var nameLabel: UILabel! @@ -30,11 +31,6 @@ class ConversationCell: UITableViewCell { @IBOutlet weak var lastMessageDateLabel: UILabel! @IBOutlet weak var lastMessagePreviewLabel: UILabel! - override func awakeFromNib() { - super.awakeFromNib() - - } - override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) self.newMessagesIndicator.backgroundColor = UIColor.red diff --git a/Ring/Ring/Conversations/ConversationCell.xib b/Ring/Ring/Conversations/ConversationCell.xib index 6f54e38306e9ce73ef9df777b4e3bcf487769d08..a82c301ea75eb6599b07f9b812b8c7a65ecf3a81 100644 --- a/Ring/Ring/Conversations/ConversationCell.xib +++ b/Ring/Ring/Conversations/ConversationCell.xib @@ -1,21 +1,21 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait"> <adaptation id="fullscreen"/> </device> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ConversationCellId" rowHeight="76" id="KGk-i7-Jjw" customClass="ConversationCell" customModule="Ring" customModuleProvider="target"> + <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="76" id="KGk-i7-Jjw" customClass="ConversationCell" customModule="Ring" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="358" height="76"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM"> - <rect key="frame" x="0.0" y="0.0" width="358" height="76"/> + <rect key="frame" x="0.0" y="0.0" width="358" height="75.5"/> <autoresizingMask key="autoresizingMask"/> <subviews> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_contact_picture" translatesAutoresizingMaskIntoConstraints="NO" id="pFB-Jn-TNP"> diff --git a/Ring/Ring/Conversations/ConversationViewController.swift b/Ring/Ring/Conversations/ConversationViewController.swift index 221444ea99585ba1b1ca22ed169e4f901f38d8c5..81c3afdb4185e04aa1ef74a193f09159fcefd4c8 100644 --- a/Ring/Ring/Conversations/ConversationViewController.swift +++ b/Ring/Ring/Conversations/ConversationViewController.swift @@ -71,7 +71,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate { } func setupUI() { - self.viewModel?.userName.asObservable().bind(to: self.navigationItem.rx.title).addDisposableTo(disposeBag) + self.viewModel?.userName.asObservable().bind(to: self.navigationItem.rx.title).disposed(by: disposeBag) self.tableView.contentInset.bottom = messageAccessoryView.frame.size.height self.tableView.scrollIndicatorInsets.bottom = messageAccessoryView.frame.size.height @@ -97,22 +97,21 @@ class ConversationViewController: UIViewController, UITextFieldDelegate { self.tableView.separatorStyle = .none //Register cell - self.tableView.register(UINib.init(nibName: "MessageCell", bundle: nil), - forCellReuseIdentifier: "MessageCellId") + self.tableView.register(cellType: MessageCell.self) //Bind the TableView to the ViewModel self.viewModel?.messages - .bind(to: tableView.rx.items(cellIdentifier: "MessageCellId", + .bind(to: tableView.rx.items(cellIdentifier: "MessageCell", cellType: MessageCell.self)) { _, messageViewModel, cell in cell.messageLabel.text = messageViewModel.content cell.bubblePosition = messageViewModel.bubblePosition() - }.addDisposableTo(disposeBag) + }.disposed(by: disposeBag) //Scroll to bottom when reloaded self.tableView.rx.methodInvoked(#selector(UITableView.reloadData)).subscribe(onNext: { _ in self.scrollToBottomIfNeed() self.updateBottomOffset() - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) } fileprivate func updateBottomOffset() { @@ -153,7 +152,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate { } lazy var messageAccessoryView: MessageAccessoryView = { - return MessageAccessoryView.instanceFromNib() + return MessageAccessoryView.loadFromNib() }() func setupBindings() { @@ -162,7 +161,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate { self.messageAccessoryView.messageTextField.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: { _ in self.viewModel?.sendMessage(withContent: self.messageAccessoryView.messageTextField.text!) self.messageAccessoryView.messageTextField.text = "" - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) } // Avoid the keyboard to be hidden when the Send button is touched diff --git a/Ring/Ring/Conversations/ConversationViewModel.swift b/Ring/Ring/Conversations/ConversationViewModel.swift index 2cd71b8964f3d9c6c955bdabcccda63790fbe16d..f67985b8e1c9d2652d00499b5255884134ab124a 100644 --- a/Ring/Ring/Conversations/ConversationViewModel.swift +++ b/Ring/Ring/Conversations/ConversationViewModel.swift @@ -89,7 +89,7 @@ class ConversationViewModel { self.log.error("Realm persistence with error: \(error)") } - }).addDisposableTo(self.disposeBag) + }).disposed(by: self.disposeBag) return tmp } @@ -134,7 +134,7 @@ class ConversationViewModel { if todayDay == day && todayMonth == month && todayYear == year { return hourFormatter.string(from: lastMessageDate) } else if day == todayDay - 1 { - return NSLocalizedString("Yesterday", tableName: "Smartlist", comment: "") + return L10n.Smartlist.yesterday.smartString } else if todayYear == year && todayWeekOfYear == weekOfYear { return lastMessageDate.dayOfWeek() } else { @@ -158,7 +158,7 @@ class ConversationViewModel { .subscribe(onCompleted: { let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!) self.saveMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: (self.conversation.recipient?.ringId)!) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) } fileprivate func saveMessage(withContent content: String, byAuthor author: String, toConversationWith account: String) { @@ -167,7 +167,7 @@ class ConversationViewModel { .subscribe(onCompleted: { [unowned self] in self.log.debug("Message saved") }) - .addDisposableTo(disposeBag) + .disposed(by: disposeBag) } func setMessagesAsRead() { @@ -175,7 +175,7 @@ class ConversationViewModel { .setMessagesAsRead(forConversation: self.conversation) .subscribe(onCompleted: { [unowned self] in self.log.debug("Message set as read") - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) } fileprivate var unreadMessagesCount: Int { diff --git a/Ring/Ring/Extensions/Bool+String.swift b/Ring/Ring/Extensions/Bool+String.swift new file mode 100644 index 0000000000000000000000000000000000000000..c065b4080c047b02f0702861fe27a3aa242898c6 --- /dev/null +++ b/Ring/Ring/Extensions/Bool+String.swift @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Savoir-faire Linux Inc. + * + * Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import Foundation + +extension Bool { + func toString() -> String { + return self ? "true" : "false" + } +} diff --git a/Ring/Ring/Extensions/BoolStringExtension.swift b/Ring/Ring/Extensions/String+Bool.swift similarity index 80% rename from Ring/Ring/Extensions/BoolStringExtension.swift rename to Ring/Ring/Extensions/String+Bool.swift index ce59513bd96bad3b91a5a187b0ebd9ad15820cd1..477e42825c824be7ab67b267fcfffb2dea251697 100644 --- a/Ring/Ring/Extensions/BoolStringExtension.swift +++ b/Ring/Ring/Extensions/String+Bool.swift @@ -22,22 +22,13 @@ import Foundation extension String { func toBool() -> Bool? { - switch self { - case "True", "true", "yes", "1": + switch self.lowercased() { + case "true", "yes", "1": return true - case "False", "false", "no", "0": + case "false", "no", "0": return false default: return nil } } } - -extension Bool { - func toString() -> String { - if self == true { - return "true" - } - return "false" - } -} diff --git a/Ring/Ring/UI/RoundedTextField.swift b/Ring/Ring/Extensions/UIColor+Ring.swift similarity index 59% rename from Ring/Ring/UI/RoundedTextField.swift rename to Ring/Ring/Extensions/UIColor+Ring.swift index 5478afdcf7b70822fda88de3ceaf15e1d12aac11..20d90c902deb0b55f2ab5582056e874fcf2d8639 100644 --- a/Ring/Ring/UI/RoundedTextField.swift +++ b/Ring/Ring/Extensions/UIColor+Ring.swift @@ -1,7 +1,7 @@ /* - * Copyright (C) 2017 Savoir-faire Linux Inc. + * Copyright (C) 2016 Savoir-faire Linux Inc. * - * Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com> + * Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,20 +18,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import Foundation import UIKit -class RoundedTextField: UITextField { +extension UIColor { - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - self.layer.borderColor = UIColor.white.cgColor - self.layer.borderWidth = 1.0 - self.clipsToBounds = true - } - - override func layoutSubviews() { - super.layoutSubviews() - self.layer.cornerRadius = self.frame.size.height / 2.0 - } + static let ringMain = UIColor(colorLiteralRed: 10.0/255.0, + green: 116.0/255.0, + blue: 137.0/255.0, + alpha: 1.0) } diff --git a/Ring/Ring/Extensions/UIView+Ring.swift b/Ring/Ring/Extensions/UIView+Ring.swift new file mode 100644 index 0000000000000000000000000000000000000000..a6a96d286ec8f24dd7029c726babd329c266531c --- /dev/null +++ b/Ring/Ring/Extensions/UIView+Ring.swift @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 Savoir-faire Linux Inc. + * + * Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import Foundation +import UIKit + +extension UIView { + + @IBInspectable + var cornerRadius: CGFloat { + get { + return self.layer.cornerRadius + } + + set { + self.clipsToBounds = true + self.layer.cornerRadius = newValue + } + } + + @IBInspectable + var roundedCorners: Bool { + get { + return self.cornerRadius == self.frame.height / 2 + } + + set { + if newValue { + self.cornerRadius = self.frame.height / 2 + } else { + self.cornerRadius = 0 + } + } + } + + @IBInspectable + var borderWidth: CGFloat { + get { + return self.layer.borderWidth + } + + set { + self.layer.borderWidth = newValue + } + } + + @IBInspectable + var borderColor: UIColor { + get { + return UIColor(cgColor: self.layer.borderColor ?? UIColor.clear.cgColor) + } + + set { + self.layer.borderColor = newValue.cgColor + } + } + +} diff --git a/Ring/Ring/MainTabBar/MainTabBarViewController.swift b/Ring/Ring/MainTabBar/MainTabBarViewController.swift index 20f2eaea96efb30e43d156b0717ae485594ab095..26be014963cdf756098878f2bbb3d1a627b00ab0 100644 --- a/Ring/Ring/MainTabBar/MainTabBarViewController.swift +++ b/Ring/Ring/MainTabBar/MainTabBarViewController.swift @@ -26,7 +26,7 @@ class MainTabBarViewController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() UITabBarItem.appearance() - .setTitleTextAttributes( [NSForegroundColorAttributeName: Colors.ringMainColor], for: .selected) + .setTitleTextAttributes( [NSForegroundColorAttributeName: UIColor.ringMain], for: .selected) } override func viewDidAppear(_ animated: Bool) { diff --git a/Ring/Ring/Messages/MessageAccessoryView.swift b/Ring/Ring/Messages/MessageAccessoryView.swift index 1d01570e518563f385af5b2ea71cfa4895528ac0..8c46e552862aad27553f86e59539ec4428c16e76 100644 --- a/Ring/Ring/Messages/MessageAccessoryView.swift +++ b/Ring/Ring/Messages/MessageAccessoryView.swift @@ -19,16 +19,10 @@ */ import UIKit +import Reusable -class MessageAccessoryView: UIView { +class MessageAccessoryView: UIView, NibLoadable { @IBOutlet weak var messageTextField: UITextField! - class func instanceFromNib() -> MessageAccessoryView { - guard let view = UINib(nibName: "MessageAccessoryView", bundle: nil).instantiate(withOwner: nil, options: nil).first as? MessageAccessoryView else { - fatalError("The view you are trying to instantiate is not a MessageAccessoryView") - } - return view - } - } diff --git a/Ring/Ring/Messages/MessageCell.swift b/Ring/Ring/Messages/MessageCell.swift index 7cc80f6543051f5373d7c50ce8d54f671fd3edaf..aeebe9a9702449b78dc407deb11856170b990e7a 100644 --- a/Ring/Ring/Messages/MessageCell.swift +++ b/Ring/Ring/Messages/MessageCell.swift @@ -19,13 +19,14 @@ */ import UIKit +import Reusable enum BubblePosition { case received case sent } -class MessageCell: UITableViewCell { +class MessageCell: UITableViewCell, NibReusable { @IBOutlet weak var bubble: UIView! @IBOutlet weak var messageLabel: UILabel! @@ -44,7 +45,7 @@ class MessageCell: UITableViewCell { self.containerLeadingConstraint.priority = 1 self.minimumLeadingConstraint.priority = 999 - self.bubble.backgroundColor = Colors.ringMainColor + self.bubble.backgroundColor = UIColor.ringMain self.messageLabel.textColor = UIColor.white } else { self.minimumLeadingConstraint.priority = 1 diff --git a/Ring/Ring/Messages/MessageCell.xib b/Ring/Ring/Messages/MessageCell.xib index 0bf54454032c3abd681c537bd54cb2fc3ec331d0..65174d4a7c78df2f3babfee33d241c43ffac4811 100644 --- a/Ring/Ring/Messages/MessageCell.xib +++ b/Ring/Ring/Messages/MessageCell.xib @@ -1,21 +1,21 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait"> <adaptation id="fullscreen"/> </device> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="MessageCellId" rowHeight="60" id="KGk-i7-Jjw" customClass="MessageCell" customModule="Ring" customModuleProvider="target"> + <tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="60" id="KGk-i7-Jjw" customClass="MessageCell" customModule="Ring" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="510" height="47"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM"> - <rect key="frame" x="0.0" y="0.0" width="510" height="46"/> + <rect key="frame" x="0.0" y="0.0" width="510" height="46.5"/> <autoresizingMask key="autoresizingMask"/> <subviews> <view clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="kZJ-Ay-LTR"> diff --git a/Ring/Ring/Resources/Main.storyboard b/Ring/Ring/Resources/Main.storyboard index 0b24cd46450238f0d5eb701a0b82b1d04939b4da..fe011f21b8d343290a207cb05c6daf6052c4d497 100644 --- a/Ring/Ring/Resources/Main.storyboard +++ b/Ring/Ring/Resources/Main.storyboard @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="qdG-Sd-QaE"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="qdG-Sd-QaE"> <device id="retina4_0" orientation="portrait"> <adaptation id="fullscreen"/> </device> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> <capability name="Constraints to layout margins" minToolsVersion="6.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> @@ -50,7 +50,7 @@ <rect key="frame" x="0.0" y="28" width="320" height="44"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="i1O-Yc-WGd" id="Bz1-A3-Z3f"> - <rect key="frame" x="0.0" y="0.0" width="320" height="44"/> + <rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/> <autoresizingMask key="autoresizingMask"/> <subviews> <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bm0-lC-K2F"> @@ -96,7 +96,7 @@ <rect key="frame" x="0.0" y="72" width="320" height="44"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Ok2-8L-eMm" id="m64-AI-t2h"> - <rect key="frame" x="0.0" y="0.0" width="320" height="44"/> + <rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/> <autoresizingMask key="autoresizingMask"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kZv-uf-BsD" userLabel="Account Name Label"> @@ -332,11 +332,11 @@ <rect key="frame" x="0.0" y="0.0" width="320" height="455"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <prototypes> - <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MessageCellId" id="8rX-Qa-Ypu"> + <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MessageCell" id="8rX-Qa-Ypu"> <rect key="frame" x="0.0" y="28" width="320" height="44"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="8rX-Qa-Ypu" id="7vA-nx-B3h"> - <rect key="frame" x="0.0" y="0.0" width="320" height="44"/> + <rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/> <autoresizingMask key="autoresizingMask"/> </tableViewCellContentView> </tableViewCell> diff --git a/Ring/Ring/Resources/en.lproj/Localizable.strings b/Ring/Ring/Resources/en.lproj/Localizable.strings index b95453ef3194165a6d49696df031a28b49fde83d..8521f93221231a1e369c70866f9b39d7f596bd88 100644 --- a/Ring/Ring/Resources/en.lproj/Localizable.strings +++ b/Ring/Ring/Resources/en.lproj/Localizable.strings @@ -19,51 +19,46 @@ */ // Global -"HomeTabBarTitle" = "Home"; +"global.homeTabBarTitle" = "Home"; +"global.ok" = "Ok"; // Smartlist -"Yesterday" = "Yesterday"; - -"UserFound" = "User found"; -"Conversations" = "Conversations"; - -"Searching" = "Searching..."; -"NoResults" = "No results"; +"smartlist.yesterday" = "Yesterday"; +"smartlist.userFound" = "User found"; +"smartlist.conversations" = "Conversations"; +"smartlist.searching" = "Searching..."; +"smartlist.noResults" = "No results"; // Walkthrough //Welcome Screen - -"WelcomeTitle" = "Welcome to Ring"; -"WelcomeText" = "A Ring account allows you to reach people securely in peer to peer through fully distributed network"; -"LinkDeviceButton" = "Link this device to an account"; -"CreateAccount" = "Create a Ring account"; +"welcome.title" = "Welcome to Ring"; +"welcome.text" = "A Ring account allows you to reach people securely in peer to peer through fully distributed network"; +"welcome.linkDeviceButton" = "Link this device to an account"; +"welcome.createAccount" = "Create a Ring account"; //Create Account form - -"CreateAccountFormTitle" = "Create your Ring account"; -"RegisterPublicUsername" = "Register public username (experimental)"; -"ChooseStrongPassword" = "Choose strong password you will remember to protect your Ring account."; -"EnterNewUsernamePlaceholder" = "Enter new username"; -"NewPasswordPlaceholder" = "New Password"; -"RepeatPasswordPlaceholder" = "Repeat new password"; -"PasswordCharactersNumberError" = "6 characters minimum"; -"PasswordNotMatchingError" = "Passwords do not match"; -"LookingForUsernameAvailability" = "Looking for username availability..."; -"InvalidUsername" = "Invalid username"; -"UsernameAlreadyTaken" = "Username already taken"; +"createAccount.createAccountFormTitle" = "Create your Ring account"; +"createAccount.registerPublicUsername" = "Register public username (experimental)"; +"createAccount.chooseStrongPassword" = "Choose strong password you will remember to protect your Ring account."; +"createAccount.enterNewUsernamePlaceholder" = "Enter new username"; +"createAccount.newPasswordPlaceholder" = "New Password"; +"createAccount.repeatPasswordPlaceholder" = "Repeat new password"; +"createAccount.passwordCharactersNumberError" = "6 characters minimum"; +"createAccount.passwordNotMatchingError" = "Passwords do not match"; +"createAccount.lookingForUsernameAvailability" = "Looking for username availability..."; +"createAccount.invalidUsername" = "Invalid username"; +"createAccount.usernameAlreadyTaken" = "Username already taken"; +"createAccount.loading" = "Loading..."; //Progress View -"WaitCreateAccountTitle" = "Adding account"; +"createAccount.waitCreateAccountTitle" = "Adding account"; //Alerts -"AccountCannotBeFoundTitle" = "Can't find account"; -"AcountCannotBeFoundMessage" = "Account couldn't be found on the Ring network. Make sure it was exported on Ring from an existing device, and that provided credentials are correct."; - -"AccountAddedTitle" = "Account Added"; - -"AccountNoNetworkTitle" = "Can't connect to the network"; -"AccountNoNetworkMessage" = "Could not add account because Ring couldn't connect to the distributed network. Check your device connectivity."; - -"AccountDefaultErrorTitle" = "Unknown error"; -"AccountDefaultErrorMessage" = "The account couldn't be created."; +"alerts.accountCannotBeFoundTitle" = "Can't find account"; +"alerts.acountCannotBeFoundMessage" = "Account couldn't be found on the Ring network. Make sure it was exported on Ring from an existing device, and that provided credentials are correct."; +"alerts.accountAddedTitle" = "Account Added"; +"alerts.accountNoNetworkTitle" = "Can't connect to the network"; +"alerts.accountNoNetworkMessage" = "Could not add account because Ring couldn't connect to the distributed network. Check your device connectivity."; +"alerts.accountDefaultErrorTitle" = "Unknown error"; +"alerts.accountDefaultErrorMessage" = "The account couldn't be created."; diff --git a/Ring/Ring/Services/AccountsService.swift b/Ring/Ring/Services/AccountsService.swift index e18d32eb1ad48c8a1756edf051cb55b6bf806152..1392d97d6693393a9ecefc19761018679dec1607 100644 --- a/Ring/Ring/Services/AccountsService.swift +++ b/Ring/Ring/Services/AccountsService.swift @@ -119,7 +119,7 @@ class AccountsService: AccountAdapterDelegate { init(withAccountAdapter accountAdapter: AccountAdapter) { self.accountList = [] - self.responseStream.addDisposableTo(disposeBag) + self.responseStream.disposed(by: disposeBag) //~ Create a shared stream based on the responseStream one. self.sharedResponseStream = responseStream.share() diff --git a/Ring/Ring/Services/ConversationsService.swift b/Ring/Ring/Services/ConversationsService.swift index f69d02280c78b4a6df32239203eff51f541438c7..3be76e5da8e1988e87b82d3bbfb5d72b09fd9571 100644 --- a/Ring/Ring/Services/ConversationsService.swift +++ b/Ring/Ring/Services/ConversationsService.swift @@ -18,7 +18,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import UIKit import RxSwift import RealmSwift import SwiftyBeaver @@ -159,20 +158,26 @@ class ConversationsService: MessagesAdapterDelegate { } func deleteConversation(conversation: ConversationModel) { - try! realm.write { - //Remove all messages from the conversation - for message in conversation.messages { - realm.delete(message) - } + do { + try realm.write { + + //Remove all messages from the conversation + for message in conversation.messages { + realm.delete(message) + } - realm.delete(conversation) + realm.delete(conversation) + } + } catch let error { + self.log.error("\(error)") } } - //MARK: Message Adapter delegate + // MARK: Message Adapter delegate - func didReceiveMessage(_ message: [String: String], from senderAccount: String, + func didReceiveMessage(_ message: [String: String], + from senderAccount: String, to receiverAccountId: String) { if let content = message[textPlainMIMEType] { @@ -180,12 +185,14 @@ class ConversationsService: MessagesAdapterDelegate { .subscribe(onCompleted: { [unowned self] in self.log.info("Message saved") }) - .addDisposableTo(disposeBag) + .disposed(by: disposeBag) } } - func messageStatusChanged(_ status: MessageStatus, for messageId: UInt64, - from senderAccountId: String, to receiverAccount: String) { + func messageStatusChanged(_ status: MessageStatus, + for messageId: UInt64, + from senderAccountId: String, + to receiverAccount: String) { log.debug("messageStatusChanged: \(status.rawValue) for: \(messageId) from: \(senderAccountId) to: \(receiverAccount)") } } diff --git a/Ring/Ring/Services/NameService.swift b/Ring/Ring/Services/NameService.swift index 2b8df9e4d07832d9ef083ed6e728ea7c0cabb4e8..d16824dcbecae0a77a4e02ebe089b925eb076880 100644 --- a/Ring/Ring/Services/NameService.swift +++ b/Ring/Ring/Services/NameService.swift @@ -18,7 +18,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import UIKit import RxSwift import SwiftyBeaver diff --git a/Ring/Ring/Settings/AccountDetailsViewController.swift b/Ring/Ring/Settings/AccountDetailsViewController.swift index a5dbc4a223e4b49997f795a3d499884a719a9db6..3be87e912075e1b5dd5240ed533d42d35b05fc41 100644 --- a/Ring/Ring/Settings/AccountDetailsViewController.swift +++ b/Ring/Ring/Settings/AccountDetailsViewController.swift @@ -27,13 +27,4 @@ class AccountDetailsViewController: UIViewController { @IBOutlet weak var detailsLabel: UILabel! - // MARK: - UIViewController - override func viewDidLoad() { - super.viewDidLoad() - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - } - } diff --git a/Ring/Ring/Settings/MeViewController.swift b/Ring/Ring/Settings/MeViewController.swift index 995e0c01fa1559fe2602bdae8c79e208a3b87ff2..d57fdd46299bd6cad5fad4811f32669c410acfed 100644 --- a/Ring/Ring/Settings/MeViewController.swift +++ b/Ring/Ring/Settings/MeViewController.swift @@ -33,11 +33,11 @@ class MeViewController: UIViewController, UITableViewDelegate, UITableViewDataSo super.viewDidLoad() if !accountService.accounts.isEmpty { -// let acc = accountService.accounts[0] -// nameLabel.text = acc.displayName -// if let username = acc.username { -// createQRFromString(username); -// } + // let acc = accountService.accounts[0] + // nameLabel.text = acc.displayName + // if let username = acc.username { + // createQRFromString(username); + // } } } @@ -74,17 +74,15 @@ class MeViewController: UIViewController, UITableViewDelegate, UITableViewDataSo func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.row < accountService.accounts.count { - if let cell = tableView.dequeueReusableCell(withIdentifier: "accountTableCell", for: indexPath) as? AccountTableViewCell { - let account = accountService.accounts[indexPath.row] - - cell.account = account - // cell.accountNameLabel.text = account.alias - // cell.activeSwitch.setOn(account.isEnabled, animated: false) - // cell.accountTypeLabel.text = account.accountType.rawValue - return cell - } else { - return tableView.dequeueReusableCell(withIdentifier: "accountTableCell", for: indexPath) - } + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: AccountTableViewCell.self) + let account = accountService.accounts[indexPath.row] + + cell.account = account + // cell.accountNameLabel.text = account.alias + // cell.activeSwitch.setOn(account.isEnabled, animated: false) + // cell.accountTypeLabel.text = account.accountType.rawValue + return cell + } else { let cell = tableView.dequeueReusableCell(withIdentifier: "addAccountTableCell", for: indexPath) return cell @@ -127,5 +125,4 @@ class MeViewController: UIViewController, UITableViewDelegate, UITableViewDataSo vc.account = cell.account } } - } diff --git a/Ring/Ring/Smartlist/SmartlistViewController.swift b/Ring/Ring/Smartlist/SmartlistViewController.swift index a0e2b8abe8b6c8b5547f12bf5d29dd01d2161e71..52c5406afae1d2059c990fc547af6390b79b035a 100644 --- a/Ring/Ring/Smartlist/SmartlistViewController.swift +++ b/Ring/Ring/Smartlist/SmartlistViewController.swift @@ -22,10 +22,9 @@ import UIKit import RxSwift import RxDataSources import RxCocoa +import Reusable //Constants -fileprivate let conversationCellIdentifier = "ConversationCellId" -fileprivate let conversationCellNibName = "ConversationCell" fileprivate let showMessages = "ShowMessages" fileprivate let smartlistRowHeight: CGFloat = 64.0 @@ -70,14 +69,12 @@ class SmartlistViewController: UIViewController { func setupUI() { - let title = NSLocalizedString("HomeTabBarTitle", tableName: "Global", comment: "") - - self.title = title - self.navigationItem.title = title + self.title = L10n.Global.homeTabBarTitle.smartString + self.navigationItem.title = L10n.Global.homeTabBarTitle.smartString self.viewModel.hideNoConversationsMessage .bind(to: self.noConversationsView.rx.isHidden) - .addDisposableTo(disposeBag) + .disposed(by: disposeBag) self.navigationItem.rightBarButtonItem = self.editButtonItem } @@ -125,16 +122,15 @@ class SmartlistViewController: UIViewController { tableView: UITableView, indexPath: IndexPath, item: ConversationSection.Item) in - if let cell = tableView.dequeueReusableCell(withIdentifier: conversationCellIdentifier, for: indexPath) as? ConversationCell { - item.userName.asObservable().bind(to: cell.nameLabel.rx.text).addDisposableTo(self.disposeBag) - cell.newMessagesLabel.text = item.unreadMessages - cell.lastMessageDateLabel.text = item.lastMessageReceivedDate - cell.newMessagesIndicator.isHidden = item.hideNewMessagesLabel - cell.lastMessagePreviewLabel.text = item.lastMessage - return cell - } else { - return tableView.dequeueReusableCell(withIdentifier: conversationCellIdentifier, for: indexPath) - } + + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ConversationCell.self) + + item.userName.asObservable().bind(to: cell.nameLabel.rx.text).disposed(by: self.disposeBag) + cell.newMessagesLabel.text = item.unreadMessages + cell.lastMessageDateLabel.text = item.lastMessageReceivedDate + cell.newMessagesIndicator.isHidden = item.hideNewMessagesLabel + cell.lastMessagePreviewLabel.text = item.lastMessage + return cell } //Allows to delete @@ -148,11 +144,11 @@ class SmartlistViewController: UIViewController { //Bind TableViews to DataSources self.viewModel.conversations .bind(to: self.conversationsTableView.rx.items(dataSource: conversationsDataSource)) - .addDisposableTo(disposeBag) + .disposed(by: disposeBag) self.viewModel.searchResults .bind(to: self.searchResultsTableView.rx.items(dataSource: searchResultsDatasource)) - .addDisposableTo(disposeBag) + .disposed(by: disposeBag) //Set header titles searchResultsDatasource.titleForHeaderInSection = { dataSource, index in @@ -167,48 +163,49 @@ class SmartlistViewController: UIViewController { self.searchResultsTableView.rowHeight = smartlistRowHeight //Register Cell - self.conversationsTableView.register(UINib.init(nibName: conversationCellNibName, bundle: nil), forCellReuseIdentifier: conversationCellIdentifier) - self.searchResultsTableView.register(UINib.init(nibName: conversationCellNibName, bundle: nil), forCellReuseIdentifier: conversationCellIdentifier) + self.conversationsTableView.register(cellType: ConversationCell.self) + self.searchResultsTableView.register(cellType: ConversationCell.self) //Bind to ViewModel to show or hide the filtered results self.viewModel.isSearching.subscribe(onNext: { isSearching in self.searchResultsTableView.isHidden = !isSearching - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) //Show the Messages screens and pass the viewModel for Conversations self.conversationsTableView.rx.modelSelected(ConversationViewModel.self).subscribe(onNext: { item in self.selectedItem = item self.performSegue(withIdentifier: showMessages, sender: nil) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) //Show the Messages screens and pass the viewModel for Search Results self.searchResultsTableView.rx.modelSelected(ConversationViewModel.self).subscribe(onNext: { item in self.selectedItem = item self.performSegue(withIdentifier: showMessages, sender: nil) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) //Deselect the rows self.conversationsTableView.rx.itemSelected.subscribe(onNext: { [unowned self] indexPath in self.conversationsTableView.deselectRow(at: indexPath, animated: true) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) self.searchResultsTableView.rx.itemSelected.subscribe(onNext: { [unowned self] indexPath in self.searchResultsTableView.deselectRow(at: indexPath, animated: true) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) //Bind the search status label self.viewModel.searchStatus .observeOn(MainScheduler.instance) .bind(to: self.searchTableViewLabel.rx.text) - .addDisposableTo(disposeBag) + .disposed(by: disposeBag) - self.searchResultsTableView.rx.setDelegate(self).addDisposableTo(disposeBag) + self.searchResultsTableView.rx.setDelegate(self).disposed(by: disposeBag) //Swipe to delete action - self.conversationsTableView.rx.itemDeleted.subscribe(onNext:{ [unowned self] indexPath in - let convToDelete :ConversationViewModel = try! self.conversationsTableView.rx.model(at: indexPath) - self.viewModel.delete(conversationViewModel: convToDelete) - }).addDisposableTo(disposeBag) + self.conversationsTableView.rx.itemDeleted.subscribe(onNext: { [unowned self] indexPath in + if let convToDelete: ConversationViewModel = try? self.conversationsTableView.rx.model(at: indexPath) { + self.viewModel.delete(conversationViewModel: convToDelete) + } + }).disposed(by: disposeBag) } func setupSearchBar() { @@ -217,29 +214,29 @@ class SmartlistViewController: UIViewController { //Bind the SearchBar to the ViewModel self.searchBar.rx.text.orEmpty - .debounce(textFieldThrottlingDuration, scheduler: MainScheduler.instance) + .debounce(Durations.textFieldThrottlingDuration.value, scheduler: MainScheduler.instance) .bind(to: self.viewModel.searchBarText) - .addDisposableTo(disposeBag) + .disposed(by: disposeBag) //Show Cancel button self.searchBar.rx.textDidBeginEditing.subscribe(onNext: { [unowned self] in self.searchBar.setShowsCancelButton(true, animated: true) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) //Hide Cancel button self.searchBar.rx.textDidEndEditing.subscribe(onNext: { [unowned self] in self.searchBar.setShowsCancelButton(false, animated: true) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) //Cancel button event self.searchBar.rx.cancelButtonClicked.subscribe(onNext: { [unowned self] in self.cancelSearch() - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) //Search button event self.searchBar.rx.searchButtonClicked.subscribe(onNext: { [unowned self] in self.searchBar.resignFirstResponder() - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) } func cancelSearch() { diff --git a/Ring/Ring/Smartlist/SmartlistViewModel.swift b/Ring/Ring/Smartlist/SmartlistViewModel.swift index 24fead9cf978ead4720c21ae0491a6116cbe0640..7e2b1b0e27d9e9b92a8f338cf8a683866b249425 100644 --- a/Ring/Ring/Smartlist/SmartlistViewModel.swift +++ b/Ring/Ring/Smartlist/SmartlistViewModel.swift @@ -87,13 +87,11 @@ class SmartlistViewModel { var sections = [ConversationSection]() if contactFoundConversation != nil { - let headerTitle = NSLocalizedString("UserFound", tableName: "Smartlist", comment: "") - sections.append(ConversationSection(header: headerTitle, items: [contactFoundConversation!])) + sections.append(ConversationSection(header: L10n.Smartlist.userFound.smartString, items: [contactFoundConversation!])) } if !filteredResults.isEmpty { - let headerTitle = NSLocalizedString("Conversations", tableName: "Smartlist", comment: "") - sections.append(ConversationSection(header: headerTitle, items: filteredResults)) + sections.append(ConversationSection(header: L10n.Smartlist.conversations.smartString, items: filteredResults)) } return sections @@ -112,7 +110,7 @@ class SmartlistViewModel { //Observes search bar text searchBarText.asObservable().observeOn(MainScheduler.instance).subscribe(onNext: { [unowned self] text in self.search(withText: text) - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) //Observe username lookup self.nameService.usernameLookupStatus.observeOn(MainScheduler.instance).subscribe(onNext: { usernameLookupStatus in @@ -136,13 +134,12 @@ class SmartlistViewModel { self.searchStatus.onNext("") } else { if self.filteredResults.value.isEmpty { - let searchStatusText = NSLocalizedString("NoResults", tableName: "Smartlist", comment: "") - self.searchStatus.onNext(searchStatusText) + self.searchStatus.onNext(L10n.Smartlist.noResults.smartString) } else { self.searchStatus.onNext("") } } - }).addDisposableTo(disposeBag) + }).disposed(by: disposeBag) } fileprivate func search(withText text: String) { @@ -167,8 +164,7 @@ class SmartlistViewModel { } self.nameService.lookupName(withAccount: "", nameserver: "", name: text) - let searchStatusText = NSLocalizedString("Searching", tableName: "Smartlist", comment: "") - self.searchStatus.onNext(searchStatusText) + self.searchStatus.onNext(L10n.Smartlist.searching.smartString) } } @@ -180,7 +176,7 @@ class SmartlistViewModel { self.conversationsService .addConversation(conversation: selectedItem.conversation) .subscribe() - .addDisposableTo(disposeBag) + .disposed(by: disposeBag) } } diff --git a/Ring/Ring/Constants/Colors.swift b/Ring/Ring/UI/DesignableButton.swift similarity index 68% rename from Ring/Ring/Constants/Colors.swift rename to Ring/Ring/UI/DesignableButton.swift index 94d112f16a17351c686d89b9c31e274168013e7d..ad771dd494a0522aa7d487f63156f9ede123fcd5 100644 --- a/Ring/Ring/Constants/Colors.swift +++ b/Ring/Ring/UI/DesignableButton.swift @@ -1,7 +1,7 @@ /* - * Copyright (C) 2017 Savoir-faire Linux Inc. + * Copyright (C) 2016 Savoir-faire Linux Inc. * - * Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com> + * Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,9 +20,7 @@ import UIKit -class Colors { - static let ringMainColor = UIColor(colorLiteralRed: 10.0/255.0, - green: 116.0/255.0, - blue: 137.0/255.0, - alpha: 1.0) +@IBDesignable +class DesignableButton: UIButton { + // just to make the UIView+Ring extension IBDesignable } diff --git a/Ring/Ring/Constants/LocalizedStringTableNames.swift b/Ring/Ring/UI/DesignableTextField.swift similarity index 87% rename from Ring/Ring/Constants/LocalizedStringTableNames.swift rename to Ring/Ring/UI/DesignableTextField.swift index 72818c081736bc82d82f339adc6586615243d9e3..7e7f2da2fd8382c95b103d81b78fdaf0bdcdc81a 100644 --- a/Ring/Ring/Constants/LocalizedStringTableNames.swift +++ b/Ring/Ring/UI/DesignableTextField.swift @@ -18,8 +18,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import Foundation +import UIKit -struct LocalizedStringTableNames { - static let walkthrough = "Walkthrough" +@IBDesignable +class DesignableTextField: UITextField { + // just to make the UIView+Ring extension IBDesignable } diff --git a/Ring/Ring/UI/RoundedButton.swift b/Ring/Ring/UI/RoundedButton.swift deleted file mode 100644 index 1f51c4e17745bb707c4b578c061c641488ab1818..0000000000000000000000000000000000000000 --- a/Ring/Ring/UI/RoundedButton.swift +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2016 Savoir-faire Linux Inc. - * - * Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -import UIKit - -class RoundedButton: UIButton { - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - - //Button layout - self.layer.borderColor = self.backgroundColor?.cgColor - self.layer.borderWidth = 1.0 - self.clipsToBounds = true - self.layer.cornerRadius = 15.0 - self.contentEdgeInsets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0) - - //Text colors - self.setTitleColor(UIColor.white, for: .normal) - self.setTitleColor(UIColor.gray, for: .disabled) - } -} diff --git a/Ring/Ring/Walkthrough/Cell/SwitchCell.swift b/Ring/Ring/Walkthrough/Cell/SwitchCell.swift index 2c5214b445fecb16b53e2b6033a1d4f1e37bda08..8f1ebde24acb13a45b87314d1c0af47c38bf9c0a 100644 --- a/Ring/Ring/Walkthrough/Cell/SwitchCell.swift +++ b/Ring/Ring/Walkthrough/Cell/SwitchCell.swift @@ -19,8 +19,9 @@ */ import UIKit +import Reusable -class SwitchCell: UITableViewCell { +class SwitchCell: UITableViewCell, NibReusable { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var registerSwitch: UISwitch! diff --git a/Ring/Ring/Walkthrough/Cell/SwitchCell.xib b/Ring/Ring/Walkthrough/Cell/SwitchCell.xib index a6daceef9c905019dd2ef8c54e2ca1f287ffdf1b..d659bb2c53325fc5be1ffc1775faa04770d6a8d9 100644 --- a/Ring/Ring/Walkthrough/Cell/SwitchCell.xib +++ b/Ring/Ring/Walkthrough/Cell/SwitchCell.xib @@ -1,21 +1,21 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait"> <adaptation id="fullscreen"/> </device> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="SwitchCellId" rowHeight="69" id="Yif-IF-81k" customClass="SwitchCell" customModule="Ring" customModuleProvider="target"> + <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="69" id="Yif-IF-81k" customClass="SwitchCell" customModule="Ring" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="320" height="69"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Yif-IF-81k" id="EOF-e2-nje"> - <rect key="frame" x="0.0" y="0.0" width="320" height="68"/> + <rect key="frame" x="0.0" y="0.0" width="320" height="68.5"/> <autoresizingMask key="autoresizingMask"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Register public username (experimental)" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tA2-KH-hQH"> diff --git a/Ring/Ring/Walkthrough/Cell/TextCell.swift b/Ring/Ring/Walkthrough/Cell/TextCell.swift index 8e4f42f28d80cd7c0fe1bf500d7a51717c38765d..829592bb588ab37d930cda107758e78c1a90ef20 100644 --- a/Ring/Ring/Walkthrough/Cell/TextCell.swift +++ b/Ring/Ring/Walkthrough/Cell/TextCell.swift @@ -19,8 +19,9 @@ */ import UIKit +import Reusable -class TextCell: UITableViewCell { +class TextCell: UITableViewCell, NibReusable { @IBOutlet weak var label: UILabel! diff --git a/Ring/Ring/Walkthrough/Cell/TextCell.xib b/Ring/Ring/Walkthrough/Cell/TextCell.xib index 2501045472de5ab875a2190bf064d5165297d878..720806f0155266b610024447ed05c68d40d95f2b 100644 --- a/Ring/Ring/Walkthrough/Cell/TextCell.xib +++ b/Ring/Ring/Walkthrough/Cell/TextCell.xib @@ -1,21 +1,21 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait"> <adaptation id="fullscreen"/> </device> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="TextCellId" rowHeight="69" id="6fR-7o-uTx" customClass="TextCell" customModule="Ring" customModuleProvider="target"> + <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="69" id="6fR-7o-uTx" customClass="TextCell" customModule="Ring" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="320" height="69"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="6fR-7o-uTx" id="dja-bC-isJ"> - <rect key="frame" x="0.0" y="0.0" width="320" height="68"/> + <rect key="frame" x="0.0" y="0.0" width="320" height="68.5"/> <autoresizingMask key="autoresizingMask"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jFA-AW-xcV"> diff --git a/Ring/Ring/Walkthrough/Cell/TextFieldCell.swift b/Ring/Ring/Walkthrough/Cell/TextFieldCell.swift index 3ca37960d1394f71102987368c745bc8ab37b6be..47f31cdcaeeef31eff551923d5d9f8b33ffdf79f 100644 --- a/Ring/Ring/Walkthrough/Cell/TextFieldCell.swift +++ b/Ring/Ring/Walkthrough/Cell/TextFieldCell.swift @@ -19,8 +19,9 @@ */ import UIKit +import Reusable -class TextFieldCell: UITableViewCell { +class TextFieldCell: UITableViewCell, NibReusable { @IBOutlet weak var textField: UITextField! @IBOutlet weak var errorMessageLabel: UILabel! diff --git a/Ring/Ring/Walkthrough/Cell/TextFieldCell.xib b/Ring/Ring/Walkthrough/Cell/TextFieldCell.xib index 51abaeaefe5bb454d2ec4847467caf62d3396912..56900cfe05aa9f5ca90d93728474b3d8e02567d2 100644 --- a/Ring/Ring/Walkthrough/Cell/TextFieldCell.xib +++ b/Ring/Ring/Walkthrough/Cell/TextFieldCell.xib @@ -1,24 +1,24 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait"> <adaptation id="fullscreen"/> </device> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="TextFieldCellId" rowHeight="80" id="U14-3B-dOe" customClass="TextFieldCell" customModule="Ring" customModuleProvider="target"> + <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="80" id="U14-3B-dOe" customClass="TextFieldCell" customModule="Ring" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="320" height="80"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="U14-3B-dOe" id="4gJ-Ji-yr3"> - <rect key="frame" x="0.0" y="0.0" width="320" height="79"/> + <rect key="frame" x="0.0" y="0.0" width="320" height="79.5"/> <autoresizingMask key="autoresizingMask"/> <subviews> - <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="let-RU-Vya" customClass="RoundedTextField" customModule="Ring" customModuleProvider="target"> + <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="let-RU-Vya" customClass="DesignableTextField" customModule="Ring" customModuleProvider="target"> <rect key="frame" x="16" y="8" width="288" height="42"/> <nil key="textColor"/> <fontDescription key="fontDescription" type="system" pointSize="14"/> diff --git a/Ring/Ring/Walkthrough/CreateRingAccountViewController.swift b/Ring/Ring/Walkthrough/CreateRingAccountViewController.swift index 47584db7ff670c049c730a8e2f3daa7ef2023362..4f60f33ed8e5ffb3f1b3f9cb639f80c120775915 100644 --- a/Ring/Ring/Walkthrough/CreateRingAccountViewController.swift +++ b/Ring/Ring/Walkthrough/CreateRingAccountViewController.swift @@ -39,21 +39,13 @@ class CreateRingAccountViewController: UITableViewController { */ private let log = SwiftyBeaver.self - var mAccountViewModel = CreateRingAccountViewModel(withAccountService: AppDelegate.accountService, + var accountViewModel = CreateRingAccountViewModel(withAccountService: AppDelegate.accountService, nameService: AppDelegate.nameService) - @IBOutlet weak var mCreateAccountButton: RoundedButton! - @IBOutlet weak var mCreateAccountTitleLabel: UILabel! + @IBOutlet weak var createAccountButton: DesignableButton! + @IBOutlet weak var createAccountTitleLabel: UILabel! - /** - Cell identifiers - */ - - let mSwitchCellId = "SwitchCellId" - let mTextFieldCellId = "TextFieldCellId" - let mTextCellId = "TextCellId" - - var mDisposeBag = DisposeBag() + var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() @@ -66,15 +58,9 @@ class CreateRingAccountViewController: UITableViewController { } func registerCells() { - self.tableView.register(UINib.init(nibName: "SwitchCell", bundle: nil), - forCellReuseIdentifier: mSwitchCellId) - - self.tableView.register(UINib.init(nibName: "TextFieldCell", bundle: nil), - forCellReuseIdentifier: mTextFieldCellId) - - self.tableView.register(UINib.init(nibName: "TextCell", bundle: nil), - forCellReuseIdentifier: mTextCellId) - + self.tableView.register(cellType: SwitchCell.self) + self.tableView.register(cellType: TextFieldCell.self) + self.tableView.register(cellType: TextCell.self) } /** @@ -84,17 +70,17 @@ class CreateRingAccountViewController: UITableViewController { fileprivate func bindViews() { //Add Account button action - self.mCreateAccountButton + self.createAccountButton .rx .tap .takeUntil(self.rx.deallocated) .subscribe(onNext: { - self.mAccountViewModel.createAccount() + self.accountViewModel.createAccount() }) - .addDisposableTo(self.mDisposeBag) + .disposed(by: self.disposeBag) //Add Account Registration state - self.mAccountViewModel.accountCreationState.observeOn(MainScheduler.instance).subscribe( + self.accountViewModel.accountCreationState.observeOn(MainScheduler.instance).subscribe( onNext: { [unowned self] state in switch state { case .started: @@ -112,18 +98,18 @@ class CreateRingAccountViewController: UITableViewController { self.showErrorAlert(error) } self.setCreateAccountAsIdle() - }).addDisposableTo(mDisposeBag) + }).disposed(by: disposeBag) //Show or hide user name field - self.mAccountViewModel.registerUsername.asObservable() + self.accountViewModel.registerUsername.asObservable() .subscribe(onNext: { [weak self] showUsernameField in self?.toggleRegisterSwitch(showUsernameField) - }).addDisposableTo(mDisposeBag) + }).disposed(by: disposeBag) //Enables create account button - self.mAccountViewModel.canCreateAccount - .bind(to: self.mCreateAccountButton.rx.isEnabled) - .addDisposableTo(mDisposeBag) + self.accountViewModel.canCreateAccount + .bind(to: self.createAccountButton.rx.isEnabled) + .disposed(by: disposeBag) } /** @@ -133,43 +119,31 @@ class CreateRingAccountViewController: UITableViewController { fileprivate func setupUI() { self.tableView.estimatedRowHeight = 44.0 self.tableView.rowHeight = UITableViewAutomaticDimension - - self.mCreateAccountTitleLabel.text = NSLocalizedString("CreateAccountFormTitle", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") + self.createAccountTitleLabel.text = L10n.Createaccount.createAccountFormTitle.smartString } fileprivate func setCreateAccountAsLoading() { log.debug("Creating account...") - self.mCreateAccountButton.setTitle("Loading...", for: .normal) - self.mCreateAccountButton.isUserInteractionEnabled = false - - let title = NSLocalizedString("WaitCreateAccountTitle", - tableName:LocalizedStringTableNames.walkthrough, - comment: "") - - HUD.show(.labeledProgress(title: title, subtitle: nil)) + self.createAccountButton.setTitle(L10n.Createaccount.loading.smartString, for: .normal) + self.createAccountButton.isUserInteractionEnabled = false + HUD.show(.labeledProgress(title: L10n.Createaccount.waitCreateAccountTitle.smartString, subtitle: nil)) } fileprivate func setCreateAccountAsIdle() { - self.mCreateAccountButton.setTitle("Create a Ring account", for: .normal) - self.mCreateAccountButton.isUserInteractionEnabled = true + self.createAccountButton.setTitle(L10n.Welcome.createAccount.smartString, for: .normal) + self.createAccountButton.isUserInteractionEnabled = true HUD.hide() } fileprivate func showDeviceAddedAlert() { - let title = NSLocalizedString("AccountAddedTitle", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - - HUD.flash(.labeledSuccess(title: title, subtitle: nil), delay: alertFlashDuration) + HUD.flash(.labeledSuccess(title: L10n.Alerts.accountAddedTitle.smartString, subtitle: nil), delay: Durations.alertFlashDuration.value) } fileprivate func showErrorAlert(_ error: AccountCreationError) { let alert = UIAlertController.init(title: error.title, message: error.message, preferredStyle: .alert) - alert.addAction(UIAlertAction.init(title: "OK", style: .default, handler: nil)) + alert.addAction(UIAlertAction.init(title: L10n.Global.ok.smartString, style: .default, handler: nil)) self.present(alert, animated: true, completion: nil) } @@ -181,12 +155,12 @@ class CreateRingAccountViewController: UITableViewController { let usernameFieldCellIndex = 1 - if show && !mCells.contains(.usernameField) { - self.mCells.insert(.usernameField, at: usernameFieldCellIndex) + if show && !cells.contains(.usernameField) { + self.cells.insert(.usernameField, at: usernameFieldCellIndex) self.tableView.insertRows(at: [IndexPath(row: usernameFieldCellIndex, section: 0)], with: .automatic) - } else if !show && mCells.contains(.usernameField) { - self.mCells.remove(at: usernameFieldCellIndex) + } else if !show && cells.contains(.usernameField) { + self.cells.remove(at: usernameFieldCellIndex) self.tableView.deleteRows(at: [IndexPath(row: usernameFieldCellIndex, section: 0)], with: .automatic) } @@ -194,7 +168,7 @@ class CreateRingAccountViewController: UITableViewController { } // MARK: TableView datasource - fileprivate var mCells: [CreateRingAccountCellType] = [.registerPublicUsername, + fileprivate var cells: [CreateRingAccountCellType] = [.registerPublicUsername, .passwordNotice, .newPasswordField, .repeatPasswordField] @@ -204,128 +178,84 @@ class CreateRingAccountViewController: UITableViewController { } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return mCells.count + return cells.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let currentCellType = mCells[indexPath.row] + let currentCellType = cells[indexPath.row] if currentCellType == .registerPublicUsername { - if let cell = tableView.dequeueReusableCell(withIdentifier: mSwitchCellId, - for: indexPath) as? SwitchCell { - cell.titleLabel.text = NSLocalizedString("RegisterPublicUsername", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - cell.titleLabel.textColor = .white - - _ = cell.registerSwitch.rx.value.bind(to: self.mAccountViewModel.registerUsername) - .addDisposableTo(mDisposeBag) - - return cell - } else { - return tableView.dequeueReusableCell(withIdentifier: mSwitchCellId, for: indexPath) - } + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: SwitchCell.self) + cell.titleLabel.text = L10n.Createaccount.registerPublicUsername.smartString + cell.titleLabel.textColor = .white + cell.registerSwitch.rx.value.bind(to: self.accountViewModel.registerUsername).disposed(by: disposeBag) + return cell } else if currentCellType == .usernameField { - if let cell = tableView.dequeueReusableCell(withIdentifier: mTextFieldCellId, - for: indexPath) as? TextFieldCell { - cell.textField.isSecureTextEntry = false - cell.textField.placeholder = NSLocalizedString("EnterNewUsernamePlaceholder", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - - //Binds the username field value to the ViewModel - _ = cell.textField.rx.text.orEmpty - .throttle(textFieldThrottlingDuration, scheduler: MainScheduler.instance) - .distinctUntilChanged() - .bind(to: self.mAccountViewModel.username) - .addDisposableTo(mDisposeBag) - - //Switch to new password cell when return button is touched - _ = cell.textField.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: { - self.switchToCell(withType: .newPasswordField) - }).addDisposableTo(mDisposeBag) - - _ = self.mAccountViewModel.usernameValidationMessage - .bind(to: cell.errorMessageLabel.rx.text) - .addDisposableTo(mDisposeBag) - - return cell - } else { - return tableView.dequeueReusableCell(withIdentifier: mTextFieldCellId, for: indexPath) - } + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: TextFieldCell.self) - } else if currentCellType == .passwordNotice { - if let cell = tableView.dequeueReusableCell(withIdentifier: mTextCellId, for: indexPath) as? TextCell { - cell.label.text = NSLocalizedString("ChooseStrongPassword", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - return cell - } else { - return tableView.dequeueReusableCell(withIdentifier: mTextCellId, for: indexPath) - } + cell.textField.isSecureTextEntry = false + cell.textField.placeholder = L10n.Createaccount.enterNewUsernamePlaceholder.smartString + //Binds the username field value to the ViewModel + cell.textField.rx.text.orEmpty + .throttle(Durations.textFieldThrottlingDuration.value, scheduler: MainScheduler.instance) + .distinctUntilChanged() + .bind(to: self.accountViewModel.username) + .disposed(by: disposeBag) + + //Switch to new password cell when return button is touched + cell.textField.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: { + self.switchToCell(withType: .newPasswordField) + }).disposed(by: disposeBag) + + self.accountViewModel.usernameValidationMessage.bind(to: cell.errorMessageLabel.rx.text).disposed(by: disposeBag) + return cell + } else if currentCellType == .passwordNotice { + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: TextCell.self) + cell.label.text = L10n.Createaccount.chooseStrongPassword.smartString + return cell } else if currentCellType == .newPasswordField { - if let cell = tableView.dequeueReusableCell(withIdentifier: mTextFieldCellId, - for: indexPath) as? TextFieldCell { - cell.textField.isSecureTextEntry = true - cell.textField.placeholder = NSLocalizedString("NewPasswordPlaceholder", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - - cell.errorMessageLabel.text = NSLocalizedString("PasswordCharactersNumberError", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - - //Binds the password field value to the ViewModel - _ = cell.textField.rx.text.orEmpty.bind(to: self.mAccountViewModel.password) - .addDisposableTo(mDisposeBag) - - //Binds the observer to show the error label if the field is not empty - _ = self.mAccountViewModel.hidePasswordError.bind(to: cell.errorMessageLabel.rx.isHidden) - .addDisposableTo(mDisposeBag) - - //Switch to the repeat pasword cell when return button is touched - _ = cell.textField.rx.controlEvent(.editingDidEndOnExit) - .subscribe(onNext: { - self.switchToCell(withType: .repeatPasswordField) - }).addDisposableTo(mDisposeBag) - - return cell - } else { - return tableView.dequeueReusableCell(withIdentifier: mTextFieldCellId, for: indexPath) - } + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: TextFieldCell.self) + + cell.textField.isSecureTextEntry = true + cell.textField.placeholder = L10n.Createaccount.newPasswordPlaceholder.smartString + cell.errorMessageLabel.text = L10n.Createaccount.passwordCharactersNumberError.smartString + + //Binds the password field value to the ViewModel + cell.textField.rx.text.orEmpty.bind(to: self.accountViewModel.password).disposed(by: disposeBag) + + //Binds the observer to show the error label if the field is not empty + self.accountViewModel.hidePasswordError.bind(to: cell.errorMessageLabel.rx.isHidden).disposed(by: disposeBag) + //Switch to the repeat pasword cell when return button is touched + cell.textField.rx.controlEvent(.editingDidEndOnExit) + .subscribe(onNext: { + self.switchToCell(withType: .repeatPasswordField) + }).disposed(by: disposeBag) + + return cell } else { - if let cell = tableView.dequeueReusableCell(withIdentifier: mTextFieldCellId, - for: indexPath) as? TextFieldCell { - cell.textField.isSecureTextEntry = true - cell.textField.placeholder = NSLocalizedString("RepeatPasswordPlaceholder", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - - cell.errorMessageLabel.text = NSLocalizedString("PasswordNotMatchingError", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - - //Binds the repeat password field value to the ViewModel - _ = cell.textField.rx.text.orEmpty.bind(to: self.mAccountViewModel.repeatPassword) - .addDisposableTo(mDisposeBag) - - //Binds the observer to the text field 'hidden' property - _ = self.mAccountViewModel.hideRepeatPasswordError.bind(to: cell.errorMessageLabel.rx.isHidden) - .addDisposableTo(mDisposeBag) - - return cell - } else { - return tableView.dequeueReusableCell(withIdentifier: mTextFieldCellId, for: indexPath) - } + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: TextFieldCell.self) + + cell.textField.isSecureTextEntry = true + cell.textField.placeholder = L10n.Createaccount.repeatPasswordPlaceholder.smartString + cell.errorMessageLabel.text = L10n.Createaccount.passwordNotMatchingError.smartString + + //Binds the repeat password field value to the ViewModel + cell.textField.rx.text.orEmpty.bind(to: self.accountViewModel.repeatPassword).disposed(by: disposeBag) + + //Binds the observer to the text field 'hidden' property + self.accountViewModel.hideRepeatPasswordError.bind(to: cell.errorMessageLabel.rx.isHidden).disposed(by: disposeBag) + + return cell + } } fileprivate func switchToCell(withType cellType: CreateRingAccountCellType) { - if let cellIndex = self.mCells.index(of: cellType) { + if let cellIndex = self.cells.index(of: cellType) { if let cell = tableView.cellForRow(at: IndexPath(row: cellIndex, section: 0)) as? TextFieldCell { cell.textField.becomeFirstResponder() diff --git a/Ring/Ring/Walkthrough/WalkthroughStoryboard.storyboard b/Ring/Ring/Walkthrough/WalkthroughStoryboard.storyboard index 80b5584b313c18bbe9ec9809ebff256662744d8b..b56028fd294fbd011458471f2c1156b7b7f3d79d 100644 --- a/Ring/Ring/Walkthrough/WalkthroughStoryboard.storyboard +++ b/Ring/Ring/Walkthrough/WalkthroughStoryboard.storyboard @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="GnB-zf-djy"> +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="GnB-zf-djy"> <device id="retina3_5" orientation="portrait"> <adaptation id="fullscreen"/> </device> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <scenes> @@ -42,7 +42,7 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="DXu-A1-gcl"> - <rect key="frame" x="25" y="100" width="271" height="280"/> + <rect key="frame" x="25" y="90" width="271" height="300"/> <subviews> <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" verticalHuggingPriority="251" image="logo-ring-beta2-blanc" translatesAutoresizingMaskIntoConstraints="NO" id="7CK-fT-m09"> <rect key="frame" x="45.5" y="0.0" width="180" height="66"/> @@ -64,28 +64,34 @@ <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/> <nil key="highlightedColor"/> </label> - <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="R0q-9u-3WR" customClass="RoundedButton" customModule="Ring" customModuleProvider="target"> - <rect key="frame" x="33.5" y="204" width="204" height="30"/> + <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="R0q-9u-3WR" customClass="DesignableButton" customModule="Ring" customModuleProvider="target"> + <rect key="frame" x="20" y="204" width="231" height="40"/> <color key="backgroundColor" red="0.0" green="0.29803921570000003" blue="0.37647058820000001" alpha="1" colorSpace="calibratedRGB"/> <constraints> - <constraint firstAttribute="height" constant="30" id="d0X-Cl-Ry4"/> + <constraint firstAttribute="height" constant="40" id="d0X-Cl-Ry4"/> </constraints> <state key="normal" title="Link this device to an account"> <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/> </state> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/> + </userDefinedRuntimeAttributes> <connections> <action selector="linkDeviceToAccountAction:" destination="zOM-us-BHp" eventType="touchUpInside" id="bul-x6-xBK"/> </connections> </button> - <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8Ve-ZD-TXf" customClass="RoundedButton" customModule="Ring" customModuleProvider="target"> - <rect key="frame" x="33.5" y="250" width="204" height="30"/> + <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8Ve-ZD-TXf" customClass="DesignableButton" customModule="Ring" customModuleProvider="target"> + <rect key="frame" x="20" y="260" width="231" height="40"/> <color key="backgroundColor" red="0.0" green="0.29803921570000003" blue="0.37647058820000001" alpha="1" colorSpace="calibratedRGB"/> <constraints> - <constraint firstAttribute="height" constant="30" id="5Qb-T3-Su0"/> + <constraint firstAttribute="height" constant="40" id="5Qb-T3-Su0"/> </constraints> <state key="normal" title="Create a Ring account"> <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/> </state> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/> + </userDefinedRuntimeAttributes> <connections> <action selector="createAccountAction:" destination="zOM-us-BHp" eventType="touchUpInside" id="o0k-MC-tsv"/> </connections> @@ -93,9 +99,9 @@ </subviews> <constraints> <constraint firstItem="8Ve-ZD-TXf" firstAttribute="leading" secondItem="R0q-9u-3WR" secondAttribute="leading" id="U0b-w9-yjf"/> - <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="R0q-9u-3WR" secondAttribute="trailing" constant="32" id="WpG-GI-pPu"/> + <constraint firstAttribute="trailing" secondItem="R0q-9u-3WR" secondAttribute="trailing" constant="20" id="WpG-GI-pPu"/> <constraint firstItem="8Ve-ZD-TXf" firstAttribute="trailing" secondItem="R0q-9u-3WR" secondAttribute="trailing" id="bE8-0O-Ead"/> - <constraint firstItem="R0q-9u-3WR" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="DXu-A1-gcl" secondAttribute="leading" constant="32" id="yWE-sO-6tc"/> + <constraint firstItem="R0q-9u-3WR" firstAttribute="leading" secondItem="DXu-A1-gcl" secondAttribute="leading" constant="20" id="yWE-sO-6tc"/> </constraints> </stackView> </subviews> @@ -135,12 +141,19 @@ <rect key="frame" x="0.0" y="0.0" width="320" height="480"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8M5-AJ-pxH" customClass="RoundedButton" customModule="Ring" customModuleProvider="target"> - <rect key="frame" x="145" y="225" width="30" height="30"/> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8M5-AJ-pxH" customClass="DesignableButton" customModule="Ring" customModuleProvider="target"> + <rect key="frame" x="110" y="220" width="100" height="40"/> <color key="backgroundColor" red="0.0" green="0.29803921570000003" blue="0.37647058820000001" alpha="1" colorSpace="calibratedRGB"/> + <constraints> + <constraint firstAttribute="width" constant="100" id="2t1-a8-dxY"/> + <constraint firstAttribute="height" constant="40" id="cln-HK-ZE8"/> + </constraints> <state key="normal" title="Skip"> <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/> </state> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/> + </userDefinedRuntimeAttributes> <connections> <action selector="skip:" destination="dmb-i6-bo9" eventType="touchUpInside" id="PAR-5w-VRm"/> </connections> @@ -192,22 +205,25 @@ <rect key="frame" x="0.0" y="130" width="320" height="54"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="UQJ-hF-j2O" customClass="RoundedButton" customModule="Ring" customModuleProvider="target"> - <rect key="frame" x="120.5" y="8" width="79" height="30"/> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="UQJ-hF-j2O" customClass="DesignableButton" customModule="Ring" customModuleProvider="target"> + <rect key="frame" x="110" y="7" width="100" height="40"/> <color key="backgroundColor" red="0.0" green="0.29803921570000003" blue="0.37647058820000001" alpha="1" colorSpace="calibratedRGB"/> <constraints> - <constraint firstAttribute="height" constant="30" id="RGF-Ib-dT9"/> + <constraint firstAttribute="height" constant="40" id="RGF-Ib-dT9"/> + <constraint firstAttribute="width" constant="100" id="p9O-j8-fcB"/> </constraints> <state key="normal" title="Link Device"> <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/> </state> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/> + </userDefinedRuntimeAttributes> </button> </subviews> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> <constraints> <constraint firstItem="UQJ-hF-j2O" firstAttribute="centerX" secondItem="bQ2-6Y-CIW" secondAttribute="centerX" id="7LD-pW-9BH"/> - <constraint firstItem="UQJ-hF-j2O" firstAttribute="top" secondItem="bQ2-6Y-CIW" secondAttribute="top" constant="8" id="FY8-ef-pse"/> - <constraint firstAttribute="bottom" secondItem="UQJ-hF-j2O" secondAttribute="bottom" constant="16" id="pmA-aY-Do4"/> + <constraint firstItem="UQJ-hF-j2O" firstAttribute="centerY" secondItem="bQ2-6Y-CIW" secondAttribute="centerY" id="f6F-dX-f0m"/> </constraints> </view> <connections> @@ -251,21 +267,25 @@ <rect key="frame" x="0.0" y="130" width="320" height="54"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gsT-SB-0AP" customClass="RoundedButton" customModule="Ring" customModuleProvider="target"> - <rect key="frame" x="84" y="8" width="152" height="30"/> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gsT-SB-0AP" customClass="DesignableButton" customModule="Ring" customModuleProvider="target"> + <rect key="frame" x="70" y="7" width="180" height="40"/> <color key="backgroundColor" red="0.0" green="0.29803921570000003" blue="0.37647058820000001" alpha="1" colorSpace="calibratedRGB"/> <constraints> - <constraint firstAttribute="height" constant="30" id="KVf-AW-ms7"/> + <constraint firstAttribute="height" constant="40" id="b0c-dD-zDx"/> </constraints> <state key="normal" title="Create a Ring account"> <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/> </state> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/> + </userDefinedRuntimeAttributes> </button> </subviews> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> <constraints> - <constraint firstItem="gsT-SB-0AP" firstAttribute="top" secondItem="MnN-De-arJ" secondAttribute="top" constant="8" id="pqs-B0-TWU"/> - <constraint firstAttribute="bottom" secondItem="gsT-SB-0AP" secondAttribute="bottom" constant="16" id="qOI-R4-9EJ"/> + <constraint firstAttribute="trailing" secondItem="gsT-SB-0AP" secondAttribute="trailing" constant="70" id="MsB-8o-KHc"/> + <constraint firstItem="gsT-SB-0AP" firstAttribute="leading" secondItem="MnN-De-arJ" secondAttribute="leading" constant="70" id="PjH-ny-nr4"/> + <constraint firstItem="gsT-SB-0AP" firstAttribute="centerY" secondItem="MnN-De-arJ" secondAttribute="centerY" id="gt1-U3-e6s"/> <constraint firstItem="gsT-SB-0AP" firstAttribute="centerX" secondItem="MnN-De-arJ" secondAttribute="centerX" id="tMA-Qg-oxa"/> </constraints> </view> @@ -275,14 +295,14 @@ </connections> </tableView> <connections> - <outlet property="mCreateAccountButton" destination="gsT-SB-0AP" id="9Fj-aJ-0Ik"/> - <outlet property="mCreateAccountTitleLabel" destination="AIX-eC-baN" id="YUL-yb-Ecy"/> + <outlet property="createAccountButton" destination="gsT-SB-0AP" id="SxP-Y3-eoo"/> + <outlet property="createAccountTitleLabel" destination="AIX-eC-baN" id="kg4-gi-v7Q"/> <segue destination="E3W-r7-J4y" kind="show" identifier="AccountToPermissionsSegue" id="Zig-iC-h9U"/> </connections> </tableViewController> <placeholder placeholderIdentifier="IBFirstResponder" id="W0G-TV-Z9c" userLabel="First Responder" sceneMemberID="firstResponder"/> </objects> - <point key="canvasLocation" x="2078" y="581"/> + <point key="canvasLocation" x="2077.5" y="580"/> </scene> <!--View Controller--> <scene sceneID="c8H-6M-3dO"> diff --git a/Ring/Ring/Walkthrough/WelcomeViewController.swift b/Ring/Ring/Walkthrough/WelcomeViewController.swift index 5f6c478499a9f5a65095070344d566b9c93ab332..29892dd40a8b232f039aa12b601402a16c878aa2 100644 --- a/Ring/Ring/Walkthrough/WelcomeViewController.swift +++ b/Ring/Ring/Walkthrough/WelcomeViewController.swift @@ -24,8 +24,8 @@ class WelcomeViewController: UIViewController { @IBOutlet weak var welcomeLabel: UILabel! @IBOutlet weak var descriptionLabel: UILabel! - @IBOutlet weak var linkDeviceButton: RoundedButton! - @IBOutlet weak var createAccountButton: RoundedButton! + @IBOutlet weak var linkDeviceButton: DesignableButton! + @IBOutlet weak var createAccountButton: DesignableButton! let createProfileSegueIdentifier = "CreateProfileSegue" let linkDeviceToAccountSegueIdentifier = "LinkDeviceToAccountSegue" @@ -74,17 +74,9 @@ class WelcomeViewController: UIViewController { func setupUI() { - self.welcomeLabel.text = NSLocalizedString("WelcomeTitle", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - self.descriptionLabel.text = NSLocalizedString("WelcomeText", - tableName: LocalizedStringTableNames.walkthrough, - comment: "") - self.linkDeviceButton.setTitle(NSLocalizedString("LinkDeviceButton", - tableName: LocalizedStringTableNames.walkthrough, - comment: ""), for: .normal) - self.createAccountButton.setTitle(NSLocalizedString("CreateAccount", - tableName: LocalizedStringTableNames.walkthrough, - comment: ""), for: .normal) + self.welcomeLabel.text = L10n.Welcome.title.smartString + self.descriptionLabel.text = L10n.Welcome.text.smartString + self.linkDeviceButton.setTitle(L10n.Welcome.linkDeviceButton.smartString, for: .normal) + self.createAccountButton.setTitle(L10n.Welcome.createAccount.smartString, for: .normal) } }