diff --git a/Localization/app.json b/Localization/app.json index ce5ba81e0..e20e901db 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -22,8 +22,8 @@ "cancel": "Cancel", "take_photo": "Take photo", "save_photo": "Save photo", - "sign_in": "Sign in", - "sign_up": "Sign up", + "sign_in": "Sign In", + "sign_up": "Sign Up", "see_more": "See More", "preview": "Preview", "open_in_safari": "Open in Safari" @@ -110,7 +110,7 @@ "dont_receive_email": { "title": "Check your email", "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": { "title": "Check your inbox.", diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index d8fff17c7..85576315e 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -10,8 +10,8 @@ 0FAA0FDF25E0B57E0017CCDE /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA0FDE25E0B57E0017CCDE /* WelcomeViewController.swift */; }; 0FAA101225E105390017CCDE /* PrimaryActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA101125E105390017CCDE /* PrimaryActionButton.swift */; }; 0FAA101C25E10E760017CCDE /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA101B25E10E760017CCDE /* UIFont.swift */; }; - 0FAA102725E1126A0017CCDE /* PickServerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA102625E1126A0017CCDE /* PickServerViewController.swift */; }; - 0FB3D2F725E4C24D00AAD544 /* PickServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2F625E4C24D00AAD544 /* PickServerViewModel.swift */; }; + 0FAA102725E1126A0017CCDE /* MastodonPickServerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */; }; + 0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */; }; 0FB3D2FE25E4CB6400AAD544 /* PickServerTitleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2FD25E4CB6400AAD544 /* PickServerTitleCell.swift */; }; 0FB3D30825E524C600AAD544 /* PickServerCategoriesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D30725E524C600AAD544 /* PickServerCategoriesCell.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 */; }; 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 */; }; - DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB01409525C40B6700F9F3CF /* AuthenticationViewController.swift */; }; DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */; }; DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.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 */; }; DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB2B3ABE25E37E15007045F9 /* InfoPlist.strings */; }; 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 */; }; DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; }; DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DD525BAA00100D1B89D /* AppDelegate.swift */; }; @@ -118,6 +118,9 @@ DB5086B825CC0D6400C2C187 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DB5086B725CC0D6400C2C187 /* Kingfisher */; }; DB5086BE25CC0D9900C2C187 /* SplashPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */; }; DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */; }; + DB68A04A25E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68A04925E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift */; }; + DB68A05D25E9055900CFDF14 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DB68A05C25E9055900CFDF14 /* Settings.bundle */; }; + DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68A06225E905E000CFDF14 /* UIApplication.swift */; }; DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; }; DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; }; DB89B9F725C10FD0008580ED /* CoreDataStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; }; @@ -142,7 +145,6 @@ DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */; }; DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.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 */; }; 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 */; }; @@ -217,8 +219,8 @@ 0FAA0FDE25E0B57E0017CCDE /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = ""; }; 0FAA101125E105390017CCDE /* PrimaryActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryActionButton.swift; sourceTree = ""; }; 0FAA101B25E10E760017CCDE /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = ""; }; - 0FAA102625E1126A0017CCDE /* PickServerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerViewController.swift; sourceTree = ""; }; - 0FB3D2F625E4C24D00AAD544 /* PickServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerViewModel.swift; sourceTree = ""; }; + 0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewController.swift; sourceTree = ""; }; + 0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewModel.swift; sourceTree = ""; }; 0FB3D2FD25E4CB6400AAD544 /* PickServerTitleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerTitleCell.swift; sourceTree = ""; }; 0FB3D30725E524C600AAD544 /* PickServerCategoriesCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoriesCell.swift; sourceTree = ""; }; 0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoryView.swift; sourceTree = ""; }; @@ -292,7 +294,6 @@ 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 = ""; }; 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 = ""; }; DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewController.swift; sourceTree = ""; }; DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift; sourceTree = ""; }; DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModel.swift; sourceTree = ""; }; @@ -303,6 +304,7 @@ DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightDimmableButton.swift; sourceTree = ""; }; DB2B3ABD25E37E15007045F9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; DB2B3AE825E38850007045F9 /* UIViewPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewPreview.swift; sourceTree = ""; }; + DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = ""; }; DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = ""; }; DB3D100E25BAA75E00EAA174 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; DB427DD225BAA00100D1B89D /* Mastodon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mastodon.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -331,6 +333,9 @@ DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarConfigurableView.swift; sourceTree = ""; }; DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashPreference.swift; sourceTree = ""; }; DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSKeyValueObservation.swift; sourceTree = ""; }; + DB68A04925E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarkContentStatusBarStyleNavigationController.swift; sourceTree = ""; }; + DB68A05C25E9055900CFDF14 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; + DB68A06225E905E000CFDF14 /* UIApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIApplication.swift; sourceTree = ""; }; DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = ""; }; DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = ""; }; DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreDataStack.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -357,7 +362,6 @@ DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = ""; }; DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineIndex.swift; sourceTree = ""; }; - DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = ""; }; DB98336A25C9420100AD9700 /* APIService+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+App.swift"; sourceTree = ""; }; DB98337025C9443200AD9700 /* APIService+Authentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Authentication.swift"; sourceTree = ""; }; DB98337E25C9452D00AD9700 /* APIService+APIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+APIError.swift"; sourceTree = ""; }; @@ -445,8 +449,8 @@ 0FB3D31825E525DE00AAD544 /* CollectionViewCell */, 0FB3D30D25E525C000AAD544 /* View */, 0FB3D2FC25E4CB4B00AAD544 /* TableViewCell */, - 0FAA102625E1126A0017CCDE /* PickServerViewController.swift */, - 0FB3D2F625E4C24D00AAD544 /* PickServerViewModel.swift */, + 0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */, + 0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */, ); path = PickServer; sourceTree = ""; @@ -598,7 +602,6 @@ 2D38F1C525CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift */, 2D38F20725CD491300561493 /* DisposeBagCollectable.swift */, 2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */, - 2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */, ); path = Protocol; sourceTree = ""; @@ -636,6 +639,7 @@ 2D7631A425C1532200929FB9 /* Share */ = { isa = PBXGroup; children = ( + DB68A04F25E9028800CFDF14 /* NavigationController */, DB9D6C2025E502C60051B173 /* ViewModel */, 2D7631A525C1532D00929FB9 /* View */, ); @@ -692,28 +696,29 @@ name = Frameworks; sourceTree = ""; }; - DB01409B25C40BB600F9F3CF /* Authentication */ = { + DB01409B25C40BB600F9F3CF /* Onboarding */ = { isa = PBXGroup; children = ( - 2D364F7025E66D5B00204FDC /* ResendEmail */, - 2D59819925E4A55C000FB903 /* ConfirmEmail */, - DB0140A625C40C0900F9F3CF /* PinBased */, + DB68A03825E900CC00CFDF14 /* Share */, + 0FAA0FDD25E0B5700017CCDE /* Welcome */, + 0FAA102525E1125D0017CCDE /* PickServer */, + DB0140A625C40C0900F9F3CF /* PinBasedAuthentication */, DBE0821A25CD382900FD6BBD /* Register */, DB72602125E36A2500235243 /* ServerRules */, - DB01409525C40B6700F9F3CF /* AuthenticationViewController.swift */, - DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */, + 2D364F7025E66D5B00204FDC /* ResendEmail */, + 2D59819925E4A55C000FB903 /* ConfirmEmail */, ); - path = Authentication; + path = Onboarding; sourceTree = ""; }; - DB0140A625C40C0900F9F3CF /* PinBased */ = { + DB0140A625C40C0900F9F3CF /* PinBasedAuthentication */ = { isa = PBXGroup; children = ( DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */, DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */, DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */, ); - path = PinBased; + path = PinBasedAuthentication; sourceTree = ""; }; DB084B5125CBC56300F898ED /* CoreDataStack */ = { @@ -733,6 +738,7 @@ DB427DD725BAA00100D1B89D /* SceneDelegate.swift */, DB427DDB25BAA00100D1B89D /* Main.storyboard */, DB427DE025BAA00100D1B89D /* LaunchScreen.storyboard */, + DB68A05C25E9055900CFDF14 /* Settings.bundle */, ); path = "Supporting Files"; sourceTree = ""; @@ -854,6 +860,23 @@ path = Preference; sourceTree = ""; }; + DB68A03825E900CC00CFDF14 /* Share */ = { + isa = PBXGroup; + children = ( + 2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */, + DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */, + ); + path = Share; + sourceTree = ""; + }; + DB68A04F25E9028800CFDF14 /* NavigationController */ = { + isa = PBXGroup; + children = ( + DB68A04925E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift */, + ); + path = NavigationController; + sourceTree = ""; + }; DB72602125E36A2500235243 /* ServerRules */ = { isa = PBXGroup; children = ( @@ -956,9 +979,7 @@ children = ( 2D7631A425C1532200929FB9 /* Share */, DB8AF54E25C13703002E6C99 /* MainTab */, - 0FAA0FDD25E0B5700017CCDE /* Welcome */, - 0FAA102525E1125D0017CCDE /* PickServer */, - DB01409B25C40BB600F9F3CF /* Authentication */, + DB01409B25C40BB600F9F3CF /* Onboarding */, 2D38F1D325CD463600561493 /* HomeTimeline */, 2D76316325C14BAC00929FB9 /* PublicTimeline */, DB9D6BEE25E4F5370051B173 /* Search */, @@ -980,6 +1001,7 @@ 2DF123A625C3B0210020F248 /* ActiveLabel.swift */, DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */, 2D42FF6A25C817D2004A627A /* MastodonContent.swift */, + DB68A06225E905E000CFDF14 /* UIApplication.swift */, DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */, 2D42FF8E25C8228A004A627A /* UIButton.swift */, DB45FAD625CA6C76005A8AC7 /* UIBarButtonItem.swift */, @@ -1249,6 +1271,7 @@ DB427DDD25BAA00100D1B89D /* Main.storyboard in Resources */, DB118A8225E4B6E600FAB162 /* Preview Assets.xcassets in Resources */, DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */, + DB68A05D25E9055900CFDF14 /* Settings.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1412,7 +1435,7 @@ 2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */, DB45FB1D25CA9D23005A8AC7 /* APIService+HomeTimeline.swift in Sources */, 2D7631B325C159F700929FB9 /* Item.swift in Sources */, - 0FB3D2F725E4C24D00AAD544 /* PickServerViewModel.swift in Sources */, + 0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */, 2D61335E25C1894B00CAE157 /* APIService.swift in Sources */, 2D38F1F725CD47AC00561493 /* HomeTimelineViewModel+LoadOldestState.swift in Sources */, 0FB3D33225E5F50E00AAD544 /* PickServerSearchCell.swift in Sources */, @@ -1422,12 +1445,13 @@ DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */, 2D364F7225E66D7500204FDC /* MastodonResendEmailViewController.swift in Sources */, 2D38F1F125CD477D00561493 /* HomeTimelineViewModel+LoadMiddleState.swift in Sources */, + DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */, DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */, 2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */, 2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */, DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */, 2D42FF8F25C8228A004A627A /* UIButton.swift in Sources */, - 0FAA102725E1126A0017CCDE /* PickServerViewController.swift in Sources */, + 0FAA102725E1126A0017CCDE /* MastodonPickServerViewController.swift in Sources */, DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */, 2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */, DB45FAE325CA7181005A8AC7 /* MastodonUser.swift in Sources */, @@ -1467,13 +1491,14 @@ DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */, 2D38F1C625CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift in Sources */, 0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */, + DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */, + DB68A04A25E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift in Sources */, 0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */, 2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */, DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */, DB9D6BF825E4F5690051B173 /* NotificationViewController.swift in Sources */, DB45FADD25CA6F6B005A8AC7 /* APIService+CoreData+MastodonUser.swift in Sources */, 2D32EABA25CB9B0500C9ED86 /* UIView.swift in Sources */, - DB98334725C8056600AD9700 /* AuthenticationViewModel.swift in Sources */, 2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */, DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */, 2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */, @@ -1504,7 +1529,6 @@ 2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */, 2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */, 2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */, - DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */, DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */, DB5086AB25CC0BBB00C2C187 /* AvatarConfigurableView.swift in Sources */, DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */, @@ -1513,7 +1537,6 @@ DB9D6BFF25E4F5940051B173 /* ProfileViewController.swift in Sources */, 0FB3D30825E524C600AAD544 /* PickServerCategoriesCell.swift in Sources */, 2D38F1FE25CD481700561493 /* StatusProvider.swift in Sources */, - 2D5A3D1125CF87AA002347D6 /* AvatarBarButtonItem.swift in Sources */, 0FB3D31E25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift in Sources */, DB45FB0F25CA87D0005A8AC7 /* AuthenticationService.swift in Sources */, ); @@ -1761,13 +1784,14 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 7LFDZ96332; INFOPLIST_FILE = Mastodon/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 0.1.0; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.Mastodon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1786,13 +1810,14 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 7LFDZ96332; INFOPLIST_FILE = Mastodon/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 0.1.0; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.Mastodon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 2addff9f9..026fc28dc 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,17 @@ CoreDataStack.xcscheme_^#shared#^_ orderHint - 8 + 11 + + Mastodon - RTL.xcscheme_^#shared#^_ + + orderHint + 9 + + Mastodon - Release.xcscheme_^#shared#^_ + + orderHint + 1 Mastodon.xcscheme_^#shared#^_ diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 304facf8a..fa2963386 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -38,44 +38,61 @@ extension SceneCoordinator { } enum Scene { + // onboarding case welcome - case pickServer(viewMode: PickServerViewModel) - case authentication(viewModel: AuthenticationViewModel) + case mastodonPickServer(viewMode: MastodonPickServerViewModel) case mastodonPinBasedAuthentication(viewModel: MastodonPinBasedAuthenticationViewModel) case mastodonRegister(viewModel: MastodonRegisterViewModel) case mastodonServerRules(viewModel: MastodonServerRulesViewModel) case mastodonConfirmEmail(viewModel: MastodonConfirmEmailViewModel) case mastodonResendEmail(viewModel: MastodonResendEmailViewModel) + // misc case alertController(alertController: UIAlertController) #if DEBUG case publicTimeline #endif + + var isOnboarding: Bool { + switch self { + case .welcome, + .mastodonPickServer, + .mastodonPinBasedAuthentication, + .mastodonRegister, + .mastodonServerRules, + .mastodonConfirmEmail, + .mastodonResendEmail: + return true + default: + return false + } + } } } extension SceneCoordinator { func setup() { - // Check user authentication status - - let request = MastodonAuthentication.sortedFetchRequest + let viewController = MainTabBarController(context: appContext, coordinator: self) + sceneDelegate.window?.rootViewController = viewController + } + + func setupOnboardingIfNeeds(animated: Bool) { + // Check user authentication status and show onboarding if needs do { - let fetchResult = try appContext.managedObjectContext.fetch(request) - DispatchQueue.main.async { - var rootViewController: UIViewController - if fetchResult.isEmpty { - let welcomViewController = WelcomeViewController() - self.setupDependency(for: welcomViewController) - rootViewController = UINavigationController(rootViewController: welcomViewController) - } else { - rootViewController = MainTabBarController(context: self.appContext, coordinator: self) + let request = MastodonAuthentication.sortedFetchRequest + if try appContext.managedObjectContext.fetch(request).isEmpty { + DispatchQueue.main.async { + self.present( + scene: .welcome, + from: nil, + transition: .modal(animated: animated, completion: nil) + ) } - self.sceneDelegate.window?.rootViewController = rootViewController } } catch { - assertionFailure("CoreDataStack error at app launch!") + assertionFailure(error.localizedDescription) } } @@ -103,7 +120,13 @@ extension SceneCoordinator { presentingViewController.showDetailViewController(navigationController, sender: sender) 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 { modalNavigationController.presentationController?.delegate = adaptivePresentationControllerDelegate } @@ -143,12 +166,8 @@ private extension SceneCoordinator { case .welcome: let _viewController = WelcomeViewController() viewController = _viewController - case .pickServer(let viewModel): - let _viewController = PickServerViewController() - _viewController.viewModel = viewModel - viewController = _viewController - case .authentication(let viewModel): - let _viewController = AuthenticationViewController() + case .mastodonPickServer(let viewModel): + let _viewController = MastodonPickServerViewController() _viewController.viewModel = viewModel viewController = _viewController case .mastodonPinBasedAuthentication(let viewModel): diff --git a/Mastodon/Extension/UIApplication.swift b/Mastodon/Extension/UIApplication.swift new file mode 100644 index 000000000..38080fdab --- /dev/null +++ b/Mastodon/Extension/UIApplication.swift @@ -0,0 +1,26 @@ +// +// UIApplication.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-2-26. +// + +import UIKit + +extension UIApplication { + + class func appVersion() -> String { + return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String + } + + class func appBuild() -> String { + return Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as! String + } + + class func versionBuild() -> String { + let version = appVersion(), build = appBuild() + + return version == build ? "v\(version)" : "v\(version) (\(build))" + } + +} diff --git a/Mastodon/Generated/Assets.swift b/Mastodon/Generated/Assets.swift index 3fa09e446..08507ed9d 100644 --- a/Mastodon/Generated/Assets.swift +++ b/Mastodon/Generated/Assets.swift @@ -39,6 +39,7 @@ internal enum Asset { } internal enum Button { internal static let actionToolbar = ColorAsset(name: "Colors/Button/action.toolbar") + internal static let disabled = ColorAsset(name: "Colors/Button/disabled") internal static let highlight = ColorAsset(name: "Colors/Button/highlight") } internal enum Icon { @@ -67,7 +68,10 @@ internal enum Asset { internal static let lightWhite = ColorAsset(name: "Colors/lightWhite") internal static let systemOrange = ColorAsset(name: "Colors/system.orange") } - internal static let welcomeLogo = ImageAsset(name: "welcome.logo") + internal enum Welcome { + internal static let mastodonLogo = ImageAsset(name: "Welcome/mastodon.logo") + internal static let mastodonLogoLarge = ImageAsset(name: "Welcome/mastodon.logo.large") + } } // swiftlint:enable identifier_name line_length nesting type_body_length type_name diff --git a/Mastodon/Generated/Strings.swift b/Mastodon/Generated/Strings.swift index 7223cfc73..47cbabab8 100644 --- a/Mastodon/Generated/Strings.swift +++ b/Mastodon/Generated/Strings.swift @@ -50,9 +50,9 @@ internal enum L10n { internal static let savePhoto = L10n.tr("Localizable", "Common.Controls.Actions.SavePhoto") /// See More 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") - /// Sign up + /// Sign Up internal static let signUp = L10n.tr("Localizable", "Common.Controls.Actions.SignUp") /// Take photo internal static let takePhoto = L10n.tr("Localizable", "Common.Controls.Actions.TakePhoto") @@ -101,7 +101,7 @@ internal enum L10n { internal enum DontReceiveEmail { /// 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") - /// Resend email + /// Resend Email internal static let resendEmail = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.ResendEmail") /// Check your email internal static let title = L10n.tr("Localizable", "Scene.ConfirmEmail.DontReceiveEmail.Title") diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index 82ee713a3..ff87c179a 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -18,6 +18,17 @@ $(MARKETING_VERSION) CFBundleVersion 1 + LSApplicationQueriesSchemes + + sparrow + googlegmail + x-dispatch + readdle-spark + airmail + ms-outlook + ymail + fastmail + LSRequiresIPhoneOS UIApplicationSceneManifest @@ -64,16 +75,5 @@ UIViewControllerBasedStatusBarAppearance - LSApplicationQueriesSchemes - - sparrow - googlegmail - x-dispatch - readdle-spark - airmail - ms-outlook - ymail - fastmail - diff --git a/Mastodon/Protocol/OnboardingViewControllerAppearance.swift b/Mastodon/Protocol/OnboardingViewControllerAppearance.swift deleted file mode 100644 index ea723e3cc..000000000 --- a/Mastodon/Protocol/OnboardingViewControllerAppearance.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// OnboardingViewControllerAppearance.swift -// Mastodon -// -// Created by sxiaojian on 2021/2/25. -// - -import UIKit - -protocol OnboardingViewControllerAppearance: UIViewController { - func setupOnboardingAppearance() -} - -extension OnboardingViewControllerAppearance { - func setupOnboardingAppearance() { - overrideUserInterfaceStyle = .light - view.backgroundColor = Asset.Colors.Background.onboardingBackground.color - - // set navigationBar transparent - let barAppearance = UINavigationBarAppearance() - barAppearance.configureWithTransparentBackground() - navigationController?.navigationBar.standardAppearance = barAppearance - navigationController?.navigationBar.compactAppearance = barAppearance - navigationController?.navigationBar.scrollEdgeAppearance = barAppearance - - let backItem = UIBarButtonItem() - backItem.title = L10n.Common.Controls.Actions.back - navigationController?.navigationBar.topItem?.backBarButtonItem = backItem - } -} diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Button/disabled.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Button/disabled.colorset/Contents.json new file mode 100644 index 000000000..78cde95fb --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Colors/Button/disabled.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.784", + "green" : "0.682", + "red" : "0.608" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Mastodon/Resources/Assets.xcassets/Welcome/Contents.json b/Mastodon/Resources/Assets.xcassets/Welcome/Contents.json index 73c00596a..6e965652d 100644 --- a/Mastodon/Resources/Assets.xcassets/Welcome/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Welcome/Contents.json @@ -2,5 +2,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "provides-namespace" : true } } diff --git a/Mastodon/Resources/Assets.xcassets/Welcome/welcome.logo.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.imageset/Contents.json similarity index 73% rename from Mastodon/Resources/Assets.xcassets/Welcome/welcome.logo.imageset/Contents.json rename to Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.imageset/Contents.json index bcfd775d6..31a9c2c27 100644 --- a/Mastodon/Resources/Assets.xcassets/Welcome/welcome.logo.imageset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Logotype (Full) 1.pdf", + "filename" : "mastodon.logo.pdf", "idiom" : "universal" } ], diff --git a/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.imageset/mastodon.logo.pdf b/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.imageset/mastodon.logo.pdf new file mode 100644 index 000000000..773ed5e77 --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.imageset/mastodon.logo.pdf @@ -0,0 +1,339 @@ +%PDF-1.7 + +1 0 obj + << /BBox [ 0.000000 0.000000 480.000000 119.097778 ] + /Resources << >> + /Subtype /Form + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Type /XObject + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.001709 -0.290527 cm +0.188235 0.533333 0.831373 scn +107.682541 47.532677 m +106.063889 39.212074 93.195923 30.106506 78.416977 28.341690 c +70.709908 27.421394 63.121937 26.576881 55.030510 26.946808 c +41.798027 27.553123 31.357124 30.104706 31.357124 30.104706 c +31.357124 28.818085 31.436523 27.591019 31.595320 26.443352 c +33.315022 13.385902 44.544495 12.602745 55.180283 12.238235 c +65.917122 11.870117 75.475624 14.885460 75.475624 14.885460 c +75.917725 5.177185 l +75.917725 5.177185 68.407349 1.147720 55.030510 0.406067 c +47.655472 0.000053 38.495773 0.590118 27.827501 3.414185 c +4.693666 9.538696 0.712913 34.197334 0.106597 59.224106 c +-0.079267 66.653275 0.034417 73.660202 0.034417 79.517647 c +0.034417 105.105621 16.798328 112.606972 16.798328 112.606972 c +25.250660 116.488472 39.757122 118.121559 54.837425 118.244263 c +55.207348 118.244263 l +70.287651 118.121559 84.801338 116.488472 93.255470 112.606972 c +93.255470 112.606972 110.019386 105.105621 110.019386 79.517647 c +110.019386 79.517647 110.230507 60.638844 107.682541 47.532677 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 23.245789 45.780518 cm +0.121569 0.137255 0.168627 scn +0.000000 39.648720 m +0.000000 43.373230 3.018947 46.392181 6.743458 46.392181 c +10.467969 46.392181 13.486919 43.373230 13.486919 39.648720 c +13.486919 35.924210 10.467969 32.905262 6.743458 32.905262 c +3.018947 32.905262 0.000000 35.924210 0.000000 39.648720 c +h +96.718201 31.461655 m +96.718201 0.478188 l +84.443916 0.478188 l +84.443916 30.553986 l +84.443916 36.893234 81.776840 40.110676 76.440903 40.110676 c +70.541954 40.110676 67.586166 36.292332 67.586166 28.745865 c +67.586166 12.286915 l +55.384064 12.286915 l +55.384064 28.745865 l +55.384064 36.294136 52.426468 40.110676 46.529324 40.110676 c +41.193382 40.110676 38.524509 36.893234 38.524509 30.553986 c +38.524509 0.481804 l +26.252031 0.481804 l +26.252031 31.465263 l +26.252031 37.795486 27.865263 42.828270 31.104361 46.550980 c +34.442707 50.273685 38.816845 52.181053 44.246616 52.181053 c +50.526318 52.181053 55.284817 49.768425 58.430077 44.939552 c +61.486919 39.814735 l +64.543762 44.939552 l +67.689026 49.768425 72.445709 52.182858 78.727219 52.182858 c +84.156990 52.182858 88.529327 50.273685 91.869476 46.552784 c +95.108574 42.828270 96.720009 37.795490 96.720009 31.463459 c +96.718201 31.461655 l +h +139.005112 16.060150 m +141.536835 18.736240 142.758499 22.105263 142.758499 26.170826 c +142.758499 30.236389 141.536835 33.605415 139.005112 36.184063 c +136.565414 38.860153 133.468872 40.148571 129.715500 40.148571 c +125.962112 40.148571 122.867371 38.860153 120.427673 36.184063 c +117.987968 33.605415 116.768120 30.236389 116.768120 26.170826 c +116.768120 22.107067 117.987968 18.736240 120.427673 16.060150 c +122.867371 13.483311 125.962112 12.194881 129.715500 12.194881 c +133.468872 12.194881 136.565414 13.483311 139.005112 16.060150 c +139.005112 16.060150 l +h +142.758499 50.953987 m +154.859543 50.953987 l +154.859543 1.389473 l +142.754898 1.389473 l +142.754898 7.236088 l +139.097153 2.378342 134.030075 -0.000008 127.463463 -0.000008 c +121.176544 -0.000008 115.827965 2.477585 111.323906 7.533829 c +106.915489 12.590069 104.665268 18.835487 104.665268 26.169022 c +104.665268 33.405113 106.915489 39.650528 111.323906 44.706768 c +115.827965 49.763008 121.176544 52.339851 127.463463 52.339851 c +134.030075 52.339851 139.097153 49.959702 142.754898 45.103760 c +142.754898 50.950378 l +142.758499 50.953987 l +h +195.581955 27.062256 m +199.147659 24.387970 200.930527 20.620148 200.836685 15.863457 c +200.836685 10.807217 199.053848 6.840900 195.394302 4.065559 c +191.734756 1.389473 187.326309 0.001801 181.977737 0.001801 c +172.314575 0.001801 165.746170 3.966316 162.274292 11.797894 c +172.783768 18.041504 l +174.191299 13.781052 177.286011 11.599396 181.977737 11.599396 c +186.294128 11.599396 188.452347 12.988873 188.452347 15.863457 c +188.452347 17.944057 185.637283 19.827969 179.913376 21.313084 c +177.755188 21.908573 175.972336 22.504059 174.566620 23.000301 c +172.596085 23.792480 170.907074 24.685715 169.499557 25.775639 c +166.027664 28.451729 164.246628 32.019249 164.246628 36.579250 c +164.246628 41.436996 165.933823 45.302254 169.311874 48.079399 c +172.783752 50.953987 177.004501 52.341656 182.071579 52.341656 c +190.141342 52.341656 196.051117 48.871578 199.898331 41.833984 c +189.578354 35.886318 l +188.076996 39.255341 185.543457 40.940754 182.071579 40.940754 c +178.412018 40.940754 176.630966 39.553085 176.630966 36.876991 c +176.630966 34.796391 179.444214 32.912483 185.168121 31.425564 c +189.578339 30.433083 193.048416 28.947971 195.581955 27.064060 c +195.581955 27.062256 l +h +234.052322 38.661655 m +223.449036 38.661655 l +223.449036 18.043308 l +223.449036 15.563908 224.389175 14.078796 226.170227 13.384060 c +227.483917 12.887817 230.111267 12.788570 234.052322 12.987068 c +234.052322 1.389473 l +225.890518 0.396988 219.978958 1.190971 216.507080 3.868866 c +213.037003 6.445709 211.346176 11.202404 211.346176 18.043308 c +211.346176 38.661655 l +203.184372 38.661655 l +203.184372 50.953987 l +211.346176 50.953987 l +211.346176 60.965416 l +223.449036 64.830681 l +223.449036 50.953987 l +234.052322 50.953987 l +234.052322 38.661655 l +234.052322 38.661655 l +h +272.614716 16.357895 m +275.054413 18.936543 276.274261 22.208122 276.274261 26.172634 c +276.274261 30.137146 275.054413 33.408722 272.614716 35.985565 c +270.176819 38.562408 267.174133 39.850830 263.514587 39.850830 c +259.855011 39.850830 256.854126 38.562408 254.414429 35.985565 c +252.068558 33.309475 250.848709 30.037895 250.848709 26.172634 c +250.848709 22.305565 252.068558 19.033985 254.414429 16.357895 c +256.854126 13.781052 259.855011 12.492626 263.514587 12.492626 c +267.174133 12.492626 270.176819 13.781052 272.614716 16.357895 c +h +245.875488 7.535637 m +241.091721 12.590076 238.745850 18.736240 238.745850 26.172634 c +238.745850 33.507973 241.091721 39.652328 245.875488 44.708572 c +250.661057 49.763008 256.570801 52.341656 263.514587 52.341656 c +270.458344 52.341656 276.368134 49.763008 281.153687 44.708572 c +285.939240 39.652328 288.377136 33.408722 288.377136 26.172634 c +288.377136 18.835491 285.939240 12.590076 281.153687 7.535637 c +276.368134 2.479401 270.552155 0.001801 263.514587 0.001801 c +256.476990 0.001801 250.661057 2.479401 245.875488 7.535637 c +h +328.818054 16.060150 m +331.257751 18.736240 332.475769 22.105263 332.475769 26.170826 c +332.475769 30.236389 331.257751 33.605415 328.818054 36.184063 c +326.378357 38.860153 323.281799 40.148571 319.528412 40.148571 c +315.775024 40.148571 312.680298 38.860153 310.146759 36.184063 c +307.708893 33.605415 306.487213 30.236389 306.487213 26.170826 c +306.487213 22.107067 307.708893 18.736240 310.146759 16.060150 c +312.680298 13.483311 315.870667 12.194881 319.528412 12.194881 c +323.281799 12.194881 326.378357 13.483311 328.818054 16.060150 c +328.818054 16.060150 l +h +332.475769 70.780151 m +344.580475 70.780151 l +344.580475 1.389473 l +332.475769 1.389473 l +332.475769 7.236088 l +328.911865 2.378342 323.844818 -0.000008 317.278198 -0.000008 c +310.991272 -0.000008 305.550690 2.477585 301.046631 7.533829 c +296.636414 12.590069 294.384369 18.835487 294.384369 26.169022 c +294.384369 33.405113 296.636414 39.650528 301.046631 44.706768 c +305.550690 49.763008 310.991272 52.339851 317.278198 52.339851 c +323.844818 52.339851 328.911865 49.959702 332.475769 45.103760 c +332.475769 70.780151 l +332.475769 70.780151 l +h +387.083923 16.357895 m +389.523621 18.936543 390.743469 22.208122 390.743469 26.172634 c +390.743469 30.137146 389.523621 33.408722 387.083923 35.985565 c +384.644226 38.562408 381.643311 39.850830 377.983765 39.850830 c +374.324219 39.850830 371.321503 38.562408 368.881805 35.985565 c +366.535950 33.309475 365.316101 30.037895 365.316101 26.172634 c +365.316101 22.305565 366.535950 19.033985 368.881805 16.357895 c +371.321503 13.781052 374.324219 12.492626 377.983765 12.492626 c +381.643311 12.492626 384.644226 13.781052 387.083923 16.357895 c +387.083923 16.357895 l +h +360.344666 7.535637 m +355.559082 12.590076 353.215027 18.736240 353.215027 26.172634 c +353.215027 33.507973 355.559082 39.652328 360.344666 44.708572 c +365.130219 49.763008 371.040009 52.341656 377.983765 52.341656 c +384.925720 52.341656 390.837280 49.763008 395.622864 44.708572 c +400.408417 39.652328 402.846313 33.408722 402.846313 26.172634 c +402.846313 18.835491 400.408417 12.590076 395.622864 7.535637 c +390.837280 2.479401 385.019562 0.001801 377.983765 0.001801 c +370.946167 0.001801 365.130219 2.479401 360.344666 7.535637 c +360.344666 7.535637 l +h +455.202423 31.822556 m +455.202423 1.389473 l +443.097748 1.389473 l +443.097748 30.236389 l +443.097748 33.507969 442.255035 35.985565 440.566010 37.869476 c +438.970825 39.553085 436.718811 40.446320 433.809937 40.446320 c +426.960022 40.446320 423.489929 36.382557 423.489929 28.153984 c +423.489929 1.389473 l +411.387054 1.389473 l +411.387054 50.953987 l +423.489929 50.953987 l +423.489929 45.403309 l +426.398773 50.060753 430.994904 52.341656 437.469482 52.341656 c +442.630371 52.341656 446.851135 50.556992 450.135345 46.888420 c +453.513397 43.221657 455.202423 38.264664 455.202423 31.820751 c +455.202423 31.822556 l +h +f +n +Q + +endstream +endobj + +2 0 obj + 9224 +endobj + +3 0 obj + << /BBox [ 0.000000 0.000000 480.000000 119.097778 ] + /Resources << >> + /Subtype /Form + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Type /XObject + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 119.097778 m +480.000000 119.097778 l +480.000000 0.000031 l +0.000000 0.000031 l +0.000000 119.097778 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 237 +endobj + +5 0 obj + << /XObject << /X1 1 0 R >> + /ExtGState << /E1 << /SMask << /Type /Mask + /G 3 0 R + /S /Alpha + >> + /Type /ExtGState + >> >> + >> +endobj + +6 0 obj + << /Length 7 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +/X1 Do +Q + +endstream +endobj + +7 0 obj + 46 +endobj + +8 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 480.000000 119.097778 ] + /Resources 5 0 R + /Contents 6 0 R + /Parent 9 0 R + >> +endobj + +9 0 obj + << /Kids [ 8 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +10 0 obj + << /Type /Catalog + /Pages 9 0 R + >> +endobj + +xref +0 11 +0000000000 65535 f +0000000010 00000 n +0000009484 00000 n +0000009507 00000 n +0000009994 00000 n +0000010016 00000 n +0000010314 00000 n +0000010416 00000 n +0000010437 00000 n +0000010612 00000 n +0000010686 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 10 0 R + /Size 11 +>> +startxref +10746 +%%EOF \ No newline at end of file diff --git a/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.large.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.large.imageset/Contents.json new file mode 100644 index 000000000..f41e77ffa --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.large.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "mastodon.large.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.large.imageset/mastodon.large.pdf b/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.large.imageset/mastodon.large.pdf new file mode 100644 index 000000000..b6244d04e --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Welcome/mastodon.logo.large.imageset/mastodon.large.pdf @@ -0,0 +1,339 @@ +%PDF-1.7 + +1 0 obj + << /BBox [ 0.000000 0.000000 960.000000 238.195496 ] + /Resources << >> + /Subtype /Form + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Type /XObject + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.003357 -0.580933 cm +0.188235 0.533333 0.831373 scn +215.365082 95.065247 m +212.127777 78.424042 186.391846 60.212906 156.833954 56.683273 c +141.419815 54.842682 126.243874 53.153656 110.061020 53.893509 c +83.596054 55.106140 62.714249 60.209290 62.714249 60.209290 c +62.714249 57.636063 62.873047 55.181931 63.190639 52.886597 c +66.630043 26.771698 89.088989 25.205383 110.360565 24.476364 c +131.834244 23.740112 150.951248 29.770813 150.951248 29.770813 c +151.835449 10.354263 l +151.835449 10.354263 136.814697 2.295334 110.061020 0.812027 c +95.310944 0.000000 76.991547 1.180130 55.655003 6.828262 c +9.387332 19.077271 1.425826 68.394562 0.213194 118.448097 c +-0.158535 133.306442 0.068834 147.320282 0.068834 159.035172 c +0.068834 210.211121 33.596657 225.213821 33.596657 225.213821 c +50.501320 232.976822 79.514244 236.242996 109.674850 236.488403 c +110.414696 236.488403 l +140.575302 236.242996 169.602676 232.976822 186.510941 225.213821 c +186.510941 225.213821 220.038773 210.211121 220.038773 159.035172 c +220.038773 159.035172 220.461014 121.277573 215.365082 95.065247 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 46.491440 91.561096 cm +0.121569 0.137255 0.168627 scn +0.000000 79.297432 m +0.000000 86.746460 6.037893 92.784363 13.486916 92.784363 c +20.935938 92.784363 26.973839 86.746460 26.973839 79.297432 c +26.973839 71.848412 20.935938 65.810516 13.486916 65.810516 c +6.037893 65.810516 0.000000 71.848412 0.000000 79.297432 c +h +193.436401 62.923302 m +193.436401 0.956360 l +168.887833 0.956360 l +168.887833 61.107964 l +168.887833 73.786461 163.553680 80.221344 152.881805 80.221344 c +141.083908 80.221344 135.172333 72.584648 135.172333 57.491714 c +135.172333 24.573814 l +110.768127 24.573814 l +110.768127 57.491714 l +110.768127 72.588257 104.852936 80.221344 93.058647 80.221344 c +82.386765 80.221344 77.049019 73.786461 77.049019 61.107964 c +77.049019 0.963593 l +52.504063 0.963593 l +52.504063 62.930511 l +52.504063 75.590965 55.730526 85.656540 62.208721 93.101944 c +68.885414 100.547363 77.633690 104.362106 88.493233 104.362106 c +101.052635 104.362106 110.569633 99.536835 116.860153 89.879089 c +122.973839 79.629471 l +129.087524 89.879089 l +135.378052 99.536835 144.891418 104.365707 157.454437 104.365707 c +168.313980 104.365707 177.058655 100.547363 183.738953 93.105560 c +190.217148 85.656540 193.440018 75.590973 193.440018 62.926910 c +193.436401 62.923302 l +h +278.010223 32.120285 m +283.073669 37.472473 285.516998 44.210510 285.516998 52.341637 c +285.516998 60.472771 283.073669 67.210823 278.010223 72.368118 c +273.130829 77.720299 266.937744 80.297134 259.431000 80.297134 c +251.924225 80.297134 245.734741 77.720299 240.855347 72.368118 c +235.975937 67.210823 233.536240 60.472771 233.536240 52.341637 c +233.536240 44.214119 235.975937 37.472473 240.855347 32.120285 c +245.734741 26.966606 251.924225 24.389748 259.431000 24.389748 c +266.937744 24.389748 273.130829 26.966606 278.010223 32.120285 c +278.010223 32.120285 l +h +285.516998 101.907967 m +309.719086 101.907967 l +309.719086 2.778915 l +285.509796 2.778915 l +285.509796 14.472160 l +278.194305 4.756668 268.060150 -0.000031 254.926926 -0.000031 c +242.353088 -0.000031 231.655930 4.955154 222.647812 15.067642 c +213.830978 25.180122 209.330536 37.670959 209.330536 52.338036 c +209.330536 66.810219 213.830978 79.301048 222.647812 89.413528 c +231.655930 99.526016 242.353088 104.679695 254.926926 104.679695 c +268.060150 104.679695 278.194305 99.919395 285.509796 90.207512 c +285.509796 101.900742 l +285.516998 101.907967 l +h +391.163910 54.124504 m +398.295319 48.775932 401.861053 41.240288 401.673370 31.726898 c +401.673370 21.614418 398.107697 13.681786 390.788605 8.131104 c +383.469513 2.778931 374.652618 0.003571 363.955475 0.003571 c +344.629150 0.003571 331.492340 7.932617 324.548584 23.595772 c +345.567535 36.082993 l +348.382599 27.562088 354.572021 23.198776 363.955475 23.198776 c +372.588257 23.198776 376.904694 25.977730 376.904694 31.726898 c +376.904694 35.888107 371.274567 39.655930 359.826752 42.626152 c +355.510376 43.817131 351.944672 45.008102 349.133240 46.000587 c +345.192169 47.584946 341.814148 49.371414 338.999115 51.551262 c +332.055328 56.903450 328.493256 64.038490 328.493256 73.158493 c +328.493256 82.873978 331.867645 90.604507 338.623749 96.158791 c +345.567505 101.907967 354.009003 104.683304 364.143158 104.683304 c +380.282684 104.683304 392.102234 97.743149 399.796661 83.667961 c +379.156708 71.772621 l +376.153992 78.510666 371.086914 81.881500 364.143158 81.881500 c +356.824036 81.881500 353.261932 79.106155 353.261932 73.753975 c +353.261932 69.592773 358.888428 65.824951 370.336243 62.851120 c +379.156677 60.866158 386.096832 57.895927 391.163910 54.128113 c +391.163910 54.124504 l +h +468.104645 77.323303 m +446.898071 77.323303 l +446.898071 36.086601 l +446.898071 31.127808 448.778351 28.157578 452.340454 26.768105 c +454.967834 25.775620 460.222534 25.577126 468.104645 25.974121 c +468.104645 2.778915 l +451.781036 0.793961 439.957916 2.381927 433.014160 7.737717 c +426.074005 12.891403 422.692352 22.404793 422.692352 36.086601 c +422.692352 77.323303 l +406.368744 77.323303 l +406.368744 101.907967 l +422.692352 101.907967 l +422.692352 121.930832 l +446.898071 129.661346 l +446.898071 101.907967 l +468.104645 101.907967 l +468.104645 77.323303 l +468.104645 77.323303 l +h +545.229431 32.715775 m +550.108826 37.873070 552.548523 44.416229 552.548523 52.345253 c +552.548523 60.274277 550.108826 66.817436 545.229431 71.971123 c +540.353638 77.124809 534.348267 79.701645 527.029175 79.701645 c +519.710022 79.701645 513.708252 77.124809 508.828857 71.971123 c +504.137115 66.618942 501.697418 60.075783 501.697418 52.345253 c +501.697418 44.611115 504.137115 38.067955 508.828857 32.715775 c +513.708252 27.562088 519.710022 24.985237 527.029175 24.985237 c +534.348267 24.985237 540.353638 27.562088 545.229431 32.715775 c +h +491.750977 15.071259 m +482.183441 25.180138 477.491699 37.472473 477.491699 52.345253 c +477.491699 67.015930 482.183441 79.304657 491.750977 89.417137 c +501.322113 99.526016 513.141602 104.683304 527.029175 104.683304 c +540.916687 104.683304 552.736267 99.526016 562.307373 89.417137 c +571.878479 79.304657 576.754272 66.817436 576.754272 52.345253 c +576.754272 37.670967 571.878479 25.180138 562.307373 15.071259 c +552.736267 4.958771 541.104309 0.003571 527.029175 0.003571 c +512.953979 0.003571 501.322113 4.958771 491.750977 15.071259 c +h +657.636108 32.120285 m +662.515503 37.472473 664.951538 44.210510 664.951538 52.341637 c +664.951538 60.472771 662.515503 67.210823 657.636108 72.368118 c +652.756714 77.720299 646.563599 80.297134 639.056824 80.297134 c +631.550049 80.297134 625.360596 77.720299 620.293518 72.368118 c +615.417786 67.210823 612.974426 60.472771 612.974426 52.341637 c +612.974426 44.214119 615.417786 37.472473 620.293518 32.120285 c +625.360596 26.966606 631.741333 24.389748 639.056824 24.389748 c +646.563599 24.389748 652.756714 26.966606 657.636108 32.120285 c +657.636108 32.120285 l +h +664.951538 141.560303 m +689.160950 141.560303 l +689.160950 2.778915 l +664.951538 2.778915 l +664.951538 14.472160 l +657.823730 4.756668 647.689636 -0.000031 634.556396 -0.000031 c +621.982544 -0.000031 611.101379 4.955154 602.093262 15.067642 c +593.272827 25.180122 588.768738 37.670959 588.768738 52.338036 c +588.768738 66.810219 593.272827 79.301048 602.093262 89.413528 c +611.101379 99.526016 621.982544 104.679695 634.556396 104.679695 c +647.689636 104.679695 657.823730 99.919395 664.951538 90.207512 c +664.951538 141.560303 l +664.951538 141.560303 l +h +774.167847 32.715775 m +779.047241 37.873070 781.486938 44.416229 781.486938 52.345253 c +781.486938 60.274277 779.047241 66.817436 774.167847 71.971123 c +769.288452 77.124809 763.286621 79.701645 755.967529 79.701645 c +748.648438 79.701645 742.643005 77.124809 737.763611 71.971123 c +733.071899 66.618942 730.632202 60.075783 730.632202 52.345253 c +730.632202 44.611115 733.071899 38.067955 737.763611 32.715775 c +742.643005 27.562088 748.648438 24.985237 755.967529 24.985237 c +763.286621 24.985237 769.288452 27.562088 774.167847 32.715775 c +774.167847 32.715775 l +h +720.689331 15.071259 m +711.118164 25.180138 706.430054 37.472473 706.430054 52.345253 c +706.430054 67.015930 711.118164 79.304657 720.689331 89.417137 c +730.260437 99.526016 742.080017 104.683304 755.967529 104.683304 c +769.851440 104.683304 781.674561 99.526016 791.245728 89.417137 c +800.816833 79.304657 805.692627 66.817436 805.692627 52.345253 c +805.692627 37.670967 800.816833 25.180138 791.245728 15.071259 c +781.674561 4.958771 770.039124 0.003571 755.967529 0.003571 c +741.892334 0.003571 730.260437 4.958771 720.689331 15.071259 c +720.689331 15.071259 l +h +910.404846 63.645103 m +910.404846 2.778915 l +886.195496 2.778915 l +886.195496 60.472771 l +886.195496 67.015930 884.510071 71.971123 881.132019 75.738945 c +877.941650 79.106163 873.437622 80.892624 867.619873 80.892624 c +853.920044 80.892624 846.979858 72.765106 846.979858 56.307961 c +846.979858 2.778915 l +822.774109 2.778915 l +822.774109 101.907967 l +846.979858 101.907967 l +846.979858 90.806610 l +852.797546 100.121506 861.989807 104.683304 874.938965 104.683304 c +885.260742 104.683304 893.702271 101.113983 900.270691 93.776840 c +907.026794 86.443298 910.404846 76.529312 910.404846 63.641495 c +910.404846 63.645103 l +h +f +n +Q + +endstream +endobj + +2 0 obj + 9343 +endobj + +3 0 obj + << /BBox [ 0.000000 0.000000 960.000000 238.195496 ] + /Resources << >> + /Subtype /Form + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Type /XObject + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 238.195496 m +960.000000 238.195496 l +960.000000 0.000000 l +0.000000 0.000000 l +0.000000 238.195496 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 237 +endobj + +5 0 obj + << /XObject << /X1 1 0 R >> + /ExtGState << /E1 << /SMask << /Type /Mask + /G 3 0 R + /S /Alpha + >> + /Type /ExtGState + >> >> + >> +endobj + +6 0 obj + << /Length 7 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +/X1 Do +Q + +endstream +endobj + +7 0 obj + 46 +endobj + +8 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 960.000000 238.195496 ] + /Resources 5 0 R + /Contents 6 0 R + /Parent 9 0 R + >> +endobj + +9 0 obj + << /Kids [ 8 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +10 0 obj + << /Type /Catalog + /Pages 9 0 R + >> +endobj + +xref +0 11 +0000000000 65535 f +0000000010 00000 n +0000009603 00000 n +0000009626 00000 n +0000010113 00000 n +0000010135 00000 n +0000010433 00000 n +0000010535 00000 n +0000010556 00000 n +0000010731 00000 n +0000010805 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 10 0 R + /Size 11 +>> +startxref +10865 +%%EOF \ No newline at end of file diff --git a/Mastodon/Resources/Assets.xcassets/Welcome/welcome.logo.imageset/Logotype (Full) 1.pdf b/Mastodon/Resources/Assets.xcassets/Welcome/welcome.logo.imageset/Logotype (Full) 1.pdf deleted file mode 100644 index 8aa4523a4..000000000 --- a/Mastodon/Resources/Assets.xcassets/Welcome/welcome.logo.imageset/Logotype (Full) 1.pdf +++ /dev/null @@ -1,335 +0,0 @@ -%PDF-1.7 - -1 0 obj - << /BBox [ 0.000000 0.000000 265.139893 65.366211 ] - /Resources << >> - /Subtype /Form - /Length 2 0 R - /Group << /Type /Group - /S /Transparency - >> - /Type /XObject - >> -stream -/DeviceRGB CS -/DeviceRGB cs -q -1.000000 0.000000 -0.000000 1.000000 0.000000 -0.161102 cm -0.188235 0.533333 0.831373 scn -59.674690 26.340824 m -58.778019 21.729694 51.647373 16.683701 43.456982 15.705723 c -39.185936 15.195595 34.981327 14.727741 30.497496 14.933094 c -23.164286 15.269001 17.378185 16.683697 17.378185 16.683697 c -17.378185 15.969608 17.422323 15.289906 17.510132 14.654335 c -18.463486 7.417763 24.686306 6.983833 30.580656 6.781731 c -36.529831 6.578239 41.826706 8.248928 41.826706 8.248928 c -42.071552 2.869816 l -42.071552 2.869816 37.910149 0.635567 30.497496 0.224861 c -26.409500 0.000000 21.334234 0.327538 15.422230 1.891842 c -2.601194 5.285728 0.396213 18.952328 0.058915 32.819641 c --0.044226 36.936905 0.019424 40.819546 0.019424 44.066154 c -0.019424 58.246605 9.309983 62.402893 9.309983 62.402893 c -13.994521 64.554443 22.032990 65.459015 30.389708 65.527313 c -30.595058 65.527313 l -38.951778 65.459015 46.995354 64.554443 51.679893 62.402893 c -51.679893 62.402893 60.970455 58.246605 60.970455 44.066154 c -60.970455 44.066154 61.086605 33.604343 59.674690 26.340824 c -h -f -n -Q -q -1.000000 0.000000 -0.000000 1.000000 12.881470 25.370911 cm -0.121569 0.137255 0.168627 scn -0.000000 21.971138 m -0.000000 24.035347 1.673481 25.708363 3.737223 25.708363 c -5.801430 25.708363 7.474446 24.035347 7.474446 21.971138 c -7.474446 19.907396 5.801430 18.233915 3.737223 18.233915 c -1.673481 18.233915 0.000000 19.907396 0.000000 21.971138 c -h -53.598671 17.434528 m -53.598671 0.264858 l -46.796501 0.264858 l -46.796501 16.929976 l -46.796501 20.443262 45.318153 22.225924 42.361454 22.225924 c -39.093010 22.225924 37.454372 20.110611 37.454372 15.928303 c -37.454372 6.806858 l -30.692154 6.806858 l -30.692154 15.928303 l -30.692154 20.110611 29.053518 22.225924 25.785074 22.225924 c -22.828375 22.225924 21.350027 20.443262 21.350027 16.929976 c -21.350027 0.264858 l -14.547852 0.264858 l -14.547852 17.434528 l -14.547852 20.943634 15.441275 23.732149 17.236015 25.795427 c -19.086971 27.858242 21.510775 28.915665 24.519045 28.915665 c -28.000275 28.915665 30.636404 27.578087 32.378643 24.902004 c -34.073498 22.061457 l -35.767883 24.902004 l -37.510586 27.578087 40.146252 28.915665 43.627945 28.915665 c -46.636215 28.915665 49.059559 27.858242 50.910519 25.795427 c -52.705257 23.732149 53.598671 20.943634 53.598671 17.434528 c -53.598671 17.434528 l -h -77.032234 8.899359 m -78.435783 10.382355 79.111771 12.250038 79.111771 14.502407 c -79.111771 16.754778 78.435783 18.622458 77.032234 20.050631 c -75.680717 21.534092 73.964493 22.247713 71.884956 22.247713 c -69.804955 22.247713 68.089188 21.534092 66.737679 20.050631 c -65.385696 18.622458 64.709709 16.754778 64.709709 14.502407 c -64.709709 12.250038 65.385696 10.382355 66.737679 8.899359 c -68.089188 7.471186 69.804955 6.757099 71.884956 6.757099 c -73.964493 6.757099 75.680717 7.471186 77.032234 8.899359 c -h -79.111771 28.235912 m -85.819176 28.235912 l -85.819176 0.768902 l -79.111771 0.768902 l -79.111771 4.010399 l -77.084267 1.318520 74.276245 -0.000008 70.637054 -0.000008 c -67.153496 -0.000008 64.189835 1.373341 61.694012 4.175331 c -59.250694 6.976852 58.002777 10.437643 58.002777 14.502407 c -58.002777 18.512350 59.250694 21.973600 61.694012 24.775124 c -64.189835 27.576649 67.153496 29.004822 70.637054 29.004822 c -74.276245 29.004822 77.084267 27.686295 79.111771 24.994881 c -79.111771 28.235912 l -79.111771 28.235912 l -h -108.385788 14.996740 m -110.361259 13.513744 111.349464 11.426306 111.297432 8.789715 c -111.297432 5.987724 110.309227 3.790642 108.281723 2.252361 c -106.253754 0.768898 103.810432 -0.000008 100.846764 -0.000008 c -95.491348 -0.000008 91.851685 2.197540 89.927788 6.537346 c -95.751526 9.997667 l -96.531120 7.636118 98.246872 6.427235 100.846764 6.427235 c -103.238045 6.427235 104.434395 7.196609 104.434395 8.789715 c -104.434395 9.943310 102.874275 10.986797 99.702927 11.810530 c -98.506592 12.140394 97.518852 12.469793 96.739258 12.744835 c -95.647453 13.183880 94.711761 13.678675 93.931702 14.282652 c -92.007797 15.765648 91.020058 17.743904 91.020058 20.270386 c -91.020058 22.962265 91.955765 25.104525 93.827629 26.642807 c -95.751526 28.235912 98.090775 29.004822 100.898804 29.004822 c -105.370094 29.004822 108.645966 27.082315 110.777069 23.182018 c -105.058350 19.886164 l -104.226250 21.753380 102.822701 22.687222 100.898804 22.687222 c -98.870834 22.687222 97.883102 21.918314 97.883102 20.435318 c -97.883102 19.281721 99.442749 18.238235 102.614563 17.414040 c -105.058342 16.864885 106.981773 16.040691 108.385788 14.996740 c -108.385788 14.996740 l -h -129.704178 21.423981 m -123.828873 21.423981 l -123.828873 9.997667 l -123.828873 8.624317 124.349236 7.800587 125.336967 7.416365 c -126.064987 7.141323 127.520576 7.086502 129.704178 7.196609 c -129.704178 0.768902 l -125.181328 0.219746 121.905449 0.659256 119.981552 2.142715 c -118.058113 3.570889 117.121948 6.207481 117.121948 9.997667 c -117.121948 21.423981 l -112.598618 21.423981 l -112.598618 28.235912 l -117.121948 28.235912 l -117.121948 33.784138 l -123.828873 35.926395 l -123.828873 28.235912 l -129.704178 28.235912 l -129.704178 21.423981 l -129.704178 21.423981 l -h -151.075012 9.064060 m -152.427002 10.492699 153.102524 12.305557 153.102524 14.502640 c -153.102524 16.699722 152.427002 18.512581 151.075012 19.940754 c -149.723038 21.369392 148.059311 22.083014 146.031342 22.083014 c -144.003845 22.083014 142.340118 21.369392 140.988144 19.940754 c -139.688202 18.457758 139.012207 16.644899 139.012207 14.502640 c -139.012207 12.359917 139.688202 10.547056 140.988144 9.064060 c -142.340118 7.635887 144.003845 6.921799 146.031342 6.921799 c -148.059311 6.921799 149.723038 7.635887 151.075012 9.064060 c -151.075012 9.064060 l -h -136.256683 4.175098 m -133.605225 6.976620 132.305267 10.382589 132.305267 14.502640 c -132.305267 18.567869 133.605225 21.973368 136.256683 24.774891 c -138.908142 27.576416 142.184006 29.004589 146.031342 29.004589 c -149.879135 29.004589 153.154556 27.576416 155.806488 24.774891 c -158.458405 21.973368 159.809921 18.512583 159.809921 14.502640 c -159.809921 10.437410 158.458405 6.976620 155.806488 4.175098 c -153.154556 1.373108 149.931168 0.000225 146.031342 0.000225 c -142.131973 0.000225 138.908142 1.373108 136.256683 4.175098 c -h -182.220291 8.899359 m -183.572281 10.382355 184.247803 12.250038 184.247803 14.502407 c -184.247803 16.754778 183.572281 18.622458 182.220291 20.050631 c -180.868774 21.534092 179.152557 22.247713 177.073013 22.247713 c -174.993011 22.247713 173.277252 21.534092 171.873703 20.050631 c -170.522202 18.622458 169.845734 16.754778 169.845734 14.502407 c -169.845734 12.250038 170.522202 10.382355 171.873703 8.899359 c -173.277252 7.471186 175.045044 6.757099 177.073013 6.757099 c -179.152557 6.757099 180.868774 7.471186 182.220291 8.899359 c -h -184.247803 39.222717 m -190.955170 39.222717 l -190.955170 0.768902 l -184.247803 0.768902 l -184.247803 4.010399 l -182.272339 1.318520 179.464294 -0.000008 175.825104 -0.000008 c -172.341553 -0.000008 169.326324 1.373341 166.830505 4.175331 c -164.386719 6.976852 163.138809 10.437643 163.138809 14.502407 c -163.138809 18.512350 164.386719 21.973600 166.830505 24.775124 c -169.326324 27.576649 172.341553 29.004822 175.825104 29.004822 c -179.464294 29.004822 182.272339 27.686295 184.247803 24.994881 c -184.247803 39.222717 l -184.247803 39.222717 l -h -214.509811 9.064060 m -215.861328 10.492699 216.537323 12.305557 216.537323 14.502640 c -216.537323 16.699722 215.861328 18.512581 214.509811 19.940754 c -213.157837 21.369392 211.494110 22.083014 209.466141 22.083014 c -207.438644 22.083014 205.774460 21.369392 204.422943 19.940754 c -203.122543 18.457758 202.447006 16.644899 202.447006 14.502640 c -202.447006 12.359917 203.122543 10.547056 204.422943 9.064060 c -205.774460 7.635887 207.438644 6.921799 209.466141 6.921799 c -211.494110 6.921799 213.157837 7.635887 214.509811 9.064060 c -214.509811 9.064060 l -h -199.691467 4.175098 m -197.039551 6.976620 195.740082 10.382589 195.740082 14.502640 c -195.740082 18.567869 197.039551 21.973368 199.691467 24.774891 c -202.343399 27.576416 205.618805 29.004589 209.466141 29.004589 c -213.313934 29.004589 216.589355 27.576416 219.241272 24.774891 c -221.893204 21.973368 223.244705 18.512583 223.244705 14.502640 c -223.244705 10.437410 221.893204 6.976620 219.241272 4.175098 c -216.589355 1.373108 213.365982 0.000225 209.466141 0.000225 c -205.566772 0.000225 202.343399 1.373108 199.691467 4.175098 c -h -252.258377 17.633701 m -252.258377 0.769272 l -245.550980 0.769272 l -245.550980 16.754683 l -245.550980 18.567543 245.083130 19.940893 244.147430 20.984379 c -243.263290 21.918221 242.015381 22.413017 240.403702 22.413017 c -236.607925 22.413017 234.684494 20.160648 234.684494 15.601088 c -234.684494 0.769272 l -227.977112 0.769272 l -227.977112 28.235821 l -234.684494 28.235821 l -234.684494 25.159718 l -236.296188 27.741486 238.843567 29.004728 242.431656 29.004728 c -245.291260 29.004728 247.630966 28.016064 249.450806 25.983450 c -251.322205 23.950836 252.258377 21.204134 252.258377 17.633701 c -f -n -Q - -endstream -endobj - -2 0 obj - 8987 -endobj - -3 0 obj - << /BBox [ 0.000000 0.000000 265.139893 65.366211 ] - /Resources << >> - /Subtype /Form - /Length 4 0 R - /Group << /Type /Group - /S /Transparency - >> - /Type /XObject - >> -stream -/DeviceRGB CS -/DeviceRGB cs -q -1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm -0.000000 0.000000 0.000000 scn -0.000000 65.366211 m -265.139862 65.366211 l -265.139862 0.000000 l -0.000000 0.000000 l -0.000000 65.366211 l -h -f -n -Q - -endstream -endobj - -4 0 obj - 234 -endobj - -5 0 obj - << /XObject << /X1 1 0 R >> - /ExtGState << /E1 << /SMask << /Type /Mask - /G 3 0 R - /S /Alpha - >> - /Type /ExtGState - >> >> - >> -endobj - -6 0 obj - << /Length 7 0 R >> -stream -/DeviceRGB CS -/DeviceRGB cs -q -/E1 gs -/X1 Do -Q - -endstream -endobj - -7 0 obj - 46 -endobj - -8 0 obj - << /Annots [] - /Type /Page - /MediaBox [ 0.000000 0.000000 265.139893 65.366211 ] - /Resources 5 0 R - /Contents 6 0 R - /Parent 9 0 R - >> -endobj - -9 0 obj - << /Kids [ 8 0 R ] - /Count 1 - /Type /Pages - >> -endobj - -10 0 obj - << /Type /Catalog - /Pages 9 0 R - >> -endobj - -xref -0 11 -0000000000 65535 f -0000000010 00000 n -0000009246 00000 n -0000009269 00000 n -0000009752 00000 n -0000009774 00000 n -0000010072 00000 n -0000010174 00000 n -0000010195 00000 n -0000010369 00000 n -0000010443 00000 n -trailer -<< /ID [ (some) (id) ] - /Root 10 0 R - /Size 11 ->> -startxref -10503 -%%EOF \ No newline at end of file diff --git a/Mastodon/Resources/en.lproj/Localizable.strings b/Mastodon/Resources/en.lproj/Localizable.strings index 3902036cd..b3df9a77f 100644 --- a/Mastodon/Resources/en.lproj/Localizable.strings +++ b/Mastodon/Resources/en.lproj/Localizable.strings @@ -13,8 +13,8 @@ "Common.Controls.Actions.Save" = "Save"; "Common.Controls.Actions.SavePhoto" = "Save photo"; "Common.Controls.Actions.SeeMore" = "See More"; -"Common.Controls.Actions.SignIn" = "Sign in"; -"Common.Controls.Actions.SignUp" = "Sign up"; +"Common.Controls.Actions.SignIn" = "Sign In"; +"Common.Controls.Actions.SignUp" = "Sign Up"; "Common.Controls.Actions.TakePhoto" = "Take photo"; "Common.Controls.Status.MediaContentWarning" = "Tap to reveal that may be sensitive"; "Common.Controls.Status.ShowPost" = "Show Post"; @@ -26,7 +26,7 @@ "Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email"; "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.ResendEmail" = "Resend email"; +"Scene.ConfirmEmail.DontReceiveEmail.ResendEmail" = "Resend 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.Mail" = "Mail"; @@ -62,4 +62,4 @@ any server."; "Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@."; "Scene.ServerRules.Title" = "Some ground rules."; "Scene.Welcome.Slogan" = "Social networking -back in your hands."; \ No newline at end of file +back in your hands."; diff --git a/Mastodon/Scene/Authentication/AuthenticationViewController.swift b/Mastodon/Scene/Authentication/AuthenticationViewController.swift deleted file mode 100644 index 090d84234..000000000 --- a/Mastodon/Scene/Authentication/AuthenticationViewController.swift +++ /dev/null @@ -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() - - 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 - let application: Mastodon.Response.Content - } - - private struct SignUpResponseSecond { - let instance: Mastodon.Response.Content - let authenticateInfo: AuthenticationViewModel.AuthenticateInfo - } - - private struct SignUpResponseThird { - let instance: Mastodon.Response.Content - let authenticateInfo: AuthenticationViewModel.AuthenticateInfo - let applicationToken: Mastodon.Response.Content - } - - @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? 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? 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 - } -} diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift index db6ddfa67..69f0347e0 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift @@ -37,35 +37,5 @@ extension HomeTimelineViewController { 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 diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift index 125ce8545..d3906fd90 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift @@ -70,8 +70,20 @@ extension HomeTimelineViewController { return imageView }() navigationItem.leftBarButtonItem = settingBarButtonItem - settingBarButtonItem.target = self - settingBarButtonItem.action = #selector(HomeTimelineViewController.settingBarButtonItemPressed(_:)) + #if DEBUG + // 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 composeBarButtonItem.target = self composeBarButtonItem.action = #selector(HomeTimelineViewController.composeBarButtonItemPressed(_:)) @@ -111,11 +123,6 @@ extension HomeTimelineViewController { } } .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) + } } diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift index beb95d2ac..c085471c6 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift @@ -43,7 +43,7 @@ extension HomeTimelineViewModel.LoadLatestState { super.didEnter(from: previousState) guard let viewModel = viewModel, let stateMachine = stateMachine else { return } guard let activeMastodonAuthenticationBox = viewModel.context.authenticationService.activeMastodonAuthenticationBox.value else { - assertionFailure() + // sign out when loading will enter here stateMachine.enter(Fail.self) return } diff --git a/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewController.swift b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift similarity index 92% rename from Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewController.swift rename to Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift index 3bf7f78c4..2d69f0dd3 100644 --- a/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewController.swift +++ b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift @@ -11,7 +11,8 @@ import os.log import ThirdPartyMailer import UIKit -final class MastodonConfirmEmailViewController: UIViewController, NeedsDependency, OnboardingViewControllerAppearance { +final class MastodonConfirmEmailViewController: UIViewController, NeedsDependency { + var disposeBag = Set() weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } @@ -57,17 +58,18 @@ final class MastodonConfirmEmailViewController: UIViewController, NeedsDependenc button.addTarget(self, action: #selector(dontReceiveButtonPressed(_:)), for: UIControl.Event.touchUpInside) return button }() + + deinit { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) + } + } extension MastodonConfirmEmailViewController { - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - navigationController?.setNavigationBarHidden(false, animated: false) - } override func viewDidLoad() { - self.setupOnboardingAppearance() + setupOnboardingAppearance() // resizedView let resizedView = UIView() @@ -111,13 +113,15 @@ extension MastodonConfirmEmailViewController { case .finished: break } - } receiveValue: { _ in - self.coordinator.setup() + } receiveValue: { response in + 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) } + } extension MastodonConfirmEmailViewController { @@ -172,3 +176,6 @@ extension MastodonConfirmEmailViewController { self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil)) } } + +// MARK: - OnboardingViewControllerAppearance +extension MastodonConfirmEmailViewController: OnboardingViewControllerAppearance { } diff --git a/Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewModel.swift b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewModel.swift similarity index 100% rename from Mastodon/Scene/Authentication/ConfirmEmail/MastodonConfirmEmailViewModel.swift rename to Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewModel.swift diff --git a/Mastodon/Scene/PickServer/CollectionViewCell/PickServerCategoryCollectionViewCell.swift b/Mastodon/Scene/Onboarding/PickServer/CollectionViewCell/PickServerCategoryCollectionViewCell.swift similarity index 95% rename from Mastodon/Scene/PickServer/CollectionViewCell/PickServerCategoryCollectionViewCell.swift rename to Mastodon/Scene/Onboarding/PickServer/CollectionViewCell/PickServerCategoryCollectionViewCell.swift index 587ffbae4..1f02baad6 100644 --- a/Mastodon/Scene/PickServer/CollectionViewCell/PickServerCategoryCollectionViewCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/CollectionViewCell/PickServerCategoryCollectionViewCell.swift @@ -9,7 +9,7 @@ import UIKit class PickServerCategoryCollectionViewCell: UICollectionViewCell { - var category: PickServerViewModel.Category? { + var category: MastodonPickServerViewModel.Category? { didSet { categoryView.category = category } diff --git a/Mastodon/Scene/PickServer/PickServerViewController.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift similarity index 84% rename from Mastodon/Scene/PickServer/PickServerViewController.swift rename to Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift index ef87e8145..9e10cd329 100644 --- a/Mastodon/Scene/PickServer/PickServerViewController.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift @@ -1,5 +1,5 @@ // -// PickServerViewController.swift +// MastodonPickServerViewController.swift // Mastodon // // Created by BradGao on 2021/2/20. @@ -10,14 +10,14 @@ import Combine import OSLog import MastodonSDK -final class PickServerViewController: UIViewController, NeedsDependency { +final class MastodonPickServerViewController: UIViewController, NeedsDependency { private var disposeBag = Set() weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } - var viewModel: PickServerViewModel! + var viewModel: MastodonPickServerViewModel! private var isAuthenticating = CurrentValueSubject(false) @@ -29,7 +29,7 @@ final class PickServerViewController: UIViewController, NeedsDependency { case search case serverList } - + let tableView: UITableView = { let tableView = ControlContainableTableView() tableView.register(PickServerTitleCell.self, forCellReuseIdentifier: String(describing: PickServerTitleCell.self)) @@ -46,14 +46,19 @@ final class PickServerViewController: UIViewController, NeedsDependency { }() let nextStepButton: PrimaryActionButton = { - let button = PrimaryActionButton(type: .system) + let button = PrimaryActionButton() button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal) button.translatesAutoresizingMaskIntoConstraints = false return button }() + + deinit { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) + } + } -extension PickServerViewController { +extension MastodonPickServerViewController { override var preferredStatusBarStyle: UIStatusBarStyle { return .darkContent @@ -62,13 +67,15 @@ extension PickServerViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = Asset.Colors.Background.onboardingBackground.color + setupOnboardingAppearance() + defer { setupNavigationBarBackgroundView() } view.addSubview(nextStepButton) NSLayoutConstraint.activate([ - nextStepButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 12), - view.readableContentGuide.trailingAnchor.constraint(equalTo: nextStepButton.trailingAnchor, constant: 12), - view.bottomAnchor.constraint(equalTo: nextStepButton.bottomAnchor, constant: 34), + nextStepButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: MastodonPickServerViewController.actionButtonMargin), + view.readableContentGuide.trailingAnchor.constraint(equalTo: nextStepButton.trailingAnchor, constant: MastodonPickServerViewController.actionButtonMargin), + nextStepButton.heightAnchor.constraint(equalToConstant: MastodonPickServerViewController.actionButtonHeight).priority(.defaultHigh), + view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: nextStepButton.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight), ]) view.addSubview(tableView) @@ -87,7 +94,6 @@ extension PickServerViewController { } nextStepButton.addTarget(self, action: #selector(nextStepButtonDidClicked(_:)), for: .touchUpInside) -// viewModel.tableView = tableView tableView.delegate = self tableView.dataSource = self @@ -133,11 +139,11 @@ extension PickServerViewController { viewModel .authenticated - .receive(on: DispatchQueue.main) .flatMap { [weak self] (domain, user) -> AnyPublisher, Never> in guard let self = self else { return Just(.success(false)).eraseToAnyPublisher() } return 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 { @@ -145,30 +151,22 @@ extension PickServerViewController { assertionFailure(error.localizedDescription) case .success(let isActived): assert(isActived) - self.coordinator.setup() + self.dismiss(animated: true, completion: nil) } } .store(in: &disposeBag) isAuthenticating .receive(on: DispatchQueue.main) - .sink { [weak self] loading in - if loading { - self?.nextStepButton.showLoading() - } else { - self?.nextStepButton.stopLoading() - } + .sink { [weak self] isAuthenticating in + guard let self = self else { return } + isAuthenticating ? self.nextStepButton.showLoading() : self.nextStepButton.stopLoading() } .store(in: &disposeBag) viewModel.fetchAllServers() } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - navigationController?.setNavigationBarHidden(false, animated: animated) - } - @objc private func nextStepButtonDidClicked(_ sender: UIButton) { switch viewModel.mode { @@ -183,9 +181,9 @@ extension PickServerViewController { guard let server = viewModel.selectedServer.value else { return } isAuthenticating.send(true) context.apiService.createApplication(domain: server.domain) - .tryMap { response -> PickServerViewModel.AuthenticateInfo in + .tryMap { response -> MastodonPickServerViewModel.AuthenticateInfo in 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) } return info @@ -224,24 +222,24 @@ extension PickServerViewController { isAuthenticating.send(true) context.apiService.instance(domain: server.domain) - .compactMap { [weak self] response -> AnyPublisher? in + .compactMap { [weak self] response -> AnyPublisher? 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: server.domain) - .map { PickServerViewModel.SignUpResponseFirst(instance: response, application: $0) } + .map { MastodonPickServerViewModel.SignUpResponseFirst(instance: response, application: $0) } .eraseToAnyPublisher() } .switchToLatest() - .tryMap { response -> PickServerViewModel.SignUpResponseSecond in + .tryMap { response -> MastodonPickServerViewModel.SignUpResponseSecond in let application = response.application.value guard let authenticateInfo = AuthenticationViewModel.AuthenticateInfo(domain: server.domain, application: application) else { 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? in + .compactMap { [weak self] response -> AnyPublisher? in guard let self = self else { return nil } let instance = response.instance let authenticateInfo = response.authenticateInfo @@ -250,7 +248,7 @@ extension PickServerViewController { clientID: authenticateInfo.clientID, clientSecret: authenticateInfo.clientSecret ) - .map { PickServerViewModel.SignUpResponseThird(instance: instance, authenticateInfo: authenticateInfo, applicationToken: $0) } + .map { MastodonPickServerViewModel.SignUpResponseThird(instance: instance, authenticateInfo: authenticateInfo, applicationToken: $0) } .eraseToAnyPublisher() } .switchToLatest() @@ -273,13 +271,13 @@ extension PickServerViewController { instance: response.instance.value, applicationToken: response.applicationToken.value ) - self.coordinator.present(scene: .mastodonRegister(viewModel: mastodonRegisterViewModel), from: self, transition: .show) + self.coordinator.present(scene: .mastodonRegister(viewModel: mastodonRegisterViewModel), from: nil, transition: .show) } .store(in: &disposeBag) } } -extension PickServerViewController: UITableViewDelegate { +extension MastodonPickServerViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { let category = Section.allCases[section] switch category { @@ -318,7 +316,7 @@ extension PickServerViewController: UITableViewDelegate { } } -extension PickServerViewController: UITableViewDataSource { +extension MastodonPickServerViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { return UIView() } @@ -376,7 +374,7 @@ extension PickServerViewController: UITableViewDataSource { } } -extension PickServerViewController: PickServerCellDelegate { +extension MastodonPickServerViewController: PickServerCellDelegate { func pickServerCell(modeChange server: Mastodon.Entity.Server, newMode: PickServerCell.Mode, updates: (() -> Void)) { if newMode == .collapse { expandServerDomainSet.remove(server.domain) @@ -394,18 +392,18 @@ extension PickServerViewController: PickServerCellDelegate { } } -extension PickServerViewController: PickServerSearchCellDelegate { +extension MastodonPickServerViewController: PickServerSearchCellDelegate { func pickServerSearchCell(didChange searchText: String?) { viewModel.searchText.send(searchText) } } -extension PickServerViewController: PickServerCategoriesDataSource, PickServerCategoriesDelegate { +extension MastodonPickServerViewController: PickServerCategoriesDataSource, PickServerCategoriesDelegate { func numberOfCategories() -> Int { return viewModel.categories.count } - func category(at index: Int) -> PickServerViewModel.Category { + func category(at index: Int) -> MastodonPickServerViewModel.Category { return viewModel.categories[index] } @@ -417,3 +415,6 @@ extension PickServerViewController: PickServerCategoriesDataSource, PickServerCa return viewModel.selectCategoryIndex.send(index) } } + +// MARK: - OnboardingViewControllerAppearance +extension MastodonPickServerViewController: OnboardingViewControllerAppearance { } diff --git a/Mastodon/Scene/PickServer/PickServerViewModel.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift similarity index 98% rename from Mastodon/Scene/PickServer/PickServerViewModel.swift rename to Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift index b6bc07984..3a701f09f 100644 --- a/Mastodon/Scene/PickServer/PickServerViewModel.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift @@ -1,5 +1,5 @@ // -// PickServerViewModel.swift +// MastodonPickServerViewModel.swift // Mastodon // // Created by BradGao on 2021/2/23. @@ -11,7 +11,7 @@ import Combine import MastodonSDK import CoreDataStack -class PickServerViewModel: NSObject { +class MastodonPickServerViewModel: NSObject { enum PickServerMode { case signUp case signIn @@ -173,7 +173,7 @@ class PickServerViewModel: NSObject { } // MARK: - SignIn methods & structs -extension PickServerViewModel { +extension MastodonPickServerViewModel { enum AuthenticationError: Error, LocalizedError { case badCredentials case registrationClosed @@ -322,7 +322,7 @@ extension PickServerViewModel { } // MARK: - SignUp methods & structs -extension PickServerViewModel { +extension MastodonPickServerViewModel { struct SignUpResponseFirst { let instance: Mastodon.Response.Content let application: Mastodon.Response.Content diff --git a/Mastodon/Scene/PickServer/TableViewCell/PickServerCategoriesCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCategoriesCell.swift similarity index 78% rename from Mastodon/Scene/PickServer/TableViewCell/PickServerCategoriesCell.swift rename to Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCategoriesCell.swift index b324fe831..8f66e9847 100644 --- a/Mastodon/Scene/PickServer/TableViewCell/PickServerCategoriesCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCategoriesCell.swift @@ -10,7 +10,7 @@ import MastodonSDK protocol PickServerCategoriesDataSource: class { func numberOfCategories() -> Int - func category(at index: Int) -> PickServerViewModel.Category + func category(at index: Int) -> MastodonPickServerViewModel.Category func selectedIndex() -> Int } @@ -23,14 +23,17 @@ final class PickServerCategoriesCell: UITableViewCell { weak var dataSource: PickServerCategoriesDataSource! weak var delegate: PickServerCategoriesDelegate! + let metricView = UIView() + let collectionView: UICollectionView = { let flowLayout = UICollectionViewFlowLayout() flowLayout.scrollDirection = .horizontal let view = ControlContainableCollectionView(frame: .zero, collectionViewLayout: flowLayout) + view.register(PickServerCategoryCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: PickServerCategoryCollectionViewCell.self)) view.backgroundColor = .clear view.showsHorizontalScrollIndicator = false - view.register(PickServerCategoryCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: PickServerCategoryCollectionViewCell.self)) view.showsVerticalScrollIndicator = false + view.layer.masksToBounds = false view.translatesAutoresizingMaskIntoConstraints = false return view }() @@ -51,20 +54,36 @@ extension PickServerCategoriesCell { private func _init() { self.selectionStyle = .none backgroundColor = .clear - + + metricView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(metricView) + NSLayoutConstraint.activate([ + metricView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + metricView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + metricView.topAnchor.constraint(equalTo: contentView.topAnchor), + metricView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + metricView.heightAnchor.constraint(equalToConstant: 80).priority(.defaultHigh), + ]) + contentView.addSubview(collectionView) NSLayoutConstraint.activate([ collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), collectionView.topAnchor.constraint(equalTo: contentView.topAnchor), collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), - - collectionView.heightAnchor.constraint(equalToConstant: 80), + collectionView.heightAnchor.constraint(equalToConstant: 80).priority(.defaultHigh), ]) collectionView.delegate = self collectionView.dataSource = self } + + override func layoutSubviews() { + super.layoutSubviews() + + collectionView.collectionViewLayout.invalidateLayout() + } + } extension PickServerCategoriesCell: UICollectionViewDelegateFlowLayout { @@ -74,7 +93,8 @@ extension PickServerCategoriesCell: UICollectionViewDelegateFlowLayout { } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { - return UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) + layoutIfNeeded() + return UIEdgeInsets(top: 0, left: metricView.frame.minX - collectionView.frame.minX, bottom: 0, right: collectionView.frame.maxX - metricView.frame.maxX) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { diff --git a/Mastodon/Scene/PickServer/TableViewCell/PickServerCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift similarity index 83% rename from Mastodon/Scene/PickServer/TableViewCell/PickServerCell.swift rename to Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift index d7deba29c..711822186 100644 --- a/Mastodon/Scene/PickServer/TableViewCell/PickServerCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift @@ -22,8 +22,9 @@ class PickServerCell: UITableViewCell { case expand } - private var bgView: UIView = { + private var containerView: UIView = { let view = UIView() + view.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 10, right: 16) view.backgroundColor = Asset.Colors.lightWhite.color view.translatesAutoresizingMaskIntoConstraints = false return view @@ -193,16 +194,16 @@ extension PickServerCell { selectionStyle = .none backgroundColor = .clear - contentView.addSubview(bgView) - contentView.addSubview(domainLabel) - contentView.addSubview(checkbox) - contentView.addSubview(descriptionLabel) - contentView.addSubview(seperator) + contentView.addSubview(containerView) + containerView.addSubview(domainLabel) + containerView.addSubview(checkbox) + containerView.addSubview(descriptionLabel) + containerView.addSubview(seperator) - contentView.addSubview(expandButton) + containerView.addSubview(expandButton) // Always add the expandbox which contains elements only visible in expand mode - contentView.addSubview(expandBox) + containerView.addSubview(expandBox) expandBox.addSubview(thumbImageView) expandBox.addSubview(infoStackView) expandBox.isHidden = true @@ -217,68 +218,63 @@ extension PickServerCell { let expandButtonTopConstraintInCollapse = expandButton.topAnchor.constraint(equalTo: descriptionLabel.lastBaselineAnchor, constant: 12).priority(.required) collapseConstraints.append(expandButtonTopConstraintInCollapse) - let expandButtonTopConstraintInExpand = expandButton.topAnchor.constraint(equalTo: expandBox.bottomAnchor, constant: 8).priority(.required) + let expandButtonTopConstraintInExpand = expandButton.topAnchor.constraint(equalTo: expandBox.bottomAnchor, constant: 8).priority(.defaultHigh) expandConstraints.append(expandButtonTopConstraintInExpand) -// domainLabel.setContentHuggingPriority(.required - 1, for: .vertical) -// domainLabel.setContentCompressionResistancePriority(.required - 1, for: .vertical) -// descriptionLabel.setContentHuggingPriority(.required - 2, for: .vertical) -// descriptionLabel.setContentCompressionResistancePriority(.required - 2, for: .vertical) - domainLabel.setContentHuggingPriority(.required, for: .vertical) - domainLabel.setContentCompressionResistancePriority(.required, for: .vertical) - descriptionLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) - descriptionLabel.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) - NSLayoutConstraint.activate([ // Set background view - bgView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), - bgView.topAnchor.constraint(equalTo: contentView.topAnchor), - contentView.readableContentGuide.trailingAnchor.constraint(equalTo: bgView.trailingAnchor), - contentView.bottomAnchor.constraint(equalTo: bgView.bottomAnchor, constant: 1), + containerView.topAnchor.constraint(equalTo: contentView.topAnchor), + containerView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), + contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 1), // Set bottom separator - seperator.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), - contentView.readableContentGuide.trailingAnchor.constraint(equalTo: seperator.trailingAnchor), - contentView.bottomAnchor.constraint(equalTo: seperator.bottomAnchor), - seperator.heightAnchor.constraint(equalToConstant: 1), + seperator.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), + containerView.trailingAnchor.constraint(equalTo: seperator.trailingAnchor), + containerView.bottomAnchor.constraint(equalTo: seperator.bottomAnchor), + seperator.heightAnchor.constraint(equalToConstant: 1).priority(.defaultHigh), - domainLabel.leadingAnchor.constraint(equalTo: bgView.leadingAnchor, constant: 16), - domainLabel.topAnchor.constraint(equalTo: bgView.topAnchor, constant: 16), + domainLabel.topAnchor.constraint(equalTo: containerView.layoutMarginsGuide.topAnchor), + domainLabel.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor), checkbox.widthAnchor.constraint(equalToConstant: 23), checkbox.heightAnchor.constraint(equalToConstant: 22), - bgView.trailingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: 16), + containerView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: checkbox.trailingAnchor), checkbox.leadingAnchor.constraint(equalTo: domainLabel.trailingAnchor, constant: 16), checkbox.centerYAnchor.constraint(equalTo: domainLabel.centerYAnchor), - descriptionLabel.leadingAnchor.constraint(equalTo: bgView.leadingAnchor, constant: 16), - descriptionLabel.topAnchor.constraint(equalTo: domainLabel.firstBaselineAnchor, constant: 8).priority(.required), - bgView.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor, constant: 16), + descriptionLabel.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor), + descriptionLabel.topAnchor.constraint(equalTo: domainLabel.bottomAnchor, constant: 8), + containerView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor), // Set expandBox constraints - expandBox.leadingAnchor.constraint(equalTo: bgView.leadingAnchor, constant: 16), - bgView.trailingAnchor.constraint(equalTo: expandBox.trailingAnchor, constant: 16), + expandBox.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor), + containerView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: expandBox.trailingAnchor), expandBox.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 8), expandBox.bottomAnchor.constraint(equalTo: infoStackView.bottomAnchor).priority(.defaultHigh), + thumbImageView.topAnchor.constraint(equalTo: expandBox.topAnchor), thumbImageView.leadingAnchor.constraint(equalTo: expandBox.leadingAnchor), expandBox.trailingAnchor.constraint(equalTo: thumbImageView.trailingAnchor), - thumbImageView.topAnchor.constraint(equalTo: expandBox.topAnchor).priority(.defaultHigh), thumbImageView.heightAnchor.constraint(equalTo: thumbImageView.widthAnchor, multiplier: 151.0 / 303.0).priority(.defaultHigh), infoStackView.leadingAnchor.constraint(equalTo: expandBox.leadingAnchor), expandBox.trailingAnchor.constraint(equalTo: infoStackView.trailingAnchor), infoStackView.topAnchor.constraint(equalTo: thumbImageView.bottomAnchor, constant: 16), - expandButton.leadingAnchor.constraint(equalTo: bgView.leadingAnchor, constant: 16), - bgView.trailingAnchor.constraint(equalTo: expandButton.trailingAnchor, constant: 16), - bgView.bottomAnchor.constraint(equalTo: expandButton.bottomAnchor, constant: 8), + expandButton.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor), + containerView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: expandButton.trailingAnchor), + containerView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: expandButton.bottomAnchor), ]) NSLayoutConstraint.activate(collapseConstraints) - expandButton.addTarget(self, action: #selector(expandButtonDidClicked(_:)), for: .touchUpInside) + domainLabel.setContentHuggingPriority(.required - 1, for: .vertical) + domainLabel.setContentCompressionResistancePriority(.required - 1, for: .vertical) + descriptionLabel.setContentHuggingPriority(.required - 2, for: .vertical) + descriptionLabel.setContentCompressionResistancePriority(.required - 2, for: .vertical) + expandButton.addTarget(self, action: #selector(expandButtonDidClicked(_:)), for: .touchUpInside) } private func makeVerticalInfoStackView(arrangedView: UIView...) -> UIStackView { diff --git a/Mastodon/Scene/PickServer/TableViewCell/PickServerSearchCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift similarity index 100% rename from Mastodon/Scene/PickServer/TableViewCell/PickServerSearchCell.swift rename to Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift diff --git a/Mastodon/Scene/PickServer/TableViewCell/PickServerTitleCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift similarity index 96% rename from Mastodon/Scene/PickServer/TableViewCell/PickServerTitleCell.swift rename to Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift index 469667330..82d155535 100644 --- a/Mastodon/Scene/PickServer/TableViewCell/PickServerTitleCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift @@ -11,7 +11,7 @@ final class PickServerTitleCell: UITableViewCell { let titleLabel: UILabel = { let label = UILabel() - label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.boldSystemFont(ofSize: 34)) + label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold)) label.textColor = Asset.Colors.Label.primary.color label.text = L10n.Scene.ServerPicker.title label.adjustsFontForContentSizeCategory = true diff --git a/Mastodon/Scene/PickServer/View/PickServerCategoryView.swift b/Mastodon/Scene/Onboarding/PickServer/View/PickServerCategoryView.swift similarity index 98% rename from Mastodon/Scene/PickServer/View/PickServerCategoryView.swift rename to Mastodon/Scene/Onboarding/PickServer/View/PickServerCategoryView.swift index a4a4f0ef6..30fcbc1f9 100644 --- a/Mastodon/Scene/PickServer/View/PickServerCategoryView.swift +++ b/Mastodon/Scene/Onboarding/PickServer/View/PickServerCategoryView.swift @@ -9,7 +9,7 @@ import UIKit import MastodonSDK class PickServerCategoryView: UIView { - var category: PickServerViewModel.Category? { + var category: MastodonPickServerViewModel.Category? { didSet { updateCategory() } diff --git a/Mastodon/Scene/Authentication/PinBased/MastodonPinBasedAuthenticationViewController.swift b/Mastodon/Scene/Onboarding/PinBasedAuthentication/MastodonPinBasedAuthenticationViewController.swift similarity index 100% rename from Mastodon/Scene/Authentication/PinBased/MastodonPinBasedAuthenticationViewController.swift rename to Mastodon/Scene/Onboarding/PinBasedAuthentication/MastodonPinBasedAuthenticationViewController.swift diff --git a/Mastodon/Scene/Authentication/PinBased/MastodonPinBasedAuthenticationViewModel.swift b/Mastodon/Scene/Onboarding/PinBasedAuthentication/MastodonPinBasedAuthenticationViewModel.swift similarity index 100% rename from Mastodon/Scene/Authentication/PinBased/MastodonPinBasedAuthenticationViewModel.swift rename to Mastodon/Scene/Onboarding/PinBasedAuthentication/MastodonPinBasedAuthenticationViewModel.swift diff --git a/Mastodon/Scene/Authentication/PinBased/MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift b/Mastodon/Scene/Onboarding/PinBasedAuthentication/MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift similarity index 100% rename from Mastodon/Scene/Authentication/PinBased/MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift rename to Mastodon/Scene/Onboarding/PinBasedAuthentication/MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift diff --git a/Mastodon/Scene/Authentication/Register/MastodonRegisterViewController.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift similarity index 83% rename from Mastodon/Scene/Authentication/Register/MastodonRegisterViewController.swift rename to Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift index 58415b5fa..ff979c3dd 100644 --- a/Mastodon/Scene/Authentication/Register/MastodonRegisterViewController.swift +++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift @@ -21,18 +21,13 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O let tapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer - let statusBarBackground: UIView = { - let view = UIView() - view.backgroundColor = Asset.Colors.Background.onboardingBackground.color - return view - }() - let scrollView: UIScrollView = { let scrollview = UIScrollView() scrollview.showsVerticalScrollIndicator = false - scrollview.translatesAutoresizingMaskIntoConstraints = false scrollview.keyboardDismissMode = .interactive + scrollview.alwaysBounceVertical = true scrollview.clipsToBounds = false // make content could display over bleeding + scrollview.translatesAutoresizingMaskIntoConstraints = false return scrollview }() @@ -97,9 +92,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O textField.autocapitalizationType = .none textField.autocorrectionType = .no 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, - attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color, + attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)]) textField.borderStyle = UITextField.BorderStyle.roundedRect let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height)) @@ -118,9 +113,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O textField.autocapitalizationType = .none textField.autocorrectionType = .no 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, - attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color, + attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)]) textField.borderStyle = UITextField.BorderStyle.roundedRect let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height)) @@ -135,9 +130,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O textField.autocorrectionType = .no textField.keyboardType = .emailAddress 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, - attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color, + attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)]) textField.borderStyle = UITextField.BorderStyle.roundedRect let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height)) @@ -159,9 +154,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O textField.keyboardType = .asciiCapable textField.isSecureTextEntry = true 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, - attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color, + attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)]) textField.borderStyle = UITextField.BorderStyle.roundedRect let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height)) @@ -175,9 +170,9 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O textField.autocapitalizationType = .none textField.autocorrectionType = .no 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, - attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.lightSecondaryText.color, + attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline)]) textField.borderStyle = UITextField.BorderStyle.roundedRect let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: textField.frame.height)) @@ -186,25 +181,18 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O return textField }() - let signUpButton: UIButton = { - let button = UIButton(type: .system) - button.titleLabel?.font = .preferredFont(forTextStyle: .headline) - button.setBackgroundImage(UIImage.placeholder(color: Asset.Colors.lightBrandBlue.color), for: .normal) - button.setBackgroundImage(UIImage.placeholder(color: Asset.Colors.lightDisabled.color), for: .disabled) + let buttonContainer = UIView() + let signUpButton: PrimaryActionButton = { + let button = PrimaryActionButton() button.isEnabled = false - button.setTitleColor(.white, for: .normal) button.setTitle(L10n.Common.Controls.Actions.continue, for: .normal) - button.layer.masksToBounds = true - button.layer.cornerRadius = 8 - button.layer.cornerCurve = .continuous return button }() - let signUpActivityIndicatorView: UIActivityIndicatorView = { - let activityIndicatorView = UIActivityIndicatorView(style: .medium) - activityIndicatorView.hidesWhenStopped = true - return activityIndicatorView - }() + deinit { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) + } + } extension MastodonRegisterViewController { @@ -212,7 +200,8 @@ extension MastodonRegisterViewController { override func viewDidLoad() { super.viewDidLoad() - self.setupOnboardingAppearance() + setupOnboardingAppearance() + defer { setupNavigationBarBackgroundView() } domainLabel.text = "@" + viewModel.domain + " " domainLabel.sizeToFit() @@ -265,15 +254,6 @@ extension MastodonRegisterViewController { stackView.widthAnchor.constraint(equalTo: scrollView.contentLayoutGuide.widthAnchor), scrollView.contentLayoutGuide.bottomAnchor.constraint(equalTo: stackView.bottomAnchor), ]) - - statusBarBackground.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(statusBarBackground) - NSLayoutConstraint.activate([ - statusBarBackground.topAnchor.constraint(equalTo: view.topAnchor), - statusBarBackground.leadingAnchor.constraint(equalTo: view.leadingAnchor), - statusBarBackground.trailingAnchor.constraint(equalTo: view.trailingAnchor), - statusBarBackground.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor), - ]) // photoview photoView.translatesAutoresizingMaskIntoConstraints = false @@ -314,19 +294,17 @@ extension MastodonRegisterViewController { stackView.setCustomSpacing(32, after: passwordCheckLabel) // button + stackView.addArrangedSubview(buttonContainer) signUpButton.translatesAutoresizingMaskIntoConstraints = false - stackView.addArrangedSubview(signUpButton) + buttonContainer.addSubview(signUpButton) NSLayoutConstraint.activate([ - signUpButton.heightAnchor.constraint(equalToConstant: 46).priority(.defaultHigh), + 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), ]) - - signUpActivityIndicatorView.translatesAutoresizingMaskIntoConstraints = false - scrollView.addSubview(signUpActivityIndicatorView) - NSLayoutConstraint.activate([ - signUpActivityIndicatorView.centerXAnchor.constraint(equalTo: signUpButton.centerXAnchor), - signUpActivityIndicatorView.centerYAnchor.constraint(equalTo: signUpButton.centerYAnchor), - ]) - + Publishers.CombineLatest( KeyboardResponderService.shared.state.eraseToAnyPublisher(), KeyboardResponderService.shared.willEndFrame.eraseToAnyPublisher() @@ -352,7 +330,7 @@ extension MastodonRegisterViewController { self.scrollView.verticalScrollIndicatorInsets.bottom = padding + 16 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 contentOffsetY = self.scrollView.contentOffset.y DispatchQueue.main.async { @@ -366,9 +344,7 @@ extension MastodonRegisterViewController { .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 ? "" : L10n.Common.Controls.Actions.continue, for: .normal) - self.signUpButton.isEnabled = !isRegistering + isRegistering ? self.signUpButton.showLoading() : self.signUpButton.stopLoading() } .store(in: &disposeBag) @@ -483,7 +459,7 @@ extension MastodonRegisterViewController { .receive(on: DispatchQueue.main) .sink { [weak self] _ in 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) } @@ -491,11 +467,6 @@ extension MastodonRegisterViewController { 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 { @@ -513,7 +484,7 @@ extension MastodonRegisterViewController: UITextFieldDelegate { case passwordTextField: viewModel.password.value = text case inviteTextField: - viewModel.invite.value = text + viewModel.reason.value = text default: break } @@ -556,19 +527,8 @@ extension MastodonRegisterViewController { let username = viewModel.username.value let email = viewModel.email.value let password = viewModel.password.value - - if let rules = viewModel.instance.rules, !rules.isEmpty { - let mastodonServerRulesViewModel = MastodonServerRulesViewModel( - context: context, - domain: viewModel.domain, - rules: rules - ) - coordinator.present(scene: .mastodonServerRules(viewModel: mastodonServerRulesViewModel), from: self, transition: .show) - return - } - let query = Mastodon.API.Account.RegisterQuery( - reason: viewModel.invite.value, + reason: viewModel.reason.value, username: username, email: email, password: password, @@ -576,34 +536,45 @@ extension MastodonRegisterViewController { locale: "en" // TODO: ) - 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 + if let rules = viewModel.instance.rules, !rules.isEmpty { + // show server rules before register + let mastodonServerRulesViewModel = MastodonServerRulesViewModel( + context: context, + domain: viewModel.domain, + authenticateInfo: viewModel.authenticateInfo, + rules: rules, + registerQuery: query, + applicationAuthorization: viewModel.applicationAuthorization + ) - 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 + viewModel.isRegistering.value = false + view.endEditing(true) + coordinator.present(scene: .mastodonServerRules(viewModel: mastodonServerRulesViewModel), from: self, transition: .show) + return + } else { + // 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) } - alertController.addAction(okAction) - self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil)) + .store(in: &disposeBag) } - .store(in: &disposeBag) } } diff --git a/Mastodon/Scene/Authentication/Register/MastodonRegisterViewModel.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift similarity index 91% rename from Mastodon/Scene/Authentication/Register/MastodonRegisterViewModel.swift rename to Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift index 9afa531f8..5a9098347 100644 --- a/Mastodon/Scene/Authentication/Register/MastodonRegisterViewModel.swift +++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift @@ -23,33 +23,19 @@ final class MastodonRegisterViewModel { let displayName = CurrentValueSubject("") let email = CurrentValueSubject("") let password = CurrentValueSubject("") - let invite = CurrentValueSubject("") - - let isUsernameValidateDalay = CurrentValueSubject(true) - let isDisplayNameValidateDalay = CurrentValueSubject(true) - let isEmailValidateDalay = CurrentValueSubject(true) - let isPasswordValidateDalay = CurrentValueSubject(true) - let isInviteValidateDelay = CurrentValueSubject(true) - let isRegistering = CurrentValueSubject(false) + let reason = CurrentValueSubject("") // output - lazy var approvalRequired: Bool = { - if let approvalRequired = instance.approvalRequired { - return approvalRequired - } - return false - }() - + let approvalRequired: Bool let applicationAuthorization: Mastodon.API.OAuth.Authorization - let usernameValidateState = CurrentValueSubject(.empty) let displayNameValidateState = CurrentValueSubject(.empty) let emailValidateState = CurrentValueSubject(.empty) let passwordValidateState = CurrentValueSubject(.empty) let inviteValidateState = CurrentValueSubject(.empty) + let isRegistering = CurrentValueSubject(false) let isAllValid = CurrentValueSubject(false) - let error = CurrentValueSubject(nil) init( @@ -62,6 +48,7 @@ final class MastodonRegisterViewModel { self.authenticateInfo = authenticateInfo self.instance = instance self.applicationToken = applicationToken + self.approvalRequired = instance.approvalRequired ?? false self.applicationAuthorization = Mastodon.API.OAuth.Authorization(accessToken: applicationToken.accessToken) username @@ -107,7 +94,7 @@ final class MastodonRegisterViewModel { .assign(to: \.value, on: passwordValidateState) .store(in: &disposeBag) if approvalRequired { - invite + reason .map { invite in guard !invite.isEmpty else { return .empty } return .valid diff --git a/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewController.swift b/Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewController.swift similarity index 98% rename from Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewController.swift rename to Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewController.swift index a10755fe3..25b9ca402 100644 --- a/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewController.swift +++ b/Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewController.swift @@ -10,7 +10,8 @@ import os.log import UIKit import WebKit -final class MastodonResendEmailViewController: UIViewController, NeedsDependency, WKNavigationDelegate { +final class MastodonResendEmailViewController: UIViewController, NeedsDependency { + weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } @@ -35,6 +36,7 @@ final class MastodonResendEmailViewController: UIViewController, NeedsDependency } } } + } extension MastodonResendEmailViewController { diff --git a/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewModel.swift b/Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewModel.swift similarity index 98% rename from Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewModel.swift rename to Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewModel.swift index e8e16e6e5..dc907cc6c 100644 --- a/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewModel.swift +++ b/Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewModel.swift @@ -11,6 +11,7 @@ import os.log import WebKit final class MastodonResendEmailViewModel { + // input let resendEmailURL: URL let email: String @@ -25,6 +26,7 @@ final class MastodonResendEmailViewModel { deinit { os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", (#file as NSString).lastPathComponent, #line, #function) } + } extension MastodonResendEmailViewModel { diff --git a/Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewModelNavigationDelegateShim.swift b/Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewModelNavigationDelegateShim.swift similarity index 100% rename from Mastodon/Scene/Authentication/ResendEmail/MastodonResendEmailViewModelNavigationDelegateShim.swift rename to Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewModelNavigationDelegateShim.swift diff --git a/Mastodon/Scene/Authentication/ServerRules/MastodonServerRulesViewController.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift similarity index 73% rename from Mastodon/Scene/Authentication/ServerRules/MastodonServerRulesViewController.swift rename to Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift index 4f23a6134..6f2554ce1 100644 --- a/Mastodon/Scene/Authentication/ServerRules/MastodonServerRulesViewController.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift @@ -7,8 +7,11 @@ import os.log import UIKit +import Combine -final class MastodonServerRulesViewController: UIViewController, NeedsDependency ,OnboardingViewControllerAppearance{ +final class MastodonServerRulesViewController: UIViewController, NeedsDependency { + + var disposeBag = Set() weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } @@ -17,7 +20,7 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency let largeTitleLabel: UILabel = { let label = UILabel() - label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.boldSystemFont(ofSize: 34)) + label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold)) label.textColor = .label label.text = L10n.Scene.ServerRules.title return label @@ -56,20 +59,23 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency return label }() - let confirmButton: UIButton = { - let button = UIButton(type: .system) + let confirmButton: PrimaryActionButton = { + let button = PrimaryActionButton() button.titleLabel?.font = .preferredFont(forTextStyle: .headline) - button.setBackgroundImage(UIImage.placeholder(color: Asset.Colors.lightBrandBlue.color), for: .normal) - button.setBackgroundImage(UIImage.placeholder(color: Asset.Colors.lightDisabled.color), for: .disabled) - button.setTitleColor(Asset.Colors.Label.primary.color, for: .normal) + button.setTitleColor(.white, for: .normal) button.setTitle(L10n.Scene.ServerRules.Button.confirm, for: .normal) - button.layer.masksToBounds = true - button.layer.cornerRadius = 8 - button.layer.cornerCurve = .continuous return button }() - let scrollView = UIScrollView() + let scrollView: UIScrollView = { + let scrollView = UIScrollView() + scrollView.alwaysBounceVertical = true + return scrollView + }() + + deinit { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) + } } @@ -92,8 +98,7 @@ extension MastodonServerRulesViewController { confirmButton.translatesAutoresizingMaskIntoConstraints = false bottonContainerView.addSubview(confirmButton) NSLayoutConstraint.activate([ - bottonContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: confirmButton.bottomAnchor, constant: 16), - bottonContainerView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: confirmButton.bottomAnchor).priority(.defaultHigh), + bottonContainerView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: confirmButton.bottomAnchor, constant: MastodonServerRulesViewController.viewBottomPaddingHeight), confirmButton.leadingAnchor.constraint(equalTo: bottonContainerView.readableContentGuide.leadingAnchor), confirmButton.trailingAnchor.constraint(equalTo: bottonContainerView.readableContentGuide.trailingAnchor), confirmButton.heightAnchor.constraint(equalToConstant: 46).priority(.defaultHigh), @@ -122,7 +127,7 @@ extension MastodonServerRulesViewController { stackView.axis = .vertical stackView.distribution = .fill stackView.spacing = 10 - stackView.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0) + stackView.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0) stackView.addArrangedSubview(largeTitleLabel) stackView.addArrangedSubview(subtitleLabel) stackView.addArrangedSubview(rulesLabel) @@ -138,12 +143,14 @@ extension MastodonServerRulesViewController { rulesLabel.attributedText = viewModel.rulesAttributedString confirmButton.addTarget(self, action: #selector(MastodonServerRulesViewController.confirmButtonPressed(_:)), for: .touchUpInside) - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - navigationController?.setNavigationBarHidden(false, animated: false) + 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) } } @@ -151,10 +158,37 @@ extension MastodonServerRulesViewController { extension MastodonServerRulesViewController { @objc private func confirmButtonPressed(_ sender: UIButton) { 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) } } +// MARK: - OnboardingViewControllerAppearance +extension MastodonServerRulesViewController: OnboardingViewControllerAppearance { } + #if canImport(SwiftUI) && DEBUG import SwiftUI diff --git a/Mastodon/Scene/Authentication/ServerRules/MastodonServerRulesViewModel.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift similarity index 55% rename from Mastodon/Scene/Authentication/ServerRules/MastodonServerRulesViewModel.swift rename to Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift index 0c43392f5..9569ffe81 100644 --- a/Mastodon/Scene/Authentication/ServerRules/MastodonServerRulesViewModel.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift @@ -6,6 +6,7 @@ // import UIKit +import Combine import MastodonSDK final class MastodonServerRulesViewModel { @@ -13,12 +14,30 @@ final class MastodonServerRulesViewModel { // input let context: AppContext let domain: String + let authenticateInfo: AuthenticationViewModel.AuthenticateInfo let rules: [Mastodon.Entity.Instance.Rule] + let registerQuery: Mastodon.API.Account.RegisterQuery + let applicationAuthorization: Mastodon.API.OAuth.Authorization + + // output + let isRegistering = CurrentValueSubject(false) + let error = CurrentValueSubject(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.domain = domain + self.authenticateInfo = authenticateInfo self.rules = rules + self.registerQuery = registerQuery + self.applicationAuthorization = applicationAuthorization } var rulesAttributedString: NSAttributedString { @@ -32,9 +51,6 @@ final class MastodonServerRulesViewModel { attributedString.append(indexString) 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 } diff --git a/Mastodon/Scene/Authentication/AuthenticationViewModel.swift b/Mastodon/Scene/Onboarding/Share/AuthenticationViewModel.swift similarity index 100% rename from Mastodon/Scene/Authentication/AuthenticationViewModel.swift rename to Mastodon/Scene/Onboarding/Share/AuthenticationViewModel.swift diff --git a/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift new file mode 100644 index 000000000..c4b26321a --- /dev/null +++ b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift @@ -0,0 +1,60 @@ +// +// OnboardingViewControllerAppearance.swift +// Mastodon +// +// Created by sxiaojian on 2021/2/25. +// + +import UIKit + +protocol OnboardingViewControllerAppearance: UIViewController { + static var viewBottomPaddingHeight: CGFloat { get } + func setupOnboardingAppearance() + func setupNavigationBarAppearance() +} + +extension OnboardingViewControllerAppearance { + + static var actionButtonHeight: CGFloat { return 46 } + static var actionButtonMargin: CGFloat { return 12 } + static var viewBottomPaddingHeight: CGFloat { return 11 } + + func setupOnboardingAppearance() { + overrideUserInterfaceStyle = .light + view.backgroundColor = Asset.Colors.Background.onboardingBackground.color + + setupNavigationBarAppearance() + + let backItem = UIBarButtonItem() + backItem.title = L10n.Common.Controls.Actions.back + navigationItem.backBarButtonItem = backItem + } + + func setupNavigationBarAppearance() { + // use TransparentBackground so view push / dismiss will be more visual nature + // please add opaque background for status bar manually if needs + let barAppearance = UINavigationBarAppearance() + barAppearance.configureWithTransparentBackground() + navigationController?.navigationBar.standardAppearance = barAppearance + navigationController?.navigationBar.compactAppearance = barAppearance + navigationController?.navigationBar.scrollEdgeAppearance = barAppearance + } + + func setupNavigationBarBackgroundView() { + let navigationBarBackgroundView: UIView = { + let view = UIView() + view.backgroundColor = Asset.Colors.Background.onboardingBackground.color + return view + }() + + navigationBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(navigationBarBackgroundView) + NSLayoutConstraint.activate([ + navigationBarBackgroundView.topAnchor.constraint(equalTo: view.topAnchor), + navigationBarBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + navigationBarBackgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + navigationBarBackgroundView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor), + ]) + } + +} diff --git a/Mastodon/Scene/Welcome/WelcomeViewController.swift b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift similarity index 61% rename from Mastodon/Scene/Welcome/WelcomeViewController.swift rename to Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift index f2e30670d..e832e5a43 100644 --- a/Mastodon/Scene/Welcome/WelcomeViewController.swift +++ b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift @@ -13,15 +13,16 @@ final class WelcomeViewController: UIViewController, NeedsDependency { weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } - let logoImageView: UIImageView = { - let imageView = UIImageView(image: Asset.welcomeLogo.image) + private(set) lazy var logoImageView: UIImageView = { + let image = view.traitCollection.userInterfaceIdiom == .phone ? Asset.Welcome.mastodonLogo.image : Asset.Welcome.mastodonLogoLarge.image + let imageView = UIImageView(image: image) imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() let sloganLabel: UILabel = { let label = UILabel() - label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.boldSystemFont(ofSize: 34)) + label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold)) label.textColor = Asset.Colors.Label.primary.color label.text = L10n.Scene.Welcome.slogan label.adjustsFontForContentSizeCategory = true @@ -31,8 +32,7 @@ final class WelcomeViewController: UIViewController, NeedsDependency { }() let signUpButton: PrimaryActionButton = { - let button = PrimaryActionButton(type: .system) - button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold)) + let button = PrimaryActionButton() button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal) button.translatesAutoresizingMaskIntoConstraints = false return button @@ -47,6 +47,11 @@ final class WelcomeViewController: UIViewController, NeedsDependency { button.translatesAutoresizingMaskIntoConstraints = false return button }() + + deinit { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) + } + } extension WelcomeViewController { @@ -54,18 +59,13 @@ extension WelcomeViewController { override func viewDidLoad() { super.viewDidLoad() - overrideUserInterfaceStyle = .light - view.backgroundColor = Asset.Colors.Background.onboardingBackground.color - navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) - navigationController?.navigationBar.shadowImage = UIImage() - navigationController?.navigationBar.isTranslucent = true - navigationController?.view.backgroundColor = .clear + setupOnboardingAppearance() view.addSubview(logoImageView) NSLayoutConstraint.activate([ + logoImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor), logoImageView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 35), view.readableContentGuide.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor, constant: 35), - logoImageView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor, constant: 46), logoImageView.heightAnchor.constraint(equalTo: logoImageView.widthAnchor, multiplier: 65.4/265.1), ]) @@ -79,34 +79,43 @@ extension WelcomeViewController { view.addSubview(signInButton) view.addSubview(signUpButton) NSLayoutConstraint.activate([ - signInButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 12), - view.readableContentGuide.trailingAnchor.constraint(equalTo: signInButton.trailingAnchor, constant: 12), - view.readableContentGuide.bottomAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: 11), + signInButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: WelcomeViewController.actionButtonMargin), + view.readableContentGuide.trailingAnchor.constraint(equalTo: signInButton.trailingAnchor, constant: WelcomeViewController.actionButtonMargin), + view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight), + signInButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.defaultHigh), - signUpButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 12), - view.readableContentGuide.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor, constant: 12), - signInButton.topAnchor.constraint(equalTo: signUpButton.bottomAnchor, constant: 5) + signInButton.topAnchor.constraint(equalTo: signUpButton.bottomAnchor, constant: 9), + signUpButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: WelcomeViewController.actionButtonMargin), + view.readableContentGuide.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor, constant: WelcomeViewController.actionButtonMargin), + signUpButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.defaultHigh), ]) signUpButton.addTarget(self, action: #selector(signUpButtonDidClicked(_:)), for: .touchUpInside) signInButton.addTarget(self, action: #selector(signInButtonDidClicked(_:)), for: .touchUpInside) } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - navigationController?.setNavigationBarHidden(true, animated: false) - } + override var preferredStatusBarStyle: UIStatusBarStyle { return .darkContent } } extension WelcomeViewController { @objc 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 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 +extension WelcomeViewController: OnboardingViewControllerAppearance { } + +// MARK: - UIAdaptivePresentationControllerDelegate +extension WelcomeViewController: UIAdaptivePresentationControllerDelegate { + func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle { + return .fullScreen } } diff --git a/Mastodon/Scene/Share/NavigationController/DarkContentStatusBarStyleNavigationController.swift b/Mastodon/Scene/Share/NavigationController/DarkContentStatusBarStyleNavigationController.swift new file mode 100644 index 000000000..0fa4a0e20 --- /dev/null +++ b/Mastodon/Scene/Share/NavigationController/DarkContentStatusBarStyleNavigationController.swift @@ -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 + } +} diff --git a/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift b/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift index 5533daed6..0d68cd74d 100644 --- a/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift +++ b/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift @@ -13,6 +13,7 @@ class PrimaryActionButton: UIButton { lazy var activityIndicator: UIActivityIndicatorView = { let indicator = UIActivityIndicatorView(style: .medium) + indicator.color = .white indicator.hidesWhenStopped = true indicator.translatesAutoresizingMaskIntoConstraints = false return indicator @@ -29,6 +30,19 @@ class PrimaryActionButton: UIButton { super.init(coder: coder) _init() } + +} + +extension PrimaryActionButton { + + private func _init() { + titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold)) + setTitleColor(.white, for: .normal) + setBackgroundImage(UIImage.placeholder(color: Asset.Colors.Button.highlight.color), for: .normal) + setBackgroundImage(UIImage.placeholder(color: Asset.Colors.Button.highlight.color.withAlphaComponent(0.5)), for: .highlighted) + setBackgroundImage(UIImage.placeholder(color: Asset.Colors.Button.disabled.color), for: .disabled) + applyCornerRadius(radius: 10) + } func showLoading() { guard !isLoading else { return } @@ -55,14 +69,3 @@ class PrimaryActionButton: UIButton { self.setTitle(originalButtonTitle, for: .disabled) } } - -extension PrimaryActionButton { - private func _init() { - titleLabel?.font = .preferredFont(forTextStyle: .headline) - setTitleColor(Asset.Colors.lightWhite.color, for: .normal) - setBackgroundImage(UIImage.placeholder(color: Asset.Colors.lightBrandBlue.color), for: .normal) - setBackgroundImage(UIImage.placeholder(color: Asset.Colors.lightDisabled.color), for: .disabled) - applyCornerRadius(radius: 10) - setInsets(forContentPadding: UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0), imageTitlePadding: 0) - } -} diff --git a/Mastodon/Supporting Files/AppDelegate.swift b/Mastodon/Supporting Files/AppDelegate.swift index 78c24f21f..cfac7f1a5 100644 --- a/Mastodon/Supporting Files/AppDelegate.swift +++ b/Mastodon/Supporting Files/AppDelegate.swift @@ -13,8 +13,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let appContext = AppContext() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. return true + + // Update app version info. See: `Settings.bundle` + UserDefaults.standard.setValue(UIApplication.appVersion(), forKey: "Mastodon.appVersion") + UserDefaults.standard.setValue(UIApplication.appBuild(), forKey: "Mastodon.appBundle") } // MARK: UISceneSession Lifecycle diff --git a/Mastodon/Supporting Files/SceneDelegate.swift b/Mastodon/Supporting Files/SceneDelegate.swift index 934c115ce..e13395ccd 100644 --- a/Mastodon/Supporting Files/SceneDelegate.swift +++ b/Mastodon/Supporting Files/SceneDelegate.swift @@ -25,22 +25,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { self.coordinator = sceneCoordinator sceneCoordinator.setup() - -// 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) -// } - + sceneCoordinator.setupOnboardingIfNeeds(animated: false) window.makeKeyAndVisible() } diff --git a/Mastodon/Supporting Files/Settings.bundle/Root.plist b/Mastodon/Supporting Files/Settings.bundle/Root.plist new file mode 100644 index 000000000..8ce400b04 --- /dev/null +++ b/Mastodon/Supporting Files/Settings.bundle/Root.plist @@ -0,0 +1,37 @@ + + + + + StringsTable + Root + PreferenceSpecifiers + + + Type + PSGroupSpecifier + Title + About + + + Type + PSTitleValueSpecifier + Title + Version + Key + Mastodon.appVersion + DefaultValue + 1.0.0 + + + Type + PSTitleValueSpecifier + Title + Build + Key + Mastodon.appBundle + DefaultValue + 1 + + + + diff --git a/Mastodon/Supporting Files/Settings.bundle/en.lproj/Root.strings b/Mastodon/Supporting Files/Settings.bundle/en.lproj/Root.strings new file mode 100644 index 000000000..8cd87b9d6 Binary files /dev/null and b/Mastodon/Supporting Files/Settings.bundle/en.lproj/Root.strings differ diff --git a/MastodonSDK/Sources/MastodonSDK/API/Error/Mastodon+API+Error.swift b/MastodonSDK/Sources/MastodonSDK/API/Error/Mastodon+API+Error.swift index ee649b43e..05540feda 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Error/Mastodon+API+Error.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Error/Mastodon+API+Error.swift @@ -39,7 +39,7 @@ extension Mastodon.API.Error: LocalizedError { public var errorDescription: String? { guard let mastodonError = mastodonError else { - return nil + return "HTTP \(httpResponseStatus.code)" } switch mastodonError { case .generic(let error): @@ -49,7 +49,7 @@ extension Mastodon.API.Error: LocalizedError { public var failureReason: String? { guard let mastodonError = mastodonError else { - return nil + return httpResponseStatus.reasonPhrase } switch mastodonError { case .generic(let error):