chore: make onboarding ready
This commit is contained in:
parent
24fc57d982
commit
7aa45ff230
|
@ -22,8 +22,8 @@
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"take_photo": "Take photo",
|
"take_photo": "Take photo",
|
||||||
"save_photo": "Save photo",
|
"save_photo": "Save photo",
|
||||||
"sign_in": "Sign in",
|
"sign_in": "Sign In",
|
||||||
"sign_up": "Sign up",
|
"sign_up": "Sign Up",
|
||||||
"see_more": "See More",
|
"see_more": "See More",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"open_in_safari": "Open in Safari"
|
"open_in_safari": "Open in Safari"
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
"dont_receive_email": {
|
"dont_receive_email": {
|
||||||
"title": "Check your email",
|
"title": "Check your email",
|
||||||
"description": "Check if your email address is correct as well as your junk folder if you haven’t.",
|
"description": "Check if your email address is correct as well as your junk folder if you haven’t.",
|
||||||
"resend_email": "Resend email"
|
"resend_email": "Resend Email"
|
||||||
},
|
},
|
||||||
"open_email_app": {
|
"open_email_app": {
|
||||||
"title": "Check your inbox.",
|
"title": "Check your inbox.",
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
0FAA0FDF25E0B57E0017CCDE /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA0FDE25E0B57E0017CCDE /* WelcomeViewController.swift */; };
|
0FAA0FDF25E0B57E0017CCDE /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA0FDE25E0B57E0017CCDE /* WelcomeViewController.swift */; };
|
||||||
0FAA101225E105390017CCDE /* PrimaryActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA101125E105390017CCDE /* PrimaryActionButton.swift */; };
|
0FAA101225E105390017CCDE /* PrimaryActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA101125E105390017CCDE /* PrimaryActionButton.swift */; };
|
||||||
0FAA101C25E10E760017CCDE /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA101B25E10E760017CCDE /* UIFont.swift */; };
|
0FAA101C25E10E760017CCDE /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA101B25E10E760017CCDE /* UIFont.swift */; };
|
||||||
0FAA102725E1126A0017CCDE /* PickServerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA102625E1126A0017CCDE /* PickServerViewController.swift */; };
|
0FAA102725E1126A0017CCDE /* MastodonPickServerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */; };
|
||||||
0FB3D2F725E4C24D00AAD544 /* PickServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2F625E4C24D00AAD544 /* PickServerViewModel.swift */; };
|
0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */; };
|
||||||
0FB3D2FE25E4CB6400AAD544 /* PickServerTitleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2FD25E4CB6400AAD544 /* PickServerTitleCell.swift */; };
|
0FB3D2FE25E4CB6400AAD544 /* PickServerTitleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2FD25E4CB6400AAD544 /* PickServerTitleCell.swift */; };
|
||||||
0FB3D30825E524C600AAD544 /* PickServerCategoriesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D30725E524C600AAD544 /* PickServerCategoriesCell.swift */; };
|
0FB3D30825E524C600AAD544 /* PickServerCategoriesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D30725E524C600AAD544 /* PickServerCategoriesCell.swift */; };
|
||||||
0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */; };
|
0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */; };
|
||||||
|
@ -83,7 +83,6 @@
|
||||||
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; };
|
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; };
|
||||||
5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */; };
|
5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */; };
|
||||||
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
|
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
|
||||||
DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB01409525C40B6700F9F3CF /* AuthenticationViewController.swift */; };
|
|
||||||
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */; };
|
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */; };
|
||||||
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */; };
|
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */; };
|
||||||
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */; };
|
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */; };
|
||||||
|
@ -95,6 +94,7 @@
|
||||||
DB118A8C25E4BFB500FAB162 /* HighlightDimmableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */; };
|
DB118A8C25E4BFB500FAB162 /* HighlightDimmableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */; };
|
||||||
DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB2B3ABE25E37E15007045F9 /* InfoPlist.strings */; };
|
DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB2B3ABE25E37E15007045F9 /* InfoPlist.strings */; };
|
||||||
DB2B3AE925E38850007045F9 /* UIViewPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2B3AE825E38850007045F9 /* UIViewPreview.swift */; };
|
DB2B3AE925E38850007045F9 /* UIViewPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2B3AE825E38850007045F9 /* UIViewPreview.swift */; };
|
||||||
|
DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */; };
|
||||||
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* AlamofireImage */; };
|
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* AlamofireImage */; };
|
||||||
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; };
|
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; };
|
||||||
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DD525BAA00100D1B89D /* AppDelegate.swift */; };
|
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DD525BAA00100D1B89D /* AppDelegate.swift */; };
|
||||||
|
@ -118,6 +118,7 @@
|
||||||
DB5086B825CC0D6400C2C187 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DB5086B725CC0D6400C2C187 /* Kingfisher */; };
|
DB5086B825CC0D6400C2C187 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DB5086B725CC0D6400C2C187 /* Kingfisher */; };
|
||||||
DB5086BE25CC0D9900C2C187 /* SplashPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */; };
|
DB5086BE25CC0D9900C2C187 /* SplashPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */; };
|
||||||
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */; };
|
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */; };
|
||||||
|
DB68A04A25E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68A04925E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift */; };
|
||||||
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; };
|
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; };
|
||||||
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; };
|
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; };
|
||||||
DB89B9F725C10FD0008580ED /* CoreDataStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; };
|
DB89B9F725C10FD0008580ED /* CoreDataStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; };
|
||||||
|
@ -142,7 +143,6 @@
|
||||||
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */; };
|
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */; };
|
||||||
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; };
|
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; };
|
||||||
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */; };
|
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */; };
|
||||||
DB98334725C8056600AD9700 /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */; };
|
|
||||||
DB98336B25C9420100AD9700 /* APIService+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98336A25C9420100AD9700 /* APIService+App.swift */; };
|
DB98336B25C9420100AD9700 /* APIService+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98336A25C9420100AD9700 /* APIService+App.swift */; };
|
||||||
DB98337125C9443200AD9700 /* APIService+Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337025C9443200AD9700 /* APIService+Authentication.swift */; };
|
DB98337125C9443200AD9700 /* APIService+Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337025C9443200AD9700 /* APIService+Authentication.swift */; };
|
||||||
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337E25C9452D00AD9700 /* APIService+APIError.swift */; };
|
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337E25C9452D00AD9700 /* APIService+APIError.swift */; };
|
||||||
|
@ -217,8 +217,8 @@
|
||||||
0FAA0FDE25E0B57E0017CCDE /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = "<group>"; };
|
0FAA0FDE25E0B57E0017CCDE /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = "<group>"; };
|
||||||
0FAA101125E105390017CCDE /* PrimaryActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryActionButton.swift; sourceTree = "<group>"; };
|
0FAA101125E105390017CCDE /* PrimaryActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryActionButton.swift; sourceTree = "<group>"; };
|
||||||
0FAA101B25E10E760017CCDE /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; };
|
0FAA101B25E10E760017CCDE /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; };
|
||||||
0FAA102625E1126A0017CCDE /* PickServerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerViewController.swift; sourceTree = "<group>"; };
|
0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewController.swift; sourceTree = "<group>"; };
|
||||||
0FB3D2F625E4C24D00AAD544 /* PickServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerViewModel.swift; sourceTree = "<group>"; };
|
0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewModel.swift; sourceTree = "<group>"; };
|
||||||
0FB3D2FD25E4CB6400AAD544 /* PickServerTitleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerTitleCell.swift; sourceTree = "<group>"; };
|
0FB3D2FD25E4CB6400AAD544 /* PickServerTitleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerTitleCell.swift; sourceTree = "<group>"; };
|
||||||
0FB3D30725E524C600AAD544 /* PickServerCategoriesCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoriesCell.swift; sourceTree = "<group>"; };
|
0FB3D30725E524C600AAD544 /* PickServerCategoriesCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoriesCell.swift; sourceTree = "<group>"; };
|
||||||
0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoryView.swift; sourceTree = "<group>"; };
|
0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoryView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -292,7 +292,6 @@
|
||||||
A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BB482D32A7B9825BF5327C4F /* Pods-Mastodon-MastodonUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.release.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.release.xcconfig"; sourceTree = "<group>"; };
|
BB482D32A7B9825BF5327C4F /* Pods-Mastodon-MastodonUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.release.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
DB01409525C40B6700F9F3CF /* AuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewController.swift; sourceTree = "<group>"; };
|
|
||||||
DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewController.swift; sourceTree = "<group>"; };
|
DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewController.swift; sourceTree = "<group>"; };
|
||||||
DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift; sourceTree = "<group>"; };
|
DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift; sourceTree = "<group>"; };
|
||||||
DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModel.swift; sourceTree = "<group>"; };
|
DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
@ -303,6 +302,7 @@
|
||||||
DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightDimmableButton.swift; sourceTree = "<group>"; };
|
DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightDimmableButton.swift; sourceTree = "<group>"; };
|
||||||
DB2B3ABD25E37E15007045F9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
DB2B3ABD25E37E15007045F9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
DB2B3AE825E38850007045F9 /* UIViewPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewPreview.swift; sourceTree = "<group>"; };
|
DB2B3AE825E38850007045F9 /* UIViewPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewPreview.swift; sourceTree = "<group>"; };
|
||||||
|
DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
|
||||||
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
|
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
|
||||||
DB3D100E25BAA75E00EAA174 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
DB3D100E25BAA75E00EAA174 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
DB427DD225BAA00100D1B89D /* Mastodon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mastodon.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
DB427DD225BAA00100D1B89D /* Mastodon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mastodon.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -331,6 +331,7 @@
|
||||||
DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarConfigurableView.swift; sourceTree = "<group>"; };
|
DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarConfigurableView.swift; sourceTree = "<group>"; };
|
||||||
DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashPreference.swift; sourceTree = "<group>"; };
|
DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashPreference.swift; sourceTree = "<group>"; };
|
||||||
DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSKeyValueObservation.swift; sourceTree = "<group>"; };
|
DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSKeyValueObservation.swift; sourceTree = "<group>"; };
|
||||||
|
DB68A04925E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarkContentStatusBarStyleNavigationController.swift; sourceTree = "<group>"; };
|
||||||
DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = "<group>"; };
|
DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = "<group>"; };
|
||||||
DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = "<group>"; };
|
DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = "<group>"; };
|
||||||
DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreDataStack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreDataStack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -357,7 +358,6 @@
|
||||||
DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = "<group>"; };
|
DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = "<group>"; };
|
||||||
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
|
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
|
||||||
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineIndex.swift; sourceTree = "<group>"; };
|
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineIndex.swift; sourceTree = "<group>"; };
|
||||||
DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
DB98336A25C9420100AD9700 /* APIService+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+App.swift"; sourceTree = "<group>"; };
|
DB98336A25C9420100AD9700 /* APIService+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+App.swift"; sourceTree = "<group>"; };
|
||||||
DB98337025C9443200AD9700 /* APIService+Authentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Authentication.swift"; sourceTree = "<group>"; };
|
DB98337025C9443200AD9700 /* APIService+Authentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Authentication.swift"; sourceTree = "<group>"; };
|
||||||
DB98337E25C9452D00AD9700 /* APIService+APIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+APIError.swift"; sourceTree = "<group>"; };
|
DB98337E25C9452D00AD9700 /* APIService+APIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+APIError.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -445,8 +445,8 @@
|
||||||
0FB3D31825E525DE00AAD544 /* CollectionViewCell */,
|
0FB3D31825E525DE00AAD544 /* CollectionViewCell */,
|
||||||
0FB3D30D25E525C000AAD544 /* View */,
|
0FB3D30D25E525C000AAD544 /* View */,
|
||||||
0FB3D2FC25E4CB4B00AAD544 /* TableViewCell */,
|
0FB3D2FC25E4CB4B00AAD544 /* TableViewCell */,
|
||||||
0FAA102625E1126A0017CCDE /* PickServerViewController.swift */,
|
0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */,
|
||||||
0FB3D2F625E4C24D00AAD544 /* PickServerViewModel.swift */,
|
0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */,
|
||||||
);
|
);
|
||||||
path = PickServer;
|
path = PickServer;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -598,7 +598,6 @@
|
||||||
2D38F1C525CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift */,
|
2D38F1C525CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift */,
|
||||||
2D38F20725CD491300561493 /* DisposeBagCollectable.swift */,
|
2D38F20725CD491300561493 /* DisposeBagCollectable.swift */,
|
||||||
2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */,
|
2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */,
|
||||||
2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */,
|
|
||||||
);
|
);
|
||||||
path = Protocol;
|
path = Protocol;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -636,6 +635,7 @@
|
||||||
2D7631A425C1532200929FB9 /* Share */ = {
|
2D7631A425C1532200929FB9 /* Share */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
DB68A04F25E9028800CFDF14 /* NavigationController */,
|
||||||
DB9D6C2025E502C60051B173 /* ViewModel */,
|
DB9D6C2025E502C60051B173 /* ViewModel */,
|
||||||
2D7631A525C1532D00929FB9 /* View */,
|
2D7631A525C1532D00929FB9 /* View */,
|
||||||
);
|
);
|
||||||
|
@ -695,27 +695,26 @@
|
||||||
DB01409B25C40BB600F9F3CF /* Onboarding */ = {
|
DB01409B25C40BB600F9F3CF /* Onboarding */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
DB68A03825E900CC00CFDF14 /* Share */,
|
||||||
0FAA0FDD25E0B5700017CCDE /* Welcome */,
|
0FAA0FDD25E0B5700017CCDE /* Welcome */,
|
||||||
0FAA102525E1125D0017CCDE /* PickServer */,
|
0FAA102525E1125D0017CCDE /* PickServer */,
|
||||||
|
DB0140A625C40C0900F9F3CF /* PinBasedAuthentication */,
|
||||||
DBE0821A25CD382900FD6BBD /* Register */,
|
DBE0821A25CD382900FD6BBD /* Register */,
|
||||||
DB72602125E36A2500235243 /* ServerRules */,
|
DB72602125E36A2500235243 /* ServerRules */,
|
||||||
2D364F7025E66D5B00204FDC /* ResendEmail */,
|
2D364F7025E66D5B00204FDC /* ResendEmail */,
|
||||||
2D59819925E4A55C000FB903 /* ConfirmEmail */,
|
2D59819925E4A55C000FB903 /* ConfirmEmail */,
|
||||||
DB0140A625C40C0900F9F3CF /* PinBased */,
|
|
||||||
DB01409525C40B6700F9F3CF /* AuthenticationViewController.swift */,
|
|
||||||
DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */,
|
|
||||||
);
|
);
|
||||||
path = Onboarding;
|
path = Onboarding;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
DB0140A625C40C0900F9F3CF /* PinBased */ = {
|
DB0140A625C40C0900F9F3CF /* PinBasedAuthentication */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */,
|
DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */,
|
||||||
DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */,
|
DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */,
|
||||||
DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */,
|
DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */,
|
||||||
);
|
);
|
||||||
path = PinBased;
|
path = PinBasedAuthentication;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
DB084B5125CBC56300F898ED /* CoreDataStack */ = {
|
DB084B5125CBC56300F898ED /* CoreDataStack */ = {
|
||||||
|
@ -856,6 +855,23 @@
|
||||||
path = Preference;
|
path = Preference;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
DB68A03825E900CC00CFDF14 /* Share */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */,
|
||||||
|
DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */,
|
||||||
|
);
|
||||||
|
path = Share;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DB68A04F25E9028800CFDF14 /* NavigationController */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DB68A04925E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift */,
|
||||||
|
);
|
||||||
|
path = NavigationController;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
DB72602125E36A2500235243 /* ServerRules */ = {
|
DB72602125E36A2500235243 /* ServerRules */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1412,7 +1428,7 @@
|
||||||
2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */,
|
2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */,
|
||||||
DB45FB1D25CA9D23005A8AC7 /* APIService+HomeTimeline.swift in Sources */,
|
DB45FB1D25CA9D23005A8AC7 /* APIService+HomeTimeline.swift in Sources */,
|
||||||
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
|
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
|
||||||
0FB3D2F725E4C24D00AAD544 /* PickServerViewModel.swift in Sources */,
|
0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */,
|
||||||
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */,
|
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */,
|
||||||
2D38F1F725CD47AC00561493 /* HomeTimelineViewModel+LoadOldestState.swift in Sources */,
|
2D38F1F725CD47AC00561493 /* HomeTimelineViewModel+LoadOldestState.swift in Sources */,
|
||||||
0FB3D33225E5F50E00AAD544 /* PickServerSearchCell.swift in Sources */,
|
0FB3D33225E5F50E00AAD544 /* PickServerSearchCell.swift in Sources */,
|
||||||
|
@ -1427,7 +1443,7 @@
|
||||||
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */,
|
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */,
|
||||||
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */,
|
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */,
|
||||||
2D42FF8F25C8228A004A627A /* UIButton.swift in Sources */,
|
2D42FF8F25C8228A004A627A /* UIButton.swift in Sources */,
|
||||||
0FAA102725E1126A0017CCDE /* PickServerViewController.swift in Sources */,
|
0FAA102725E1126A0017CCDE /* MastodonPickServerViewController.swift in Sources */,
|
||||||
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */,
|
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */,
|
||||||
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */,
|
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */,
|
||||||
DB45FAE325CA7181005A8AC7 /* MastodonUser.swift in Sources */,
|
DB45FAE325CA7181005A8AC7 /* MastodonUser.swift in Sources */,
|
||||||
|
@ -1467,13 +1483,14 @@
|
||||||
DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */,
|
DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */,
|
||||||
2D38F1C625CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift in Sources */,
|
2D38F1C625CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift in Sources */,
|
||||||
0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */,
|
0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */,
|
||||||
|
DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */,
|
||||||
|
DB68A04A25E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift in Sources */,
|
||||||
0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */,
|
0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */,
|
||||||
2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */,
|
2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */,
|
||||||
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
|
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
|
||||||
DB9D6BF825E4F5690051B173 /* NotificationViewController.swift in Sources */,
|
DB9D6BF825E4F5690051B173 /* NotificationViewController.swift in Sources */,
|
||||||
DB45FADD25CA6F6B005A8AC7 /* APIService+CoreData+MastodonUser.swift in Sources */,
|
DB45FADD25CA6F6B005A8AC7 /* APIService+CoreData+MastodonUser.swift in Sources */,
|
||||||
2D32EABA25CB9B0500C9ED86 /* UIView.swift in Sources */,
|
2D32EABA25CB9B0500C9ED86 /* UIView.swift in Sources */,
|
||||||
DB98334725C8056600AD9700 /* AuthenticationViewModel.swift in Sources */,
|
|
||||||
2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */,
|
2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */,
|
||||||
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */,
|
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */,
|
||||||
2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */,
|
2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */,
|
||||||
|
@ -1504,7 +1521,6 @@
|
||||||
2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */,
|
2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */,
|
||||||
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */,
|
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */,
|
||||||
2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */,
|
2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */,
|
||||||
DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */,
|
|
||||||
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */,
|
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */,
|
||||||
DB5086AB25CC0BBB00C2C187 /* AvatarConfigurableView.swift in Sources */,
|
DB5086AB25CC0BBB00C2C187 /* AvatarConfigurableView.swift in Sources */,
|
||||||
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */,
|
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */,
|
||||||
|
|
|
@ -7,12 +7,17 @@
|
||||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>9</integer>
|
<integer>11</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Mastodon - RTL.xcscheme_^#shared#^_</key>
|
<key>Mastodon - RTL.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>8</integer>
|
<integer>9</integer>
|
||||||
|
</dict>
|
||||||
|
<key>Mastodon - Release.xcscheme_^#shared#^_</key>
|
||||||
|
<dict>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>1</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Mastodon.xcscheme_^#shared#^_</key>
|
<key>Mastodon.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
|
|
@ -38,44 +38,61 @@ extension SceneCoordinator {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Scene {
|
enum Scene {
|
||||||
|
// onboarding
|
||||||
case welcome
|
case welcome
|
||||||
case pickServer(viewMode: PickServerViewModel)
|
case mastodonPickServer(viewMode: MastodonPickServerViewModel)
|
||||||
case authentication(viewModel: AuthenticationViewModel)
|
|
||||||
case mastodonPinBasedAuthentication(viewModel: MastodonPinBasedAuthenticationViewModel)
|
case mastodonPinBasedAuthentication(viewModel: MastodonPinBasedAuthenticationViewModel)
|
||||||
case mastodonRegister(viewModel: MastodonRegisterViewModel)
|
case mastodonRegister(viewModel: MastodonRegisterViewModel)
|
||||||
case mastodonServerRules(viewModel: MastodonServerRulesViewModel)
|
case mastodonServerRules(viewModel: MastodonServerRulesViewModel)
|
||||||
case mastodonConfirmEmail(viewModel: MastodonConfirmEmailViewModel)
|
case mastodonConfirmEmail(viewModel: MastodonConfirmEmailViewModel)
|
||||||
case mastodonResendEmail(viewModel: MastodonResendEmailViewModel)
|
case mastodonResendEmail(viewModel: MastodonResendEmailViewModel)
|
||||||
|
|
||||||
|
// misc
|
||||||
case alertController(alertController: UIAlertController)
|
case alertController(alertController: UIAlertController)
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
case publicTimeline
|
case publicTimeline
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
var isOnboarding: Bool {
|
||||||
|
switch self {
|
||||||
|
case .welcome,
|
||||||
|
.mastodonPickServer,
|
||||||
|
.mastodonPinBasedAuthentication,
|
||||||
|
.mastodonRegister,
|
||||||
|
.mastodonServerRules,
|
||||||
|
.mastodonConfirmEmail,
|
||||||
|
.mastodonResendEmail:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SceneCoordinator {
|
extension SceneCoordinator {
|
||||||
|
|
||||||
func setup() {
|
func setup() {
|
||||||
// Check user authentication status
|
let viewController = MainTabBarController(context: appContext, coordinator: self)
|
||||||
|
sceneDelegate.window?.rootViewController = viewController
|
||||||
let request = MastodonAuthentication.sortedFetchRequest
|
}
|
||||||
|
|
||||||
|
func setupOnboardingIfNeeds(animated: Bool) {
|
||||||
|
// Check user authentication status and show onboarding if needs
|
||||||
do {
|
do {
|
||||||
let fetchResult = try appContext.managedObjectContext.fetch(request)
|
let request = MastodonAuthentication.sortedFetchRequest
|
||||||
DispatchQueue.main.async {
|
if try appContext.managedObjectContext.fetch(request).isEmpty {
|
||||||
var rootViewController: UIViewController
|
DispatchQueue.main.async {
|
||||||
if fetchResult.isEmpty {
|
self.present(
|
||||||
let welcomViewController = WelcomeViewController()
|
scene: .welcome,
|
||||||
self.setupDependency(for: welcomViewController)
|
from: nil,
|
||||||
rootViewController = UINavigationController(rootViewController: welcomViewController)
|
transition: .modal(animated: animated, completion: nil)
|
||||||
} else {
|
)
|
||||||
rootViewController = MainTabBarController(context: self.appContext, coordinator: self)
|
|
||||||
}
|
}
|
||||||
self.sceneDelegate.window?.rootViewController = rootViewController
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
assertionFailure("CoreDataStack error at app launch!")
|
assertionFailure(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +120,13 @@ extension SceneCoordinator {
|
||||||
presentingViewController.showDetailViewController(navigationController, sender: sender)
|
presentingViewController.showDetailViewController(navigationController, sender: sender)
|
||||||
|
|
||||||
case .modal(let animated, let completion):
|
case .modal(let animated, let completion):
|
||||||
let modalNavigationController = UINavigationController(rootViewController: viewController)
|
let modalNavigationController: UINavigationController = {
|
||||||
|
if scene.isOnboarding {
|
||||||
|
return DarkContentStatusBarStyleNavigationController(rootViewController: viewController)
|
||||||
|
} else {
|
||||||
|
return UINavigationController(rootViewController: viewController)
|
||||||
|
}
|
||||||
|
}()
|
||||||
if let adaptivePresentationControllerDelegate = viewController as? UIAdaptivePresentationControllerDelegate {
|
if let adaptivePresentationControllerDelegate = viewController as? UIAdaptivePresentationControllerDelegate {
|
||||||
modalNavigationController.presentationController?.delegate = adaptivePresentationControllerDelegate
|
modalNavigationController.presentationController?.delegate = adaptivePresentationControllerDelegate
|
||||||
}
|
}
|
||||||
|
@ -143,12 +166,8 @@ private extension SceneCoordinator {
|
||||||
case .welcome:
|
case .welcome:
|
||||||
let _viewController = WelcomeViewController()
|
let _viewController = WelcomeViewController()
|
||||||
viewController = _viewController
|
viewController = _viewController
|
||||||
case .pickServer(let viewModel):
|
case .mastodonPickServer(let viewModel):
|
||||||
let _viewController = PickServerViewController()
|
let _viewController = MastodonPickServerViewController()
|
||||||
_viewController.viewModel = viewModel
|
|
||||||
viewController = _viewController
|
|
||||||
case .authentication(let viewModel):
|
|
||||||
let _viewController = AuthenticationViewController()
|
|
||||||
_viewController.viewModel = viewModel
|
_viewController.viewModel = viewModel
|
||||||
viewController = _viewController
|
viewController = _viewController
|
||||||
case .mastodonPinBasedAuthentication(let viewModel):
|
case .mastodonPinBasedAuthentication(let viewModel):
|
||||||
|
|
|
@ -50,9 +50,9 @@ internal enum L10n {
|
||||||
internal static let savePhoto = L10n.tr("Localizable", "Common.Controls.Actions.SavePhoto")
|
internal static let savePhoto = L10n.tr("Localizable", "Common.Controls.Actions.SavePhoto")
|
||||||
/// See More
|
/// See More
|
||||||
internal static let seeMore = L10n.tr("Localizable", "Common.Controls.Actions.SeeMore")
|
internal static let seeMore = L10n.tr("Localizable", "Common.Controls.Actions.SeeMore")
|
||||||
/// Sign in
|
/// Sign In
|
||||||
internal static let signIn = L10n.tr("Localizable", "Common.Controls.Actions.SignIn")
|
internal static let signIn = L10n.tr("Localizable", "Common.Controls.Actions.SignIn")
|
||||||
/// Sign up
|
/// Sign Up
|
||||||
internal static let signUp = L10n.tr("Localizable", "Common.Controls.Actions.SignUp")
|
internal static let signUp = L10n.tr("Localizable", "Common.Controls.Actions.SignUp")
|
||||||
/// Take photo
|
/// Take photo
|
||||||
internal static let takePhoto = L10n.tr("Localizable", "Common.Controls.Actions.TakePhoto")
|
internal static let takePhoto = L10n.tr("Localizable", "Common.Controls.Actions.TakePhoto")
|
||||||
|
@ -101,7 +101,7 @@ internal enum L10n {
|
||||||
internal enum DontReceiveEmail {
|
internal enum DontReceiveEmail {
|
||||||
/// Check if your email address is correct as well as your junk folder if you haven’t.
|
/// Check if your email address is correct as well as your junk folder if you haven’t.
|
||||||
internal static let description = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.Description")
|
internal static let description = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.Description")
|
||||||
/// Resend email
|
/// Resend Email
|
||||||
internal static let resendEmail = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.ResendEmail")
|
internal static let resendEmail = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.ResendEmail")
|
||||||
/// Check your email
|
/// Check your email
|
||||||
internal static let title = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.Title")
|
internal static let title = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.Title")
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
"Common.Controls.Actions.Save" = "Save";
|
"Common.Controls.Actions.Save" = "Save";
|
||||||
"Common.Controls.Actions.SavePhoto" = "Save photo";
|
"Common.Controls.Actions.SavePhoto" = "Save photo";
|
||||||
"Common.Controls.Actions.SeeMore" = "See More";
|
"Common.Controls.Actions.SeeMore" = "See More";
|
||||||
"Common.Controls.Actions.SignIn" = "Sign in";
|
"Common.Controls.Actions.SignIn" = "Sign In";
|
||||||
"Common.Controls.Actions.SignUp" = "Sign up";
|
"Common.Controls.Actions.SignUp" = "Sign Up";
|
||||||
"Common.Controls.Actions.TakePhoto" = "Take photo";
|
"Common.Controls.Actions.TakePhoto" = "Take photo";
|
||||||
"Common.Controls.Status.MediaContentWarning" = "Tap to reveal that may be sensitive";
|
"Common.Controls.Status.MediaContentWarning" = "Tap to reveal that may be sensitive";
|
||||||
"Common.Controls.Status.ShowPost" = "Show Post";
|
"Common.Controls.Status.ShowPost" = "Show Post";
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
"Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email";
|
"Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email";
|
||||||
"Scene.ConfirmEmail.Button.OpenEmailApp" = "Open Email App";
|
"Scene.ConfirmEmail.Button.OpenEmailApp" = "Open Email App";
|
||||||
"Scene.ConfirmEmail.DontReceiveEmail.Description" = "Check if your email address is correct as well as your junk folder if you haven’t.";
|
"Scene.ConfirmEmail.DontReceiveEmail.Description" = "Check if your email address is correct as well as your junk folder if you haven’t.";
|
||||||
"Scene.ConfirmEmail.DontReceiveEmail.ResendEmail" = "Resend email";
|
"Scene.ConfirmEmail.DontReceiveEmail.ResendEmail" = "Resend Email";
|
||||||
"Scene.ConfirmEmail.DontReceiveEmail.Title" = "Check your email";
|
"Scene.ConfirmEmail.DontReceiveEmail.Title" = "Check your email";
|
||||||
"Scene.ConfirmEmail.OpenEmailApp.Description" = "We just sent you an email. Check your junk folder if you haven’t.";
|
"Scene.ConfirmEmail.OpenEmailApp.Description" = "We just sent you an email. Check your junk folder if you haven’t.";
|
||||||
"Scene.ConfirmEmail.OpenEmailApp.Mail" = "Mail";
|
"Scene.ConfirmEmail.OpenEmailApp.Mail" = "Mail";
|
||||||
|
@ -62,4 +62,4 @@ any server.";
|
||||||
"Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@.";
|
"Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@.";
|
||||||
"Scene.ServerRules.Title" = "Some ground rules.";
|
"Scene.ServerRules.Title" = "Some ground rules.";
|
||||||
"Scene.Welcome.Slogan" = "Social networking
|
"Scene.Welcome.Slogan" = "Social networking
|
||||||
back in your hands.";
|
back in your hands.";
|
||||||
|
|
|
@ -37,35 +37,5 @@ extension HomeTimelineViewController {
|
||||||
coordinator.present(scene: .publicTimeline, from: self, transition: .show)
|
coordinator.present(scene: .publicTimeline, from: self, transition: .show)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func signOutAction(_ sender: UIAction) {
|
|
||||||
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let currentAccountCount = context.authenticationService.mastodonAuthentications.value.count
|
|
||||||
let isAuthenticationExistWhenSignOut = currentAccountCount - 1 > 0
|
|
||||||
// prepare advance
|
|
||||||
let authenticationViewModel = AuthenticationViewModel(context: context, coordinator: coordinator, isAuthenticationExist: isAuthenticationExistWhenSignOut)
|
|
||||||
|
|
||||||
context.authenticationService.signOutMastodonUser(
|
|
||||||
domain: activeMastodonAuthenticationBox.domain,
|
|
||||||
userID: activeMastodonAuthenticationBox.userID
|
|
||||||
)
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] result in
|
|
||||||
guard let self = self else { return }
|
|
||||||
switch result {
|
|
||||||
case .failure(let error):
|
|
||||||
assertionFailure(error.localizedDescription)
|
|
||||||
case .success(let isSignOut):
|
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: sign out %s", ((#file as NSString).lastPathComponent), #line, #function, isSignOut ? "success" : "fail")
|
|
||||||
guard isSignOut else { return }
|
|
||||||
if !isAuthenticationExistWhenSignOut {
|
|
||||||
self.coordinator.present(scene: .authentication(viewModel: authenticationViewModel), from: nil, transition: .modal(animated: true, completion: nil))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -70,8 +70,20 @@ extension HomeTimelineViewController {
|
||||||
return imageView
|
return imageView
|
||||||
}()
|
}()
|
||||||
navigationItem.leftBarButtonItem = settingBarButtonItem
|
navigationItem.leftBarButtonItem = settingBarButtonItem
|
||||||
settingBarButtonItem.target = self
|
#if DEBUG
|
||||||
settingBarButtonItem.action = #selector(HomeTimelineViewController.settingBarButtonItemPressed(_:))
|
// long press to trigger debug menu
|
||||||
|
settingBarButtonItem.menu = debugMenu
|
||||||
|
#else
|
||||||
|
// settingBarButtonItem.target = self
|
||||||
|
// settingBarButtonItem.action = #selector(HomeTimelineViewController.settingBarButtonItemPressed(_:))
|
||||||
|
settingBarButtonItem.menu = UIMenu(title: "Settings", image: nil, identifier: nil, options: .displayInline, children: [
|
||||||
|
UIAction(title: "Sign Out", image: UIImage(systemName: "escape"), attributes: .destructive) { [weak self] action in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.signOutAction(action)
|
||||||
|
}
|
||||||
|
])
|
||||||
|
#endif
|
||||||
|
|
||||||
navigationItem.rightBarButtonItem = composeBarButtonItem
|
navigationItem.rightBarButtonItem = composeBarButtonItem
|
||||||
composeBarButtonItem.target = self
|
composeBarButtonItem.target = self
|
||||||
composeBarButtonItem.action = #selector(HomeTimelineViewController.composeBarButtonItemPressed(_:))
|
composeBarButtonItem.action = #selector(HomeTimelineViewController.composeBarButtonItemPressed(_:))
|
||||||
|
@ -111,11 +123,6 @@ extension HomeTimelineViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
// long press to trigger debug menu
|
|
||||||
settingBarButtonItem.menu = debugMenu
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -169,6 +176,30 @@ extension HomeTimelineViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func signOutAction(_ sender: UIAction) {
|
||||||
|
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
context.authenticationService.signOutMastodonUser(
|
||||||
|
domain: activeMastodonAuthenticationBox.domain,
|
||||||
|
userID: activeMastodonAuthenticationBox.userID
|
||||||
|
)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] result in
|
||||||
|
guard let self = self else { return }
|
||||||
|
switch result {
|
||||||
|
case .failure(let error):
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
case .success(let isSignOut):
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: sign out %s", ((#file as NSString).lastPathComponent), #line, #function, isSignOut ? "success" : "fail")
|
||||||
|
guard isSignOut else { return }
|
||||||
|
self.coordinator.setup()
|
||||||
|
self.coordinator.setupOnboardingIfNeeds(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,354 +0,0 @@
|
||||||
//
|
|
||||||
// AuthenticationViewController.swift
|
|
||||||
// Mastodon
|
|
||||||
//
|
|
||||||
// Created by MainasuK Cirno on 2021/1/29.
|
|
||||||
//
|
|
||||||
|
|
||||||
import os.log
|
|
||||||
import UIKit
|
|
||||||
import Combine
|
|
||||||
import MastodonSDK
|
|
||||||
import UITextField_Shake
|
|
||||||
|
|
||||||
final class AuthenticationViewController: UIViewController, NeedsDependency, OnboardingViewControllerAppearance{
|
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
|
||||||
|
|
||||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
|
||||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
|
||||||
|
|
||||||
var viewModel: AuthenticationViewModel!
|
|
||||||
|
|
||||||
let domainLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .preferredFont(forTextStyle: .headline)
|
|
||||||
label.textColor = Asset.Colors.Label.primary.color
|
|
||||||
label.text = "Domain:"
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
let domainTextField: UITextField = {
|
|
||||||
let textField = UITextField()
|
|
||||||
textField.placeholder = "example.com"
|
|
||||||
textField.autocapitalizationType = .none
|
|
||||||
textField.autocorrectionType = .no
|
|
||||||
textField.keyboardType = .URL
|
|
||||||
return textField
|
|
||||||
}()
|
|
||||||
|
|
||||||
let signInButton: UIButton = {
|
|
||||||
let button = UIButton(type: .system)
|
|
||||||
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
|
|
||||||
button.setBackgroundImage(UIImage.placeholder(color: Asset.Colors.Background.secondarySystemBackground.color), for: .normal)
|
|
||||||
button.setBackgroundImage(UIImage.placeholder(color: Asset.Colors.Background.secondarySystemBackground.color.withAlphaComponent(0.8)), for: .disabled)
|
|
||||||
button.setTitleColor(Asset.Colors.Label.primary.color, for: .normal)
|
|
||||||
button.setTitle("Sign in", for: .normal)
|
|
||||||
button.layer.masksToBounds = true
|
|
||||||
button.layer.cornerRadius = 8
|
|
||||||
button.layer.cornerCurve = .continuous
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
let signUpButton: UIButton = {
|
|
||||||
let button = UIButton(type: .system)
|
|
||||||
button.titleLabel?.font = .preferredFont(forTextStyle: .subheadline)
|
|
||||||
button.setTitleColor(Asset.Colors.Button.highlight.color, for: .normal)
|
|
||||||
button.setTitleColor(.systemGray, for: .disabled)
|
|
||||||
button.setTitle("Sign up", for: .normal)
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
let signInActivityIndicatorView: UIActivityIndicatorView = {
|
|
||||||
let activityIndicatorView = UIActivityIndicatorView(style: .medium)
|
|
||||||
activityIndicatorView.hidesWhenStopped = true
|
|
||||||
return activityIndicatorView
|
|
||||||
}()
|
|
||||||
|
|
||||||
let signUpActivityIndicatorView: UIActivityIndicatorView = {
|
|
||||||
let activityIndicatorView = UIActivityIndicatorView(style: .medium)
|
|
||||||
activityIndicatorView.hidesWhenStopped = true
|
|
||||||
return activityIndicatorView
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AuthenticationViewController {
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
self.setupOnboardingAppearance()
|
|
||||||
|
|
||||||
domainLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.addSubview(domainLabel)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
domainLabel.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 16),
|
|
||||||
domainLabel.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
|
|
||||||
domainLabel.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
|
|
||||||
])
|
|
||||||
|
|
||||||
domainTextField.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.addSubview(domainTextField)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
domainTextField.topAnchor.constraint(equalTo: domainLabel.bottomAnchor, constant: 8),
|
|
||||||
domainTextField.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
|
|
||||||
domainTextField.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
|
|
||||||
])
|
|
||||||
|
|
||||||
signInButton.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.addSubview(signInButton)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
signInButton.topAnchor.constraint(equalTo: domainTextField.bottomAnchor, constant: 20),
|
|
||||||
signInButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
|
|
||||||
signInButton.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
|
|
||||||
signInButton.heightAnchor.constraint(equalToConstant: 44).priority(.defaultHigh),
|
|
||||||
])
|
|
||||||
|
|
||||||
signInActivityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.addSubview(signInActivityIndicatorView)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
signInActivityIndicatorView.centerXAnchor.constraint(equalTo: signInButton.centerXAnchor),
|
|
||||||
signInActivityIndicatorView.centerYAnchor.constraint(equalTo: signInButton.centerYAnchor),
|
|
||||||
])
|
|
||||||
|
|
||||||
signUpButton.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.addSubview(signUpButton)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
signUpButton.topAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: 8),
|
|
||||||
signUpButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
|
|
||||||
signUpButton.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
|
|
||||||
signUpButton.heightAnchor.constraint(equalToConstant: 44).priority(.defaultHigh),
|
|
||||||
])
|
|
||||||
|
|
||||||
signUpActivityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.addSubview(signUpActivityIndicatorView)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
signUpActivityIndicatorView.centerXAnchor.constraint(equalTo: signUpButton.centerXAnchor),
|
|
||||||
signUpActivityIndicatorView.centerYAnchor.constraint(equalTo: signUpButton.centerYAnchor),
|
|
||||||
])
|
|
||||||
|
|
||||||
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: domainTextField)
|
|
||||||
.compactMap { notification in
|
|
||||||
guard let textField = notification.object as? UITextField? else { return nil }
|
|
||||||
return textField?.text ?? ""
|
|
||||||
}
|
|
||||||
.assign(to: \.value, on: viewModel.input)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
viewModel.isAuthenticating
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] isAuthenticating in
|
|
||||||
guard let self = self else { return }
|
|
||||||
isAuthenticating ? self.signInActivityIndicatorView.startAnimating() : self.signInActivityIndicatorView.stopAnimating()
|
|
||||||
self.signInButton.setTitle(isAuthenticating ? "" : "Sign in", for: .normal)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
viewModel.isRegistering
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] isRegistering in
|
|
||||||
guard let self = self else { return }
|
|
||||||
isRegistering ? self.signUpActivityIndicatorView.startAnimating() : self.signUpActivityIndicatorView.stopAnimating()
|
|
||||||
self.signUpButton.setTitle(isRegistering ? "" : "Sign up", for: .normal)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
viewModel.isIdle
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] isIdle in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.signInButton.isEnabled = isIdle
|
|
||||||
self.signUpButton.isEnabled = isIdle
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
|
|
||||||
viewModel.authenticated
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] domain, user in
|
|
||||||
guard let self = self else { return }
|
|
||||||
// reset view hierarchy only if needs
|
|
||||||
if self.viewModel.viewHierarchyShouldReset {
|
|
||||||
self.context.authenticationService.activeMastodonUser(domain: domain, userID: user.id)
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] result in
|
|
||||||
guard let self = self else { return }
|
|
||||||
switch result {
|
|
||||||
case .failure(let error):
|
|
||||||
assertionFailure(error.localizedDescription)
|
|
||||||
case .success(let isActived):
|
|
||||||
assert(isActived)
|
|
||||||
self.coordinator.setup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.store(in: &self.disposeBag)
|
|
||||||
} else {
|
|
||||||
self.dismiss(animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
viewModel.error
|
|
||||||
.compactMap { $0 }
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] error in
|
|
||||||
guard let self = self else { return }
|
|
||||||
let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert)
|
|
||||||
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
|
|
||||||
alertController.addAction(okAction)
|
|
||||||
self.coordinator.present(
|
|
||||||
scene: .alertController(alertController: alertController),
|
|
||||||
from: nil,
|
|
||||||
transition: .alertController(animated: true, completion: nil)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
signInButton.addTarget(self, action: #selector(AuthenticationViewController.signInButtonPressed(_:)), for: .touchUpInside)
|
|
||||||
signUpButton.addTarget(self, action: #selector(AuthenticationViewController.signUpButtonPressed(_:)), for: .touchUpInside)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
domainTextField.becomeFirstResponder()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AuthenticationViewController {
|
|
||||||
|
|
||||||
@objc private func signInButtonPressed(_ sender: UIButton) {
|
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
|
||||||
guard viewModel.isDomainValid.value, let domain = viewModel.domain.value else {
|
|
||||||
domainTextField.shake()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard viewModel.isIdle.value else { return }
|
|
||||||
viewModel.isAuthenticating.value = true
|
|
||||||
context.apiService.createApplication(domain: domain)
|
|
||||||
.tryMap { response -> AuthenticationViewModel.AuthenticateInfo in
|
|
||||||
let application = response.value
|
|
||||||
guard let info = AuthenticationViewModel.AuthenticateInfo(domain: domain, application: application) else {
|
|
||||||
throw APIService.APIError.explicit(.badResponse)
|
|
||||||
}
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] completion in
|
|
||||||
guard let self = self else { return }
|
|
||||||
// trigger state update
|
|
||||||
self.viewModel.isAuthenticating.value = false
|
|
||||||
|
|
||||||
switch completion {
|
|
||||||
case .failure(let error):
|
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: sign in fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
|
||||||
self.viewModel.error.value = error
|
|
||||||
case .finished:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} receiveValue: { [weak self] info in
|
|
||||||
guard let self = self else { return }
|
|
||||||
let mastodonPinBasedAuthenticationViewModel = MastodonPinBasedAuthenticationViewModel(authenticateURL: info.authorizeURL)
|
|
||||||
self.viewModel.authenticate(
|
|
||||||
info: info,
|
|
||||||
pinCodePublisher: mastodonPinBasedAuthenticationViewModel.pinCodePublisher
|
|
||||||
)
|
|
||||||
self.viewModel.mastodonPinBasedAuthenticationViewController = self.coordinator.present(
|
|
||||||
scene: .mastodonPinBasedAuthentication(viewModel: mastodonPinBasedAuthenticationViewModel),
|
|
||||||
from: nil,
|
|
||||||
transition: .modal(animated: true, completion: nil)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct SignUpResponseFirst {
|
|
||||||
let instance: Mastodon.Response.Content<Mastodon.Entity.Instance>
|
|
||||||
let application: Mastodon.Response.Content<Mastodon.Entity.Application>
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct SignUpResponseSecond {
|
|
||||||
let instance: Mastodon.Response.Content<Mastodon.Entity.Instance>
|
|
||||||
let authenticateInfo: AuthenticationViewModel.AuthenticateInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct SignUpResponseThird {
|
|
||||||
let instance: Mastodon.Response.Content<Mastodon.Entity.Instance>
|
|
||||||
let authenticateInfo: AuthenticationViewModel.AuthenticateInfo
|
|
||||||
let applicationToken: Mastodon.Response.Content<Mastodon.Entity.Token>
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func signUpButtonPressed(_ sender: UIButton) {
|
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
|
||||||
guard viewModel.isDomainValid.value, let domain = viewModel.domain.value else {
|
|
||||||
domainTextField.shake()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard viewModel.isIdle.value else { return }
|
|
||||||
viewModel.isRegistering.value = true
|
|
||||||
|
|
||||||
context.apiService.instance(domain: domain)
|
|
||||||
.compactMap { [weak self] response -> AnyPublisher<SignUpResponseFirst, Error>? in
|
|
||||||
guard let self = self else { return nil }
|
|
||||||
guard response.value.registrations != false else {
|
|
||||||
return Fail(error: AuthenticationViewModel.AuthenticationError.registrationClosed).eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
return self.context.apiService.createApplication(domain: domain)
|
|
||||||
.map { SignUpResponseFirst(instance: response, application: $0) }
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
.switchToLatest()
|
|
||||||
.tryMap { response -> SignUpResponseSecond in
|
|
||||||
let application = response.application.value
|
|
||||||
guard let authenticateInfo = AuthenticationViewModel.AuthenticateInfo(domain: domain, application: application) else {
|
|
||||||
throw APIService.APIError.explicit(.badResponse)
|
|
||||||
}
|
|
||||||
return SignUpResponseSecond(instance: response.instance, authenticateInfo: authenticateInfo)
|
|
||||||
}
|
|
||||||
.compactMap { [weak self] response -> AnyPublisher<SignUpResponseThird, Error>? in
|
|
||||||
guard let self = self else { return nil }
|
|
||||||
let instance = response.instance
|
|
||||||
let authenticateInfo = response.authenticateInfo
|
|
||||||
return self.context.apiService.applicationAccessToken(
|
|
||||||
domain: domain,
|
|
||||||
clientID: authenticateInfo.clientID,
|
|
||||||
clientSecret: authenticateInfo.clientSecret
|
|
||||||
)
|
|
||||||
.map { SignUpResponseThird(instance: instance, authenticateInfo: authenticateInfo, applicationToken: $0) }
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
.switchToLatest()
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] completion in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.viewModel.isRegistering.value = false
|
|
||||||
|
|
||||||
switch completion {
|
|
||||||
case .failure(let error):
|
|
||||||
self.viewModel.error.send(error)
|
|
||||||
case .finished:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} receiveValue: { [weak self] response in
|
|
||||||
guard let self = self else { return }
|
|
||||||
let mastodonRegisterViewModel = MastodonRegisterViewModel(
|
|
||||||
domain: domain,
|
|
||||||
authenticateInfo: response.authenticateInfo,
|
|
||||||
instance: response.instance.value,
|
|
||||||
applicationToken: response.applicationToken.value
|
|
||||||
)
|
|
||||||
self.coordinator.present(scene: .mastodonRegister(viewModel: mastodonRegisterViewModel), from: self, transition: .show)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - UIAdaptivePresentationControllerDelegate
|
|
||||||
extension AuthenticationViewController: UIAdaptivePresentationControllerDelegate {
|
|
||||||
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
|
||||||
return .fullScreen
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,8 @@ import os.log
|
||||||
import ThirdPartyMailer
|
import ThirdPartyMailer
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
final class MastodonConfirmEmailViewController: UIViewController, NeedsDependency, OnboardingViewControllerAppearance {
|
final class MastodonConfirmEmailViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
@ -57,13 +58,18 @@ final class MastodonConfirmEmailViewController: UIViewController, NeedsDependenc
|
||||||
button.addTarget(self, action: #selector(dontReceiveButtonPressed(_:)), for: UIControl.Event.touchUpInside)
|
button.addTarget(self, action: #selector(dontReceiveButtonPressed(_:)), for: UIControl.Event.touchUpInside)
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonConfirmEmailViewController {
|
extension MastodonConfirmEmailViewController {
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
|
||||||
self.setupOnboardingAppearance()
|
setupOnboardingAppearance()
|
||||||
|
|
||||||
// resizedView
|
// resizedView
|
||||||
let resizedView = UIView()
|
let resizedView = UIView()
|
||||||
|
@ -107,19 +113,15 @@ extension MastodonConfirmEmailViewController {
|
||||||
case .finished:
|
case .finished:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} receiveValue: { _ in
|
} receiveValue: { response in
|
||||||
self.coordinator.setup()
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: user %s's email confirmed", ((#file as NSString).lastPathComponent), #line, #function, response.value.username)
|
||||||
|
self.dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
.store(in: &self.disposeBag)
|
.store(in: &self.disposeBag)
|
||||||
}
|
}
|
||||||
.store(in: &self.disposeBag)
|
.store(in: &self.disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
navigationController?.setNavigationBarHidden(false, animated: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonConfirmEmailViewController {
|
extension MastodonConfirmEmailViewController {
|
||||||
|
@ -174,3 +176,6 @@ extension MastodonConfirmEmailViewController {
|
||||||
self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
|
self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - OnboardingViewControllerAppearance
|
||||||
|
extension MastodonConfirmEmailViewController: OnboardingViewControllerAppearance { }
|
||||||
|
|
|
@ -9,7 +9,7 @@ import UIKit
|
||||||
|
|
||||||
class PickServerCategoryCollectionViewCell: UICollectionViewCell {
|
class PickServerCategoryCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
var category: PickServerViewModel.Category? {
|
var category: MastodonPickServerViewModel.Category? {
|
||||||
didSet {
|
didSet {
|
||||||
categoryView.category = category
|
categoryView.category = category
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// PickServerViewController.swift
|
// MastodonPickServerViewController.swift
|
||||||
// Mastodon
|
// Mastodon
|
||||||
//
|
//
|
||||||
// Created by BradGao on 2021/2/20.
|
// Created by BradGao on 2021/2/20.
|
||||||
|
@ -10,14 +10,14 @@ import Combine
|
||||||
import OSLog
|
import OSLog
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
final class PickServerViewController: UIViewController, NeedsDependency {
|
final class MastodonPickServerViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
private var disposeBag = Set<AnyCancellable>()
|
private var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
|
||||||
var viewModel: PickServerViewModel!
|
var viewModel: MastodonPickServerViewModel!
|
||||||
|
|
||||||
private var isAuthenticating = CurrentValueSubject<Bool, Never>(false)
|
private var isAuthenticating = CurrentValueSubject<Bool, Never>(false)
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ final class PickServerViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PickServerViewController {
|
extension MastodonPickServerViewController {
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||||
return .darkContent
|
return .darkContent
|
||||||
|
@ -72,9 +72,9 @@ extension PickServerViewController {
|
||||||
|
|
||||||
view.addSubview(nextStepButton)
|
view.addSubview(nextStepButton)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
nextStepButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 12),
|
nextStepButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: MastodonPickServerViewController.actionButtonMargin),
|
||||||
view.readableContentGuide.trailingAnchor.constraint(equalTo: nextStepButton.trailingAnchor, constant: 12),
|
view.readableContentGuide.trailingAnchor.constraint(equalTo: nextStepButton.trailingAnchor, constant: MastodonPickServerViewController.actionButtonMargin),
|
||||||
nextStepButton.heightAnchor.constraint(equalToConstant: PickServerViewController.actionButtonHeight).priority(.defaultHigh),
|
nextStepButton.heightAnchor.constraint(equalToConstant: MastodonPickServerViewController.actionButtonHeight).priority(.defaultHigh),
|
||||||
view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: nextStepButton.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight),
|
view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: nextStepButton.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -139,11 +139,11 @@ extension PickServerViewController {
|
||||||
|
|
||||||
viewModel
|
viewModel
|
||||||
.authenticated
|
.authenticated
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.flatMap { [weak self] (domain, user) -> AnyPublisher<Result<Bool, Error>, Never> in
|
.flatMap { [weak self] (domain, user) -> AnyPublisher<Result<Bool, Error>, Never> in
|
||||||
guard let self = self else { return Just(.success(false)).eraseToAnyPublisher() }
|
guard let self = self else { return Just(.success(false)).eraseToAnyPublisher() }
|
||||||
return self.context.authenticationService.activeMastodonUser(domain: domain, userID: user.id)
|
return self.context.authenticationService.activeMastodonUser(domain: domain, userID: user.id)
|
||||||
}
|
}
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] result in
|
.sink { [weak self] result in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
switch result {
|
switch result {
|
||||||
|
@ -151,7 +151,7 @@ extension PickServerViewController {
|
||||||
assertionFailure(error.localizedDescription)
|
assertionFailure(error.localizedDescription)
|
||||||
case .success(let isActived):
|
case .success(let isActived):
|
||||||
assert(isActived)
|
assert(isActived)
|
||||||
self.coordinator.setup()
|
self.dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
@ -181,9 +181,9 @@ extension PickServerViewController {
|
||||||
guard let server = viewModel.selectedServer.value else { return }
|
guard let server = viewModel.selectedServer.value else { return }
|
||||||
isAuthenticating.send(true)
|
isAuthenticating.send(true)
|
||||||
context.apiService.createApplication(domain: server.domain)
|
context.apiService.createApplication(domain: server.domain)
|
||||||
.tryMap { response -> PickServerViewModel.AuthenticateInfo in
|
.tryMap { response -> MastodonPickServerViewModel.AuthenticateInfo in
|
||||||
let application = response.value
|
let application = response.value
|
||||||
guard let info = PickServerViewModel.AuthenticateInfo(domain: server.domain, application: application) else {
|
guard let info = MastodonPickServerViewModel.AuthenticateInfo(domain: server.domain, application: application) else {
|
||||||
throw APIService.APIError.explicit(.badResponse)
|
throw APIService.APIError.explicit(.badResponse)
|
||||||
}
|
}
|
||||||
return info
|
return info
|
||||||
|
@ -222,24 +222,24 @@ extension PickServerViewController {
|
||||||
isAuthenticating.send(true)
|
isAuthenticating.send(true)
|
||||||
|
|
||||||
context.apiService.instance(domain: server.domain)
|
context.apiService.instance(domain: server.domain)
|
||||||
.compactMap { [weak self] response -> AnyPublisher<PickServerViewModel.SignUpResponseFirst, Error>? in
|
.compactMap { [weak self] response -> AnyPublisher<MastodonPickServerViewModel.SignUpResponseFirst, Error>? in
|
||||||
guard let self = self else { return nil }
|
guard let self = self else { return nil }
|
||||||
guard response.value.registrations != false else {
|
guard response.value.registrations != false else {
|
||||||
return Fail(error: AuthenticationViewModel.AuthenticationError.registrationClosed).eraseToAnyPublisher()
|
return Fail(error: AuthenticationViewModel.AuthenticationError.registrationClosed).eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
return self.context.apiService.createApplication(domain: server.domain)
|
return self.context.apiService.createApplication(domain: server.domain)
|
||||||
.map { PickServerViewModel.SignUpResponseFirst(instance: response, application: $0) }
|
.map { MastodonPickServerViewModel.SignUpResponseFirst(instance: response, application: $0) }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
.switchToLatest()
|
.switchToLatest()
|
||||||
.tryMap { response -> PickServerViewModel.SignUpResponseSecond in
|
.tryMap { response -> MastodonPickServerViewModel.SignUpResponseSecond in
|
||||||
let application = response.application.value
|
let application = response.application.value
|
||||||
guard let authenticateInfo = AuthenticationViewModel.AuthenticateInfo(domain: server.domain, application: application) else {
|
guard let authenticateInfo = AuthenticationViewModel.AuthenticateInfo(domain: server.domain, application: application) else {
|
||||||
throw APIService.APIError.explicit(.badResponse)
|
throw APIService.APIError.explicit(.badResponse)
|
||||||
}
|
}
|
||||||
return PickServerViewModel.SignUpResponseSecond(instance: response.instance, authenticateInfo: authenticateInfo)
|
return MastodonPickServerViewModel.SignUpResponseSecond(instance: response.instance, authenticateInfo: authenticateInfo)
|
||||||
}
|
}
|
||||||
.compactMap { [weak self] response -> AnyPublisher<PickServerViewModel.SignUpResponseThird, Error>? in
|
.compactMap { [weak self] response -> AnyPublisher<MastodonPickServerViewModel.SignUpResponseThird, Error>? in
|
||||||
guard let self = self else { return nil }
|
guard let self = self else { return nil }
|
||||||
let instance = response.instance
|
let instance = response.instance
|
||||||
let authenticateInfo = response.authenticateInfo
|
let authenticateInfo = response.authenticateInfo
|
||||||
|
@ -248,7 +248,7 @@ extension PickServerViewController {
|
||||||
clientID: authenticateInfo.clientID,
|
clientID: authenticateInfo.clientID,
|
||||||
clientSecret: authenticateInfo.clientSecret
|
clientSecret: authenticateInfo.clientSecret
|
||||||
)
|
)
|
||||||
.map { PickServerViewModel.SignUpResponseThird(instance: instance, authenticateInfo: authenticateInfo, applicationToken: $0) }
|
.map { MastodonPickServerViewModel.SignUpResponseThird(instance: instance, authenticateInfo: authenticateInfo, applicationToken: $0) }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
.switchToLatest()
|
.switchToLatest()
|
||||||
|
@ -277,7 +277,7 @@ extension PickServerViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PickServerViewController: UITableViewDelegate {
|
extension MastodonPickServerViewController: UITableViewDelegate {
|
||||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||||
let category = Section.allCases[section]
|
let category = Section.allCases[section]
|
||||||
switch category {
|
switch category {
|
||||||
|
@ -316,7 +316,7 @@ extension PickServerViewController: UITableViewDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PickServerViewController: UITableViewDataSource {
|
extension MastodonPickServerViewController: UITableViewDataSource {
|
||||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||||
return UIView()
|
return UIView()
|
||||||
}
|
}
|
||||||
|
@ -374,7 +374,7 @@ extension PickServerViewController: UITableViewDataSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PickServerViewController: PickServerCellDelegate {
|
extension MastodonPickServerViewController: PickServerCellDelegate {
|
||||||
func pickServerCell(modeChange server: Mastodon.Entity.Server, newMode: PickServerCell.Mode, updates: (() -> Void)) {
|
func pickServerCell(modeChange server: Mastodon.Entity.Server, newMode: PickServerCell.Mode, updates: (() -> Void)) {
|
||||||
if newMode == .collapse {
|
if newMode == .collapse {
|
||||||
expandServerDomainSet.remove(server.domain)
|
expandServerDomainSet.remove(server.domain)
|
||||||
|
@ -392,18 +392,18 @@ extension PickServerViewController: PickServerCellDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PickServerViewController: PickServerSearchCellDelegate {
|
extension MastodonPickServerViewController: PickServerSearchCellDelegate {
|
||||||
func pickServerSearchCell(didChange searchText: String?) {
|
func pickServerSearchCell(didChange searchText: String?) {
|
||||||
viewModel.searchText.send(searchText)
|
viewModel.searchText.send(searchText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PickServerViewController: PickServerCategoriesDataSource, PickServerCategoriesDelegate {
|
extension MastodonPickServerViewController: PickServerCategoriesDataSource, PickServerCategoriesDelegate {
|
||||||
func numberOfCategories() -> Int {
|
func numberOfCategories() -> Int {
|
||||||
return viewModel.categories.count
|
return viewModel.categories.count
|
||||||
}
|
}
|
||||||
|
|
||||||
func category(at index: Int) -> PickServerViewModel.Category {
|
func category(at index: Int) -> MastodonPickServerViewModel.Category {
|
||||||
return viewModel.categories[index]
|
return viewModel.categories[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,4 +417,4 @@ extension PickServerViewController: PickServerCategoriesDataSource, PickServerCa
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - OnboardingViewControllerAppearance
|
// MARK: - OnboardingViewControllerAppearance
|
||||||
extension PickServerViewController: OnboardingViewControllerAppearance { }
|
extension MastodonPickServerViewController: OnboardingViewControllerAppearance { }
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// PickServerViewModel.swift
|
// MastodonPickServerViewModel.swift
|
||||||
// Mastodon
|
// Mastodon
|
||||||
//
|
//
|
||||||
// Created by BradGao on 2021/2/23.
|
// Created by BradGao on 2021/2/23.
|
||||||
|
@ -11,7 +11,7 @@ import Combine
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
|
|
||||||
class PickServerViewModel: NSObject {
|
class MastodonPickServerViewModel: NSObject {
|
||||||
enum PickServerMode {
|
enum PickServerMode {
|
||||||
case signUp
|
case signUp
|
||||||
case signIn
|
case signIn
|
||||||
|
@ -173,7 +173,7 @@ class PickServerViewModel: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - SignIn methods & structs
|
// MARK: - SignIn methods & structs
|
||||||
extension PickServerViewModel {
|
extension MastodonPickServerViewModel {
|
||||||
enum AuthenticationError: Error, LocalizedError {
|
enum AuthenticationError: Error, LocalizedError {
|
||||||
case badCredentials
|
case badCredentials
|
||||||
case registrationClosed
|
case registrationClosed
|
||||||
|
@ -322,7 +322,7 @@ extension PickServerViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - SignUp methods & structs
|
// MARK: - SignUp methods & structs
|
||||||
extension PickServerViewModel {
|
extension MastodonPickServerViewModel {
|
||||||
struct SignUpResponseFirst {
|
struct SignUpResponseFirst {
|
||||||
let instance: Mastodon.Response.Content<Mastodon.Entity.Instance>
|
let instance: Mastodon.Response.Content<Mastodon.Entity.Instance>
|
||||||
let application: Mastodon.Response.Content<Mastodon.Entity.Application>
|
let application: Mastodon.Response.Content<Mastodon.Entity.Application>
|
|
@ -10,7 +10,7 @@ import MastodonSDK
|
||||||
|
|
||||||
protocol PickServerCategoriesDataSource: class {
|
protocol PickServerCategoriesDataSource: class {
|
||||||
func numberOfCategories() -> Int
|
func numberOfCategories() -> Int
|
||||||
func category(at index: Int) -> PickServerViewModel.Category
|
func category(at index: Int) -> MastodonPickServerViewModel.Category
|
||||||
func selectedIndex() -> Int
|
func selectedIndex() -> Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import UIKit
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
class PickServerCategoryView: UIView {
|
class PickServerCategoryView: UIView {
|
||||||
var category: PickServerViewModel.Category? {
|
var category: MastodonPickServerViewModel.Category? {
|
||||||
didSet {
|
didSet {
|
||||||
updateCategory()
|
updateCategory()
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,9 +92,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
||||||
textField.autocapitalizationType = .none
|
textField.autocapitalizationType = .none
|
||||||
textField.autocorrectionType = .no
|
textField.autocorrectionType = .no
|
||||||
textField.backgroundColor = .white
|
textField.backgroundColor = .white
|
||||||
textField.textColor = Asset.Colors.Label.secondary.color
|
textField.textColor = Asset.Colors.Label.primary.color
|
||||||
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Username.placeholder,
|
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Username.placeholder,
|
||||||
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color,
|
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
|
||||||
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
||||||
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
||||||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||||
|
@ -113,9 +113,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
||||||
textField.autocapitalizationType = .none
|
textField.autocapitalizationType = .none
|
||||||
textField.autocorrectionType = .no
|
textField.autocorrectionType = .no
|
||||||
textField.backgroundColor = .white
|
textField.backgroundColor = .white
|
||||||
textField.textColor = Asset.Colors.Label.secondary.color
|
textField.textColor = Asset.Colors.Label.primary.color
|
||||||
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.DisplayName.placeholder,
|
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.DisplayName.placeholder,
|
||||||
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color,
|
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
|
||||||
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
||||||
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
||||||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||||
|
@ -130,9 +130,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
||||||
textField.autocorrectionType = .no
|
textField.autocorrectionType = .no
|
||||||
textField.keyboardType = .emailAddress
|
textField.keyboardType = .emailAddress
|
||||||
textField.backgroundColor = .white
|
textField.backgroundColor = .white
|
||||||
textField.textColor = Asset.Colors.Label.secondary.color
|
textField.textColor = Asset.Colors.Label.primary.color
|
||||||
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Email.placeholder,
|
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Email.placeholder,
|
||||||
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color,
|
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
|
||||||
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
||||||
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
||||||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||||
|
@ -154,9 +154,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
||||||
textField.keyboardType = .asciiCapable
|
textField.keyboardType = .asciiCapable
|
||||||
textField.isSecureTextEntry = true
|
textField.isSecureTextEntry = true
|
||||||
textField.backgroundColor = .white
|
textField.backgroundColor = .white
|
||||||
textField.textColor = Asset.Colors.Label.secondary.color
|
textField.textColor = Asset.Colors.Label.primary.color
|
||||||
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Password.placeholder,
|
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Password.placeholder,
|
||||||
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color,
|
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
|
||||||
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
||||||
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
||||||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||||
|
@ -170,9 +170,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
||||||
textField.autocapitalizationType = .none
|
textField.autocapitalizationType = .none
|
||||||
textField.autocorrectionType = .no
|
textField.autocorrectionType = .no
|
||||||
textField.backgroundColor = .white
|
textField.backgroundColor = .white
|
||||||
textField.textColor = Asset.Colors.Label.secondary.color
|
textField.textColor = Asset.Colors.Label.primary.color
|
||||||
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Invite.registrationUserInviteRequest,
|
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Invite.registrationUserInviteRequest,
|
||||||
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color,
|
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
|
||||||
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)])
|
||||||
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
textField.borderStyle = UITextField.BorderStyle.roundedRect
|
||||||
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height))
|
||||||
|
@ -181,18 +181,18 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
|
||||||
return textField
|
return textField
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let signUpButton: UIButton = {
|
let buttonContainer = UIView()
|
||||||
|
let signUpButton: PrimaryActionButton = {
|
||||||
let button = PrimaryActionButton()
|
let button = PrimaryActionButton()
|
||||||
button.isEnabled = false
|
button.isEnabled = false
|
||||||
button.setTitle(L10n.Common.Controls.Actions.continue, for: .normal)
|
button.setTitle(L10n.Common.Controls.Actions.continue, for: .normal)
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let signUpActivityIndicatorView: UIActivityIndicatorView = {
|
deinit {
|
||||||
let activityIndicatorView = UIActivityIndicatorView(style: .medium)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
activityIndicatorView.hidesWhenStopped = true
|
}
|
||||||
return activityIndicatorView
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonRegisterViewController {
|
extension MastodonRegisterViewController {
|
||||||
|
@ -294,19 +294,17 @@ extension MastodonRegisterViewController {
|
||||||
stackView.setCustomSpacing(32, after: passwordCheckLabel)
|
stackView.setCustomSpacing(32, after: passwordCheckLabel)
|
||||||
|
|
||||||
// button
|
// button
|
||||||
|
stackView.addArrangedSubview(buttonContainer)
|
||||||
signUpButton.translatesAutoresizingMaskIntoConstraints = false
|
signUpButton.translatesAutoresizingMaskIntoConstraints = false
|
||||||
stackView.addArrangedSubview(signUpButton)
|
buttonContainer.addSubview(signUpButton)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
|
signUpButton.topAnchor.constraint(equalTo: buttonContainer.topAnchor),
|
||||||
|
signUpButton.leadingAnchor.constraint(equalTo: buttonContainer.leadingAnchor, constant: MastodonRegisterViewController.actionButtonMargin),
|
||||||
|
buttonContainer.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor, constant: MastodonRegisterViewController.actionButtonMargin),
|
||||||
|
buttonContainer.bottomAnchor.constraint(equalTo: signUpButton.bottomAnchor),
|
||||||
signUpButton.heightAnchor.constraint(equalToConstant: MastodonRegisterViewController.actionButtonHeight).priority(.defaultHigh),
|
signUpButton.heightAnchor.constraint(equalToConstant: MastodonRegisterViewController.actionButtonHeight).priority(.defaultHigh),
|
||||||
])
|
])
|
||||||
|
|
||||||
signUpActivityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
scrollView.addSubview(signUpActivityIndicatorView)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
signUpActivityIndicatorView.centerXAnchor.constraint(equalTo: signUpButton.centerXAnchor),
|
|
||||||
signUpActivityIndicatorView.centerYAnchor.constraint(equalTo: signUpButton.centerYAnchor),
|
|
||||||
])
|
|
||||||
|
|
||||||
Publishers.CombineLatest(
|
Publishers.CombineLatest(
|
||||||
KeyboardResponderService.shared.state.eraseToAnyPublisher(),
|
KeyboardResponderService.shared.state.eraseToAnyPublisher(),
|
||||||
KeyboardResponderService.shared.willEndFrame.eraseToAnyPublisher()
|
KeyboardResponderService.shared.willEndFrame.eraseToAnyPublisher()
|
||||||
|
@ -332,7 +330,7 @@ extension MastodonRegisterViewController {
|
||||||
self.scrollView.verticalScrollIndicatorInsets.bottom = padding + 16
|
self.scrollView.verticalScrollIndicatorInsets.bottom = padding + 16
|
||||||
|
|
||||||
if self.passwordTextField.isFirstResponder {
|
if self.passwordTextField.isFirstResponder {
|
||||||
let contentFrame = self.scrollView.convert(self.signUpButton.frame, to: nil)
|
let contentFrame = self.buttonContainer.convert(self.signUpButton.frame, to: nil)
|
||||||
let labelPadding = contentFrame.maxY - endFrame.minY
|
let labelPadding = contentFrame.maxY - endFrame.minY
|
||||||
let contentOffsetY = self.scrollView.contentOffset.y
|
let contentOffsetY = self.scrollView.contentOffset.y
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
@ -346,9 +344,7 @@ extension MastodonRegisterViewController {
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] isRegistering in
|
.sink { [weak self] isRegistering in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
isRegistering ? self.signUpActivityIndicatorView.startAnimating() : self.signUpActivityIndicatorView.stopAnimating()
|
isRegistering ? self.signUpButton.showLoading() : self.signUpButton.stopLoading()
|
||||||
self.signUpButton.setTitle(isRegistering ? "" : L10n.Common.Controls.Actions.continue, for: .normal)
|
|
||||||
self.signUpButton.isEnabled = !isRegistering
|
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
@ -463,7 +459,7 @@ extension MastodonRegisterViewController {
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.viewModel.invite.value = self.inviteTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
self.viewModel.reason.value = self.inviteTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
@ -471,11 +467,6 @@ extension MastodonRegisterViewController {
|
||||||
signUpButton.addTarget(self, action: #selector(MastodonRegisterViewController.signUpButtonPressed(_:)), for: .touchUpInside)
|
signUpButton.addTarget(self, action: #selector(MastodonRegisterViewController.signUpButtonPressed(_:)), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
navigationController?.setNavigationBarHidden(false, animated: false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonRegisterViewController: UITextFieldDelegate {
|
extension MastodonRegisterViewController: UITextFieldDelegate {
|
||||||
|
@ -493,7 +484,7 @@ extension MastodonRegisterViewController: UITextFieldDelegate {
|
||||||
case passwordTextField:
|
case passwordTextField:
|
||||||
viewModel.password.value = text
|
viewModel.password.value = text
|
||||||
case inviteTextField:
|
case inviteTextField:
|
||||||
viewModel.invite.value = text
|
viewModel.reason.value = text
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -536,13 +527,24 @@ extension MastodonRegisterViewController {
|
||||||
let username = viewModel.username.value
|
let username = viewModel.username.value
|
||||||
let email = viewModel.email.value
|
let email = viewModel.email.value
|
||||||
let password = viewModel.password.value
|
let password = viewModel.password.value
|
||||||
|
let query = Mastodon.API.Account.RegisterQuery(
|
||||||
|
reason: viewModel.reason.value,
|
||||||
|
username: username,
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
agreement: true, // TODO:
|
||||||
|
locale: "en" // TODO:
|
||||||
|
)
|
||||||
|
|
||||||
if let rules = viewModel.instance.rules, !rules.isEmpty {
|
if let rules = viewModel.instance.rules, !rules.isEmpty {
|
||||||
// show server rules before register
|
// show server rules before register
|
||||||
let mastodonServerRulesViewModel = MastodonServerRulesViewModel(
|
let mastodonServerRulesViewModel = MastodonServerRulesViewModel(
|
||||||
context: context,
|
context: context,
|
||||||
domain: viewModel.domain,
|
domain: viewModel.domain,
|
||||||
rules: rules
|
authenticateInfo: viewModel.authenticateInfo,
|
||||||
|
rules: rules,
|
||||||
|
registerQuery: query,
|
||||||
|
applicationAuthorization: viewModel.applicationAuthorization
|
||||||
)
|
)
|
||||||
|
|
||||||
viewModel.isRegistering.value = false
|
viewModel.isRegistering.value = false
|
||||||
|
@ -551,51 +553,28 @@ extension MastodonRegisterViewController {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
// register without show server rules
|
// register without show server rules
|
||||||
|
context.apiService.accountRegister(
|
||||||
|
domain: viewModel.domain,
|
||||||
|
query: query,
|
||||||
|
authorization: viewModel.applicationAuthorization
|
||||||
|
)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] completion in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.viewModel.isRegistering.value = false
|
||||||
|
switch completion {
|
||||||
|
case .failure(let error):
|
||||||
|
self.viewModel.error.send(error)
|
||||||
|
case .finished:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} receiveValue: { [weak self] response in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let userToken = response.value
|
||||||
|
let viewModel = MastodonConfirmEmailViewModel(context: self.context, email: email, authenticateInfo: self.viewModel.authenticateInfo, userToken: userToken)
|
||||||
|
self.coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = Mastodon.API.Account.RegisterQuery(
|
|
||||||
reason: viewModel.invite.value,
|
|
||||||
username: username,
|
|
||||||
email: email,
|
|
||||||
password: password,
|
|
||||||
agreement: true, // TODO:
|
|
||||||
locale: "en" // TODO:
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonRegisterViewController {
|
|
||||||
// func register(query: Mastodon.API.Account.RegisterQuery) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Token>, Error> {
|
|
||||||
// context.apiService.accountRegister(
|
|
||||||
// domain: viewModel.domain,
|
|
||||||
// query: query,
|
|
||||||
// authorization: viewModel.applicationAuthorization
|
|
||||||
// )
|
|
||||||
// .receive(on: DispatchQueue.main)
|
|
||||||
// .sink { [weak self] completion in
|
|
||||||
// guard let self = self else { return }
|
|
||||||
// self.viewModel.isRegistering.value = false
|
|
||||||
// switch completion {
|
|
||||||
// case .failure(let error):
|
|
||||||
// self.viewModel.error.send(error)
|
|
||||||
// case .finished:
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// } receiveValue: { [weak self] response in
|
|
||||||
// guard let self = self else { return }
|
|
||||||
// let userToken = response.value
|
|
||||||
//
|
|
||||||
// let alertController = UIAlertController(title: L10n.Scene.Register.success, message: L10n.Scene.Register.checkEmail, preferredStyle: .alert)
|
|
||||||
// let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) { [weak self] _ in
|
|
||||||
// guard let self = self else { return }
|
|
||||||
// let viewModel = MastodonConfirmEmailViewModel(context: self.context, email: email, authenticateInfo: self.viewModel.authenticateInfo, userToken: userToken)
|
|
||||||
// self.coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show)
|
|
||||||
// }
|
|
||||||
// alertController.addAction(okAction)
|
|
||||||
// self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
|
|
||||||
// }
|
|
||||||
// .store(in: &disposeBag)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,33 +23,19 @@ final class MastodonRegisterViewModel {
|
||||||
let displayName = CurrentValueSubject<String, Never>("")
|
let displayName = CurrentValueSubject<String, Never>("")
|
||||||
let email = CurrentValueSubject<String, Never>("")
|
let email = CurrentValueSubject<String, Never>("")
|
||||||
let password = CurrentValueSubject<String, Never>("")
|
let password = CurrentValueSubject<String, Never>("")
|
||||||
let invite = CurrentValueSubject<String, Never>("")
|
let reason = CurrentValueSubject<String, Never>("")
|
||||||
|
|
||||||
let isUsernameValidateDalay = CurrentValueSubject<Bool, Never>(true)
|
|
||||||
let isDisplayNameValidateDalay = CurrentValueSubject<Bool, Never>(true)
|
|
||||||
let isEmailValidateDalay = CurrentValueSubject<Bool, Never>(true)
|
|
||||||
let isPasswordValidateDalay = CurrentValueSubject<Bool, Never>(true)
|
|
||||||
let isInviteValidateDelay = CurrentValueSubject<Bool, Never>(true)
|
|
||||||
let isRegistering = CurrentValueSubject<Bool, Never>(false)
|
|
||||||
|
|
||||||
// output
|
// output
|
||||||
lazy var approvalRequired: Bool = {
|
let approvalRequired: Bool
|
||||||
if let approvalRequired = instance.approvalRequired {
|
|
||||||
return approvalRequired
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}()
|
|
||||||
|
|
||||||
let applicationAuthorization: Mastodon.API.OAuth.Authorization
|
let applicationAuthorization: Mastodon.API.OAuth.Authorization
|
||||||
|
|
||||||
let usernameValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
let usernameValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
||||||
let displayNameValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
let displayNameValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
||||||
let emailValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
let emailValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
||||||
let passwordValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
let passwordValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
||||||
let inviteValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
let inviteValidateState = CurrentValueSubject<ValidateState, Never>(.empty)
|
||||||
|
|
||||||
|
let isRegistering = CurrentValueSubject<Bool, Never>(false)
|
||||||
let isAllValid = CurrentValueSubject<Bool, Never>(false)
|
let isAllValid = CurrentValueSubject<Bool, Never>(false)
|
||||||
|
|
||||||
let error = CurrentValueSubject<Error?, Never>(nil)
|
let error = CurrentValueSubject<Error?, Never>(nil)
|
||||||
|
|
||||||
init(
|
init(
|
||||||
|
@ -62,6 +48,7 @@ final class MastodonRegisterViewModel {
|
||||||
self.authenticateInfo = authenticateInfo
|
self.authenticateInfo = authenticateInfo
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.applicationToken = applicationToken
|
self.applicationToken = applicationToken
|
||||||
|
self.approvalRequired = instance.approvalRequired ?? false
|
||||||
self.applicationAuthorization = Mastodon.API.OAuth.Authorization(accessToken: applicationToken.accessToken)
|
self.applicationAuthorization = Mastodon.API.OAuth.Authorization(accessToken: applicationToken.accessToken)
|
||||||
|
|
||||||
username
|
username
|
||||||
|
@ -107,7 +94,7 @@ final class MastodonRegisterViewModel {
|
||||||
.assign(to: \.value, on: passwordValidateState)
|
.assign(to: \.value, on: passwordValidateState)
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
if approvalRequired {
|
if approvalRequired {
|
||||||
invite
|
reason
|
||||||
.map { invite in
|
.map { invite in
|
||||||
guard !invite.isEmpty else { return .empty }
|
guard !invite.isEmpty else { return .empty }
|
||||||
return .valid
|
return .valid
|
||||||
|
|
|
@ -10,7 +10,8 @@ import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
final class MastodonResendEmailViewController: UIViewController, NeedsDependency, WKNavigationDelegate {
|
final class MastodonResendEmailViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ final class MastodonResendEmailViewController: UIViewController, NeedsDependency
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonResendEmailViewController {
|
extension MastodonResendEmailViewController {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import os.log
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
final class MastodonResendEmailViewModel {
|
final class MastodonResendEmailViewModel {
|
||||||
|
|
||||||
// input
|
// input
|
||||||
let resendEmailURL: URL
|
let resendEmailURL: URL
|
||||||
let email: String
|
let email: String
|
||||||
|
@ -25,6 +26,7 @@ final class MastodonResendEmailViewModel {
|
||||||
deinit {
|
deinit {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", (#file as NSString).lastPathComponent, #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", (#file as NSString).lastPathComponent, #line, #function)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
extension MastodonResendEmailViewModel {
|
extension MastodonResendEmailViewModel {
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,12 @@
|
||||||
|
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
|
||||||
final class MastodonServerRulesViewController: UIViewController, NeedsDependency {
|
final class MastodonServerRulesViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
|
||||||
|
@ -56,7 +59,7 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let confirmButton: UIButton = {
|
let confirmButton: PrimaryActionButton = {
|
||||||
let button = PrimaryActionButton()
|
let button = PrimaryActionButton()
|
||||||
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
|
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
|
||||||
button.setTitleColor(.white, for: .normal)
|
button.setTitleColor(.white, for: .normal)
|
||||||
|
@ -69,6 +72,10 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
|
||||||
scrollView.alwaysBounceVertical = true
|
scrollView.alwaysBounceVertical = true
|
||||||
return scrollView
|
return scrollView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +116,7 @@ extension MastodonServerRulesViewController {
|
||||||
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
view.addSubview(scrollView)
|
view.addSubview(scrollView)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
scrollView.frameLayoutGuide.topAnchor.constraint(equalTo: view.topAnchor),
|
scrollView.frameLayoutGuide.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
|
||||||
scrollView.frameLayoutGuide.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
|
scrollView.frameLayoutGuide.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
|
||||||
view.readableContentGuide.trailingAnchor.constraint(equalTo: scrollView.frameLayoutGuide.trailingAnchor),
|
view.readableContentGuide.trailingAnchor.constraint(equalTo: scrollView.frameLayoutGuide.trailingAnchor),
|
||||||
scrollView.frameLayoutGuide.widthAnchor.constraint(equalTo: scrollView.contentLayoutGuide.widthAnchor),
|
scrollView.frameLayoutGuide.widthAnchor.constraint(equalTo: scrollView.contentLayoutGuide.widthAnchor),
|
||||||
|
@ -136,6 +143,14 @@ extension MastodonServerRulesViewController {
|
||||||
|
|
||||||
rulesLabel.attributedText = viewModel.rulesAttributedString
|
rulesLabel.attributedText = viewModel.rulesAttributedString
|
||||||
confirmButton.addTarget(self, action: #selector(MastodonServerRulesViewController.confirmButtonPressed(_:)), for: .touchUpInside)
|
confirmButton.addTarget(self, action: #selector(MastodonServerRulesViewController.confirmButtonPressed(_:)), for: .touchUpInside)
|
||||||
|
|
||||||
|
viewModel.isRegistering
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] isRegistering in
|
||||||
|
guard let self = self else { return }
|
||||||
|
isRegistering ? self.confirmButton.showLoading() : self.confirmButton.stopLoading()
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -143,7 +158,31 @@ extension MastodonServerRulesViewController {
|
||||||
extension MastodonServerRulesViewController {
|
extension MastodonServerRulesViewController {
|
||||||
@objc private func confirmButtonPressed(_ sender: UIButton) {
|
@objc private func confirmButtonPressed(_ sender: UIButton) {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
|
||||||
|
let email = viewModel.registerQuery.email
|
||||||
|
|
||||||
|
context.apiService.accountRegister(
|
||||||
|
domain: viewModel.domain,
|
||||||
|
query: viewModel.registerQuery,
|
||||||
|
authorization: viewModel.applicationAuthorization
|
||||||
|
)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] completion in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.viewModel.isRegistering.value = false
|
||||||
|
switch completion {
|
||||||
|
case .failure(let error):
|
||||||
|
self.viewModel.error.send(error)
|
||||||
|
case .finished:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} receiveValue: { [weak self] response in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let userToken = response.value
|
||||||
|
let viewModel = MastodonConfirmEmailViewModel(context: self.context, email: email, authenticateInfo: self.viewModel.authenticateInfo, userToken: userToken)
|
||||||
|
self.coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Combine
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
final class MastodonServerRulesViewModel {
|
final class MastodonServerRulesViewModel {
|
||||||
|
@ -13,12 +14,30 @@ final class MastodonServerRulesViewModel {
|
||||||
// input
|
// input
|
||||||
let context: AppContext
|
let context: AppContext
|
||||||
let domain: String
|
let domain: String
|
||||||
|
let authenticateInfo: AuthenticationViewModel.AuthenticateInfo
|
||||||
let rules: [Mastodon.Entity.Instance.Rule]
|
let rules: [Mastodon.Entity.Instance.Rule]
|
||||||
|
let registerQuery: Mastodon.API.Account.RegisterQuery
|
||||||
|
let applicationAuthorization: Mastodon.API.OAuth.Authorization
|
||||||
|
|
||||||
|
// output
|
||||||
|
let isRegistering = CurrentValueSubject<Bool, Never>(false)
|
||||||
|
let error = CurrentValueSubject<Error?, Never>(nil)
|
||||||
|
|
||||||
|
|
||||||
init(context: AppContext, domain: String, rules: [Mastodon.Entity.Instance.Rule]) {
|
init(
|
||||||
|
context: AppContext,
|
||||||
|
domain: String,
|
||||||
|
authenticateInfo: AuthenticationViewModel.AuthenticateInfo,
|
||||||
|
rules: [Mastodon.Entity.Instance.Rule],
|
||||||
|
registerQuery: Mastodon.API.Account.RegisterQuery,
|
||||||
|
applicationAuthorization: Mastodon.API.OAuth.Authorization
|
||||||
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
|
self.authenticateInfo = authenticateInfo
|
||||||
self.rules = rules
|
self.rules = rules
|
||||||
|
self.registerQuery = registerQuery
|
||||||
|
self.applicationAuthorization = applicationAuthorization
|
||||||
}
|
}
|
||||||
|
|
||||||
var rulesAttributedString: NSAttributedString {
|
var rulesAttributedString: NSAttributedString {
|
||||||
|
@ -32,9 +51,6 @@ final class MastodonServerRulesViewModel {
|
||||||
attributedString.append(indexString)
|
attributedString.append(indexString)
|
||||||
attributedString.append(ruleString)
|
attributedString.append(ruleString)
|
||||||
}
|
}
|
||||||
// let paragraphStyle = NSMutableParagraphStyle()
|
|
||||||
// paragraphStyle.lineSpacing = 20
|
|
||||||
// attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
|
|
||||||
return attributedString
|
return attributedString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ protocol OnboardingViewControllerAppearance: UIViewController {
|
||||||
extension OnboardingViewControllerAppearance {
|
extension OnboardingViewControllerAppearance {
|
||||||
|
|
||||||
static var actionButtonHeight: CGFloat { return 46 }
|
static var actionButtonHeight: CGFloat { return 46 }
|
||||||
|
static var actionButtonMargin: CGFloat { return 12 }
|
||||||
static var viewBottomPaddingHeight: CGFloat { return 11 }
|
static var viewBottomPaddingHeight: CGFloat { return 11 }
|
||||||
|
|
||||||
func setupOnboardingAppearance() {
|
func setupOnboardingAppearance() {
|
|
@ -47,6 +47,11 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
|
||||||
button.translatesAutoresizingMaskIntoConstraints = false
|
button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension WelcomeViewController {
|
extension WelcomeViewController {
|
||||||
|
@ -74,14 +79,14 @@ extension WelcomeViewController {
|
||||||
view.addSubview(signInButton)
|
view.addSubview(signInButton)
|
||||||
view.addSubview(signUpButton)
|
view.addSubview(signUpButton)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
signInButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 12),
|
signInButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: WelcomeViewController.actionButtonMargin),
|
||||||
view.readableContentGuide.trailingAnchor.constraint(equalTo: signInButton.trailingAnchor, constant: 12),
|
view.readableContentGuide.trailingAnchor.constraint(equalTo: signInButton.trailingAnchor, constant: WelcomeViewController.actionButtonMargin),
|
||||||
view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight),
|
view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight),
|
||||||
signInButton.heightAnchor.constraint(equalToConstant: 46).priority(.defaultHigh),
|
signInButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.defaultHigh),
|
||||||
|
|
||||||
signInButton.topAnchor.constraint(equalTo: signUpButton.bottomAnchor, constant: 9),
|
signInButton.topAnchor.constraint(equalTo: signUpButton.bottomAnchor, constant: 9),
|
||||||
signUpButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 12),
|
signUpButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: WelcomeViewController.actionButtonMargin),
|
||||||
view.readableContentGuide.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor, constant: 12),
|
view.readableContentGuide.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor, constant: WelcomeViewController.actionButtonMargin),
|
||||||
signUpButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.defaultHigh),
|
signUpButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.defaultHigh),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -89,19 +94,28 @@ extension WelcomeViewController {
|
||||||
signInButton.addTarget(self, action: #selector(signInButtonDidClicked(_:)), for: .touchUpInside)
|
signInButton.addTarget(self, action: #selector(signInButtonDidClicked(_:)), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override var preferredStatusBarStyle: UIStatusBarStyle { return .darkContent }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension WelcomeViewController {
|
extension WelcomeViewController {
|
||||||
@objc
|
@objc
|
||||||
private func signUpButtonDidClicked(_ sender: UIButton) {
|
private func signUpButtonDidClicked(_ sender: UIButton) {
|
||||||
coordinator.present(scene: .pickServer(viewMode: PickServerViewModel(context: context, mode: .signUp)), from: self, transition: .show)
|
coordinator.present(scene: .mastodonPickServer(viewMode: MastodonPickServerViewModel(context: context, mode: .signUp)), from: self, transition: .show)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
private func signInButtonDidClicked(_ sender: UIButton) {
|
private func signInButtonDidClicked(_ sender: UIButton) {
|
||||||
coordinator.present(scene: .pickServer(viewMode: PickServerViewModel(context: context, mode: .signIn)), from: self, transition: .show)
|
coordinator.present(scene: .mastodonPickServer(viewMode: MastodonPickServerViewModel(context: context, mode: .signIn)), from: self, transition: .show)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - OnboardingViewControllerAppearance
|
// MARK: - OnboardingViewControllerAppearance
|
||||||
extension WelcomeViewController: OnboardingViewControllerAppearance { }
|
extension WelcomeViewController: OnboardingViewControllerAppearance { }
|
||||||
|
|
||||||
|
// MARK: - UIAdaptivePresentationControllerDelegate
|
||||||
|
extension WelcomeViewController: UIAdaptivePresentationControllerDelegate {
|
||||||
|
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
||||||
|
return .fullScreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
//
|
||||||
|
// DarkContentStatusBarStyleNavigationController.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021-2-26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
final class DarkContentStatusBarStyleNavigationController: UINavigationController {
|
||||||
|
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||||
|
return .darkContent
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,22 +25,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
self.coordinator = sceneCoordinator
|
self.coordinator = sceneCoordinator
|
||||||
|
|
||||||
sceneCoordinator.setup()
|
sceneCoordinator.setup()
|
||||||
|
sceneCoordinator.setupOnboardingIfNeeds(animated: false)
|
||||||
// do {
|
|
||||||
// let request = MastodonAuthentication.sortedFetchRequest
|
|
||||||
// if try appContext.managedObjectContext.fetch(request).isEmpty {
|
|
||||||
// DispatchQueue.main.async {
|
|
||||||
// sceneCoordinator.present(
|
|
||||||
// scene: .welcome,
|
|
||||||
// from: nil,
|
|
||||||
// transition: .modal(animated: false, completion: nil)
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch {
|
|
||||||
// assertionFailure(error.localizedDescription)
|
|
||||||
// }
|
|
||||||
|
|
||||||
window.makeKeyAndVisible()
|
window.makeKeyAndVisible()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ extension Mastodon.API.Error: LocalizedError {
|
||||||
|
|
||||||
public var errorDescription: String? {
|
public var errorDescription: String? {
|
||||||
guard let mastodonError = mastodonError else {
|
guard let mastodonError = mastodonError else {
|
||||||
return nil
|
return "HTTP \(httpResponseStatus.code)"
|
||||||
}
|
}
|
||||||
switch mastodonError {
|
switch mastodonError {
|
||||||
case .generic(let error):
|
case .generic(let error):
|
||||||
|
@ -49,7 +49,7 @@ extension Mastodon.API.Error: LocalizedError {
|
||||||
|
|
||||||
public var failureReason: String? {
|
public var failureReason: String? {
|
||||||
guard let mastodonError = mastodonError else {
|
guard let mastodonError = mastodonError else {
|
||||||
return nil
|
return httpResponseStatus.reasonPhrase
|
||||||
}
|
}
|
||||||
switch mastodonError {
|
switch mastodonError {
|
||||||
case .generic(let error):
|
case .generic(let error):
|
||||||
|
|
Loading…
Reference in New Issue