diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index e707ae0f..3061b6e4 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -22,7 +22,7 @@ 0FAA101C25E10E760017CCDE /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA101B25E10E760017CCDE /* UIFont.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 */; }; + 0FB3D2FE25E4CB6400AAD544 /* OnboardingHeadlineTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2FD25E4CB6400AAD544 /* OnboardingHeadlineTableViewCell.swift */; }; 0FB3D30825E524C600AAD544 /* PickServerCategoriesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D30725E524C600AAD544 /* PickServerCategoriesCell.swift */; }; 0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */; }; 0FB3D31E25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D31D25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift */; }; @@ -195,6 +195,10 @@ DB0617ED277F02C50030EE79 /* OnboardingNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617EC277F02C50030EE79 /* OnboardingNavigationController.swift */; }; DB0617EF277F12720030EE79 /* NavigationActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617EE277F12720030EE79 /* NavigationActionView.swift */; }; DB0617F1278413D00030EE79 /* PickServerServerSectionTableHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617F0278413D00030EE79 /* PickServerServerSectionTableHeaderView.swift */; }; + DB0617F527855AB90030EE79 /* ServerRuleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617F427855AB90030EE79 /* ServerRuleSection.swift */; }; + DB0617FD27855BFE0030EE79 /* ServerRuleItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617FC27855BFE0030EE79 /* ServerRuleItem.swift */; }; + DB0617FF27855D6C0030EE79 /* MastodonServerRulesViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617FE27855D6C0030EE79 /* MastodonServerRulesViewModel+Diffable.swift */; }; + DB0618012785732C0030EE79 /* ServerRulesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0618002785732C0030EE79 /* ServerRulesTableViewCell.swift */; }; DB084B5725CBC56C00F898ED /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB084B5625CBC56C00F898ED /* Status.swift */; }; DB0AC6FC25CD02E600D75117 /* APIService+Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0AC6FB25CD02E600D75117 /* APIService+Instance.swift */; }; DB0C946526A6FD4D0088FB11 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB0C946426A6FD4D0088FB11 /* AlamofireImage */; }; @@ -244,7 +248,6 @@ DB427DE225BAA00100D1B89D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DB427DE025BAA00100D1B89D /* LaunchScreen.storyboard */; }; DB427DED25BAA00100D1B89D /* MastodonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DEC25BAA00100D1B89D /* MastodonTests.swift */; }; DB427DF825BAA00100D1B89D /* MastodonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DF725BAA00100D1B89D /* MastodonUITests.swift */; }; - DB44384F25E8C1FA008912A2 /* CALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44384E25E8C1FA008912A2 /* CALayer.swift */; }; DB443CD42694627B00159B29 /* AppearanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB443CD32694627B00159B29 /* AppearanceView.swift */; }; DB44767B260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */; }; DB447681260B3ED600B66B82 /* CustomEmojiPickerSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */; }; @@ -782,7 +785,7 @@ 0FAA101B25E10E760017CCDE /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.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 = ""; }; + 0FB3D2FD25E4CB6400AAD544 /* OnboardingHeadlineTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingHeadlineTableViewCell.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 = ""; }; 0FB3D31D25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoryCollectionViewCell.swift; sourceTree = ""; }; @@ -977,6 +980,10 @@ DB0617EC277F02C50030EE79 /* OnboardingNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingNavigationController.swift; sourceTree = ""; }; DB0617EE277F12720030EE79 /* NavigationActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationActionView.swift; sourceTree = ""; }; DB0617F0278413D00030EE79 /* PickServerServerSectionTableHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerServerSectionTableHeaderView.swift; sourceTree = ""; }; + DB0617F427855AB90030EE79 /* ServerRuleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerRuleSection.swift; sourceTree = ""; }; + DB0617FC27855BFE0030EE79 /* ServerRuleItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerRuleItem.swift; sourceTree = ""; }; + DB0617FE27855D6C0030EE79 /* MastodonServerRulesViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonServerRulesViewModel+Diffable.swift"; sourceTree = ""; }; + DB0618002785732C0030EE79 /* ServerRulesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerRulesTableViewCell.swift; sourceTree = ""; }; DB084B5625CBC56C00F898ED /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = ""; }; DB0AC6FB25CD02E600D75117 /* APIService+Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Instance.swift"; sourceTree = ""; }; DB0C946A26A700AB0088FB11 /* MastodonUser+Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonUser+Property.swift"; sourceTree = ""; }; @@ -1033,7 +1040,6 @@ DB427DF325BAA00100D1B89D /* MastodonUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MastodonUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DB427DF725BAA00100D1B89D /* MastodonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonUITests.swift; sourceTree = ""; }; DB427DF925BAA00100D1B89D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - DB44384E25E8C1FA008912A2 /* CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CALayer.swift; sourceTree = ""; }; DB443CD32694627B00159B29 /* AppearanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceView.swift; sourceTree = ""; }; DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerInputView.swift; sourceTree = ""; }; DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerSection.swift; sourceTree = ""; }; @@ -1598,7 +1604,6 @@ 0FB3D2FC25E4CB4B00AAD544 /* TableViewCell */ = { isa = PBXGroup; children = ( - 0FB3D2FD25E4CB6400AAD544 /* PickServerTitleCell.swift */, 0FB3D33725E6401400AAD544 /* PickServerCell.swift */, DB0F814F264D1E2500F2A12B /* PickServerLoaderTableViewCell.swift */, ); @@ -1862,28 +1867,21 @@ 2D76319C25C151DE00929FB9 /* Diffiable */ = { isa = PBXGroup; children = ( - 2D76319D25C151F600929FB9 /* Section */, - 2D7631B125C159E700929FB9 /* Item */, + DB4F097826A039B400D62E92 /* Onboarding */, + DB0617FB27855B740030EE79 /* Account */, + DB0617F827855B170030EE79 /* User */, + DB0617F927855B460030EE79 /* Profile */, + DB4F097926A039C400D62E92 /* Status */, + DB0617F627855AF30030EE79 /* Poll */, + DB4F097626A0398000D62E92 /* Compose */, + DB0617F727855B010030EE79 /* Notification */, + DB4F097726A039A200D62E92 /* Search */, + DB0617FA27855B660030EE79 /* Settings */, DBCBED2226132E1D00B49291 /* FetchedResultsController */, ); path = Diffiable; sourceTree = ""; }; - 2D76319D25C151F600929FB9 /* Section */ = { - isa = PBXGroup; - children = ( - DB4F097926A039C400D62E92 /* Status */, - DB4F097826A039B400D62E92 /* Onboarding */, - DB4F097726A039A200D62E92 /* Search */, - DB4F097626A0398000D62E92 /* Compose */, - 2D4AD8A126316CD200613EFC /* SelectedAccountSection.swift */, - DB6D9F7C26358ED4008423CD /* SettingsSection.swift */, - DBA94433265CBB5300C537E1 /* ProfileFieldSection.swift */, - DB6B74FB272FF55800C70B6E /* UserSection.swift */, - ); - path = Section; - sourceTree = ""; - }; 2D7631A425C1532200929FB9 /* Share */ = { isa = PBXGroup; children = ( @@ -1930,29 +1928,6 @@ path = TableviewCell; sourceTree = ""; }; - 2D7631B125C159E700929FB9 /* Item */ = { - isa = PBXGroup; - children = ( - 2D7631B225C159F700929FB9 /* Item.swift */, - DB6B74FD272FF59000C70B6E /* UserItem.swift */, - 2D198642261BF09500F0B013 /* SearchResultItem.swift */, - DB4F097C26A03A5B00D62E92 /* SearchHistoryItem.swift */, - 2D4AD8A726316D3500613EFC /* SelectedAccountItem.swift */, - 2D7867182625B77500211898 /* NotificationItem.swift */, - DB4481CB25EE2AFE00BEFB67 /* PollItem.swift */, - DB1E347725F519300079D7DF /* PickServerItem.swift */, - DB1FD45925F27898004CFCFC /* CategoryPickerItem.swift */, - DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */, - DB3667A0268ABB2E0027D07F /* ComposeStatusAttachmentItem.swift */, - DB3667A7268AE2900027D07F /* ComposeStatusPollItem.swift */, - DB44768A260B3F2100B66B82 /* CustomEmojiPickerItem.swift */, - DB6D9F8326358EEC008423CD /* SettingsItem.swift */, - DBBF1DCA2652539E00E5B703 /* AutoCompleteItem.swift */, - DBA94435265CBB7400C537E1 /* ProfileFieldItem.swift */, - ); - path = Item; - sourceTree = ""; - }; 2DA504672601ADBA008F4E6C /* Decoration */ = { isa = PBXGroup; children = ( @@ -2109,6 +2084,60 @@ path = Deprecated; sourceTree = ""; }; + DB0617F627855AF30030EE79 /* Poll */ = { + isa = PBXGroup; + children = ( + DB4481C525EE2ADA00BEFB67 /* PollSection.swift */, + DB4481CB25EE2AFE00BEFB67 /* PollItem.swift */, + ); + path = Poll; + sourceTree = ""; + }; + DB0617F727855B010030EE79 /* Notification */ = { + isa = PBXGroup; + children = ( + 2D35237926256D920031AF25 /* NotificationSection.swift */, + 2D7867182625B77500211898 /* NotificationItem.swift */, + ); + path = Notification; + sourceTree = ""; + }; + DB0617F827855B170030EE79 /* User */ = { + isa = PBXGroup; + children = ( + DB6B74FB272FF55800C70B6E /* UserSection.swift */, + DB6B74FD272FF59000C70B6E /* UserItem.swift */, + ); + path = User; + sourceTree = ""; + }; + DB0617F927855B460030EE79 /* Profile */ = { + isa = PBXGroup; + children = ( + DBA94433265CBB5300C537E1 /* ProfileFieldSection.swift */, + DBA94435265CBB7400C537E1 /* ProfileFieldItem.swift */, + ); + path = Profile; + sourceTree = ""; + }; + DB0617FA27855B660030EE79 /* Settings */ = { + isa = PBXGroup; + children = ( + DB6D9F7C26358ED4008423CD /* SettingsSection.swift */, + DB6D9F8326358EEC008423CD /* SettingsItem.swift */, + ); + path = Settings; + sourceTree = ""; + }; + DB0617FB27855B740030EE79 /* Account */ = { + isa = PBXGroup; + children = ( + 2D4AD8A126316CD200613EFC /* SelectedAccountSection.swift */, + 2D4AD8A726316D3500613EFC /* SelectedAccountItem.swift */, + ); + path = Account; + sourceTree = ""; + }; DB084B5125CBC56300F898ED /* CoreDataStack */ = { isa = PBXGroup; children = ( @@ -2360,10 +2389,15 @@ isa = PBXGroup; children = ( DB66729525F9F91600D60309 /* ComposeStatusSection.swift */, + DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */, DB36679E268ABAF20027D07F /* ComposeStatusAttachmentSection.swift */, + DB3667A0268ABB2E0027D07F /* ComposeStatusAttachmentItem.swift */, DB3667A5268AE2620027D07F /* ComposeStatusPollSection.swift */, + DB3667A7268AE2900027D07F /* ComposeStatusPollItem.swift */, DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */, + DB44768A260B3F2100B66B82 /* CustomEmojiPickerItem.swift */, DBBF1DC82652538500E5B703 /* AutoCompleteSection.swift */, + DBBF1DCA2652539E00E5B703 /* AutoCompleteItem.swift */, ); path = Compose; sourceTree = ""; @@ -2374,7 +2408,9 @@ 2DE0FAC02615F04D00CDF649 /* RecommendHashTagSection.swift */, 2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */, 2D198648261C0B8500F0B013 /* SearchResultSection.swift */, + 2D198642261BF09500F0B013 /* SearchResultItem.swift */, DB4F097A26A039FF00D62E92 /* SearchHistorySection.swift */, + DB4F097C26A03A5B00D62E92 /* SearchHistoryItem.swift */, ); path = Search; sourceTree = ""; @@ -2383,7 +2419,11 @@ isa = PBXGroup; children = ( DB1FD44325F26CCC004CFCFC /* PickServerSection.swift */, + DB1E347725F519300079D7DF /* PickServerItem.swift */, DB1E346725F518E20079D7DF /* CategoryPickerSection.swift */, + DB1FD45925F27898004CFCFC /* CategoryPickerItem.swift */, + DB0617F427855AB90030EE79 /* ServerRuleSection.swift */, + DB0617FC27855BFE0030EE79 /* ServerRuleItem.swift */, ); path = Onboarding; sourceTree = ""; @@ -2392,8 +2432,7 @@ isa = PBXGroup; children = ( 2D76319E25C1521200929FB9 /* StatusSection.swift */, - DB4481C525EE2ADA00BEFB67 /* PollSection.swift */, - 2D35237926256D920031AF25 /* NotificationSection.swift */, + 2D7631B225C159F700929FB9 /* Item.swift */, 5BB04FF4262F0E6D0043BFF6 /* ReportSection.swift */, ); path = Status; @@ -2533,10 +2572,11 @@ DB68A03825E900CC00CFDF14 /* Share */ = { isa = PBXGroup; children = ( - 2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */, DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */, DB029E94266A20430062874E /* MastodonAuthenticationController.swift */, + 2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */, DB0617EC277F02C50030EE79 /* OnboardingNavigationController.swift */, + 0FB3D2FD25E4CB6400AAD544 /* OnboardingHeadlineTableViewCell.swift */, DB0617EE277F12720030EE79 /* NavigationActionView.swift */, ); path = Share; @@ -2595,6 +2635,8 @@ children = ( DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */, DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */, + DB0617FE27855D6C0030EE79 /* MastodonServerRulesViewModel+Diffable.swift */, + DB0618002785732C0030EE79 /* ServerRulesTableViewCell.swift */, ); path = ServerRules; sourceTree = ""; @@ -2797,7 +2839,6 @@ 2DF123A625C3B0210020F248 /* ActiveLabel.swift */, 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */, 0F20223826146553000C64BF /* Array.swift */, - DB44384E25E8C1FA008912A2 /* CALayer.swift */, 2D206B8525F5FB0900143C56 /* Double.swift */, DB97131E2666078B00BD1E90 /* Date.swift */, DBB3BA2926A81C020004F2D4 /* FLAnimatedImageView.swift */, @@ -3954,6 +3995,7 @@ DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */, DB02CDAB26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift in Sources */, DB63BE7F268DD1070011D3F9 /* NotificationViewController+StatusProvider.swift in Sources */, + DB0617FF27855D6C0030EE79 /* MastodonServerRulesViewModel+Diffable.swift in Sources */, DBB5255E2611F07A002F1F29 /* ProfileViewModel.swift in Sources */, 2D8434FB25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift in Sources */, 0F1E2D0B2615C39400C38565 /* DoubleTitleLabelNavigationBarTitleView.swift in Sources */, @@ -3990,6 +4032,7 @@ DB789A0B25F9F2950071ACA0 /* ComposeViewController.swift in Sources */, DB938F0926240F3C00E5B6C1 /* RemoteThreadViewModel.swift in Sources */, DB0617ED277F02C50030EE79 /* OnboardingNavigationController.swift in Sources */, + DB0617F527855AB90030EE79 /* ServerRuleSection.swift in Sources */, DBBC24AE26A53DC100398BB9 /* ReplicaStatusView.swift in Sources */, DB75BF1E263C1C1B00EDBF1F /* CustomScheduler.swift in Sources */, 0FAA102725E1126A0017CCDE /* MastodonPickServerViewController.swift in Sources */, @@ -4101,7 +4144,7 @@ DB71FD3C25F8A1C500512AE1 /* APIService+Persist+PersistCache.swift in Sources */, 2DA6055125F74407006356F9 /* AudioContainerViewModel.swift in Sources */, DB4F0968269ED8AD00D62E92 /* SearchHistoryTableHeaderView.swift in Sources */, - 0FB3D2FE25E4CB6400AAD544 /* PickServerTitleCell.swift in Sources */, + 0FB3D2FE25E4CB6400AAD544 /* OnboardingHeadlineTableViewCell.swift in Sources */, 5DA732CC2629CEF500A92342 /* UIView+Remove.swift in Sources */, DBAEDE5C267A058D00D25FF5 /* BlurhashImageCacheService.swift in Sources */, 2D38F1DF25CD46A400561493 /* HomeTimelineViewController+Provider.swift in Sources */, @@ -4146,6 +4189,7 @@ 2D34D9D126148D9E0081BFC0 /* APIService+Recommend.swift in Sources */, DBB525562611EDCA002F1F29 /* UserTimelineViewModel.swift in Sources */, 2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */, + DB0618012785732C0030EE79 /* ServerRulesTableViewCell.swift in Sources */, DB221B16260C395900AEFE46 /* CustomEmojiPickerInputViewModel.swift in Sources */, DB0617EF277F12720030EE79 /* NavigationActionView.swift in Sources */, DB1FD43625F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift in Sources */, @@ -4216,7 +4260,6 @@ DB6180F826391D660018D199 /* MediaPreviewingViewController.swift in Sources */, DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */, DB6B75022730060700C70B6E /* UserProviderFacade+UITableViewDelegate.swift in Sources */, - DB44384F25E8C1FA008912A2 /* CALayer.swift in Sources */, 2D34D9CB261489930081BFC0 /* SearchViewController+Recommend.swift in Sources */, DB71C7CB271D5A0300BE3819 /* LineChartView.swift in Sources */, DB938F1526241FDF00E5B6C1 /* APIService+Thread.swift in Sources */, @@ -4358,6 +4401,7 @@ DBAFB7352645463500371D5F /* Emojis.swift in Sources */, DBCCC71E25F73297007E1AB6 /* APIService+Reblog.swift in Sources */, DBE3CE13261D7D4200430CC6 /* StatusTableViewControllerAspect.swift in Sources */, + DB0617FD27855BFE0030EE79 /* ServerRuleItem.swift in Sources */, 5BB04FD5262E7AFF0043BFF6 /* ReportViewController.swift in Sources */, DBAE3F942616E28B004B8251 /* APIService+Follow.swift in Sources */, ); diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3f83af78..e52bb1d9 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,6 +1,60 @@ { "object": { "pins": [ + { + "package": "Alamofire", + "repositoryURL": "https://github.com/Alamofire/Alamofire.git", + "state": { + "branch": null, + "revision": "f82c23a8a7ef8dc1a49a8bfc6a96883e79121864", + "version": "5.5.0" + } + }, + { + "package": "AlamofireImage", + "repositoryURL": "https://github.com/Alamofire/AlamofireImage.git", + "state": { + "branch": null, + "revision": "98cbb00ce0ec5fc8e52a5b50a6bfc08d3e5aee10", + "version": "4.2.0" + } + }, + { + "package": "AlamofireNetworkActivityIndicator", + "repositoryURL": "https://github.com/Alamofire/AlamofireNetworkActivityIndicator", + "state": { + "branch": null, + "revision": "392bed083e8d193aca16bfa684ee24e4bcff0510", + "version": "3.1.0" + } + }, + { + "package": "CommonOSLog", + "repositoryURL": "https://github.com/MainasuK/CommonOSLog", + "state": { + "branch": null, + "revision": "c121624a30698e9886efe38aebb36ff51c01b6c2", + "version": "0.1.1" + } + }, + { + "package": "DiffableDataSources", + "repositoryURL": "https://github.com/MainasuK/DiffableDataSources.git", + "state": { + "branch": "feature/async-display-table", + "revision": "73393a97690959d24387c95594c045c62d9c47cf", + "version": null + } + }, + { + "package": "DifferenceKit", + "repositoryURL": "https://github.com/ra1028/DifferenceKit.git", + "state": { + "branch": null, + "revision": "62745d7780deef4a023a792a1f8f763ec7bf9705", + "version": "1.2.0" + } + }, { "package": "FLAnimatedImage", "repositoryURL": "https://github.com/Flipboard/FLAnimatedImage", @@ -10,6 +64,42 @@ "version": "1.0.16" } }, + { + "package": "FPSIndicator", + "repositoryURL": "https://github.com/MainasuK/FPSIndicator.git", + "state": { + "branch": null, + "revision": "e4a5067ccd5293b024c767f09e51056afd4a4796", + "version": "1.1.0" + } + }, + { + "package": "Fuzi", + "repositoryURL": "https://github.com/cezheng/Fuzi.git", + "state": { + "branch": null, + "revision": "f08c8323da21e985f3772610753bcfc652c2103f", + "version": "3.1.3" + } + }, + { + "package": "KeychainAccess", + "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess.git", + "state": { + "branch": null, + "revision": "84e546727d66f1adc5439debad16270d0fdd04e7", + "version": "4.2.2" + } + }, + { + "package": "MetaTextKit", + "repositoryURL": "https://github.com/TwidereProject/MetaTextKit.git", + "state": { + "branch": null, + "revision": "7af4182f64329440a4656f2cba307cb5848e496a", + "version": "2.1.2" + } + }, { "package": "Nuke", "repositoryURL": "https://github.com/kean/Nuke.git", @@ -28,6 +118,42 @@ "version": "8.0.0" } }, + { + "package": "Pageboy", + "repositoryURL": "https://github.com/uias/Pageboy", + "state": { + "branch": null, + "revision": "34ecb6e7c4e0e07494960ab2f7cc9a02293915a6", + "version": "3.6.2" + } + }, + { + "package": "PanModal", + "repositoryURL": "https://github.com/slackhq/PanModal.git", + "state": { + "branch": null, + "revision": "b012aecb6b67a8e46369227f893c12544846613f", + "version": "1.2.7" + } + }, + { + "package": "SDWebImage", + "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", + "state": { + "branch": null, + "revision": "0fff0d7505b5306348263ea64fcc561253bbeb21", + "version": "5.12.2" + } + }, + { + "package": "swift-collections", + "repositoryURL": "https://github.com/apple/swift-collections.git", + "state": { + "branch": null, + "revision": "9d8719c8bebdc79740b6969c912ac706eb721d7a", + "version": "0.0.7" + } + }, { "package": "swift-nio", "repositoryURL": "https://github.com/apple/swift-nio.git", @@ -63,6 +189,33 @@ "revision": "b3dcd7dbd0d488e1a7077cb33b00f2083e382f07", "version": "5.0.1" } + }, + { + "package": "Tabman", + "repositoryURL": "https://github.com/uias/Tabman", + "state": { + "branch": null, + "revision": "f43489cdd743ba7ad86a422ebb5fcbf34e333df4", + "version": "2.11.1" + } + }, + { + "package": "ThirdPartyMailer", + "repositoryURL": "https://github.com/vtourraine/ThirdPartyMailer.git", + "state": { + "branch": null, + "revision": "779da6ce0793b461ccbbac2804755c1e29b6fa63", + "version": "1.8.0" + } + }, + { + "package": "TOCropViewController", + "repositoryURL": "https://github.com/TimOliver/TOCropViewController.git", + "state": { + "branch": null, + "revision": "dad97167bf1be16aeecd109130900995dd01c515", + "version": "2.6.0" + } } ] }, diff --git a/Mastodon/Diffiable/Item/SelectedAccountItem.swift b/Mastodon/Diffiable/Account/SelectedAccountItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/SelectedAccountItem.swift rename to Mastodon/Diffiable/Account/SelectedAccountItem.swift diff --git a/Mastodon/Diffiable/Section/SelectedAccountSection.swift b/Mastodon/Diffiable/Account/SelectedAccountSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/SelectedAccountSection.swift rename to Mastodon/Diffiable/Account/SelectedAccountSection.swift diff --git a/Mastodon/Diffiable/Item/AutoCompleteItem.swift b/Mastodon/Diffiable/Compose/AutoCompleteItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/AutoCompleteItem.swift rename to Mastodon/Diffiable/Compose/AutoCompleteItem.swift diff --git a/Mastodon/Diffiable/Section/Compose/AutoCompleteSection.swift b/Mastodon/Diffiable/Compose/AutoCompleteSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Compose/AutoCompleteSection.swift rename to Mastodon/Diffiable/Compose/AutoCompleteSection.swift diff --git a/Mastodon/Diffiable/Item/ComposeStatusAttachmentItem.swift b/Mastodon/Diffiable/Compose/ComposeStatusAttachmentItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/ComposeStatusAttachmentItem.swift rename to Mastodon/Diffiable/Compose/ComposeStatusAttachmentItem.swift diff --git a/Mastodon/Diffiable/Section/Compose/ComposeStatusAttachmentSection.swift b/Mastodon/Diffiable/Compose/ComposeStatusAttachmentSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Compose/ComposeStatusAttachmentSection.swift rename to Mastodon/Diffiable/Compose/ComposeStatusAttachmentSection.swift diff --git a/Mastodon/Diffiable/Item/ComposeStatusItem.swift b/Mastodon/Diffiable/Compose/ComposeStatusItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/ComposeStatusItem.swift rename to Mastodon/Diffiable/Compose/ComposeStatusItem.swift diff --git a/Mastodon/Diffiable/Item/ComposeStatusPollItem.swift b/Mastodon/Diffiable/Compose/ComposeStatusPollItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/ComposeStatusPollItem.swift rename to Mastodon/Diffiable/Compose/ComposeStatusPollItem.swift diff --git a/Mastodon/Diffiable/Section/Compose/ComposeStatusPollSection.swift b/Mastodon/Diffiable/Compose/ComposeStatusPollSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Compose/ComposeStatusPollSection.swift rename to Mastodon/Diffiable/Compose/ComposeStatusPollSection.swift diff --git a/Mastodon/Diffiable/Section/Compose/ComposeStatusSection.swift b/Mastodon/Diffiable/Compose/ComposeStatusSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Compose/ComposeStatusSection.swift rename to Mastodon/Diffiable/Compose/ComposeStatusSection.swift diff --git a/Mastodon/Diffiable/Item/CustomEmojiPickerItem.swift b/Mastodon/Diffiable/Compose/CustomEmojiPickerItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/CustomEmojiPickerItem.swift rename to Mastodon/Diffiable/Compose/CustomEmojiPickerItem.swift diff --git a/Mastodon/Diffiable/Section/Compose/CustomEmojiPickerSection.swift b/Mastodon/Diffiable/Compose/CustomEmojiPickerSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Compose/CustomEmojiPickerSection.swift rename to Mastodon/Diffiable/Compose/CustomEmojiPickerSection.swift diff --git a/Mastodon/Diffiable/Item/NotificationItem.swift b/Mastodon/Diffiable/Notification/NotificationItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/NotificationItem.swift rename to Mastodon/Diffiable/Notification/NotificationItem.swift diff --git a/Mastodon/Diffiable/Section/Status/NotificationSection.swift b/Mastodon/Diffiable/Notification/NotificationSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Status/NotificationSection.swift rename to Mastodon/Diffiable/Notification/NotificationSection.swift diff --git a/Mastodon/Diffiable/Item/CategoryPickerItem.swift b/Mastodon/Diffiable/Onboarding/CategoryPickerItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/CategoryPickerItem.swift rename to Mastodon/Diffiable/Onboarding/CategoryPickerItem.swift diff --git a/Mastodon/Diffiable/Section/Onboarding/CategoryPickerSection.swift b/Mastodon/Diffiable/Onboarding/CategoryPickerSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Onboarding/CategoryPickerSection.swift rename to Mastodon/Diffiable/Onboarding/CategoryPickerSection.swift diff --git a/Mastodon/Diffiable/Item/PickServerItem.swift b/Mastodon/Diffiable/Onboarding/PickServerItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/PickServerItem.swift rename to Mastodon/Diffiable/Onboarding/PickServerItem.swift diff --git a/Mastodon/Diffiable/Section/Onboarding/PickServerSection.swift b/Mastodon/Diffiable/Onboarding/PickServerSection.swift similarity index 98% rename from Mastodon/Diffiable/Section/Onboarding/PickServerSection.swift rename to Mastodon/Diffiable/Onboarding/PickServerSection.swift index b2079aab..9f74bad5 100644 --- a/Mastodon/Diffiable/Section/Onboarding/PickServerSection.swift +++ b/Mastodon/Diffiable/Onboarding/PickServerSection.swift @@ -28,7 +28,7 @@ extension PickServerSection { guard let dependency = dependency else { return nil } switch item { case .header: - let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerTitleCell.self), for: indexPath) as! PickServerTitleCell + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: OnboardingHeadlineTableViewCell.self), for: indexPath) as! OnboardingHeadlineTableViewCell return cell case .server(let server, let attribute): let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerCell.self), for: indexPath) as! PickServerCell diff --git a/Mastodon/Diffiable/Onboarding/ServerRuleItem.swift b/Mastodon/Diffiable/Onboarding/ServerRuleItem.swift new file mode 100644 index 00000000..37d8b6ee --- /dev/null +++ b/Mastodon/Diffiable/Onboarding/ServerRuleItem.swift @@ -0,0 +1,21 @@ +// +// ServerRuleItem.swift +// Mastodon +// +// Created by MainasuK on 2022-1-5. +// + +import Foundation +import MastodonSDK + +enum ServerRuleItem: Hashable { + case header(domain: String) + case rule(RuleContext) +} + +extension ServerRuleItem { + struct RuleContext: Hashable { + let index: Int + let rule: Mastodon.Entity.Instance.Rule + } +} diff --git a/Mastodon/Diffiable/Onboarding/ServerRuleSection.swift b/Mastodon/Diffiable/Onboarding/ServerRuleSection.swift new file mode 100644 index 00000000..ba96e873 --- /dev/null +++ b/Mastodon/Diffiable/Onboarding/ServerRuleSection.swift @@ -0,0 +1,34 @@ +// +// ServerRuleSection.swift +// Mastodon +// +// Created by MainasuK on 2022-1-5. +// + +import UIKit + +enum ServerRuleSection: Hashable { + case header + case rules +} + +extension ServerRuleSection { + static func tableViewDiffableDataSource( + tableView: UITableView + ) -> UITableViewDiffableDataSource { + return UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item in + switch item { + case .header(let domain): + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: OnboardingHeadlineTableViewCell.self), for: indexPath) as! OnboardingHeadlineTableViewCell + cell.titleLabel.text = L10n.Scene.ServerRules.title + cell.subTitleLabel.text = L10n.Scene.ServerRules.subtitle(domain) + return cell + case .rule(let ruleContext): + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ServerRulesTableViewCell.self), for: indexPath) as! ServerRulesTableViewCell + cell.indexImageView.image = UIImage(systemName: "\(ruleContext.index).circle.fill") ?? UIImage(systemName: "questionmark.circle.fill") + cell.ruleLabel.text = ruleContext.rule.text + return cell + } + } + } +} diff --git a/Mastodon/Diffiable/Item/PollItem.swift b/Mastodon/Diffiable/Poll/PollItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/PollItem.swift rename to Mastodon/Diffiable/Poll/PollItem.swift diff --git a/Mastodon/Diffiable/Section/Status/PollSection.swift b/Mastodon/Diffiable/Poll/PollSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Status/PollSection.swift rename to Mastodon/Diffiable/Poll/PollSection.swift diff --git a/Mastodon/Diffiable/Item/ProfileFieldItem.swift b/Mastodon/Diffiable/Profile/ProfileFieldItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/ProfileFieldItem.swift rename to Mastodon/Diffiable/Profile/ProfileFieldItem.swift diff --git a/Mastodon/Diffiable/Section/ProfileFieldSection.swift b/Mastodon/Diffiable/Profile/ProfileFieldSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/ProfileFieldSection.swift rename to Mastodon/Diffiable/Profile/ProfileFieldSection.swift diff --git a/Mastodon/Diffiable/Section/Search/RecommendAccountSection.swift b/Mastodon/Diffiable/Search/RecommendAccountSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Search/RecommendAccountSection.swift rename to Mastodon/Diffiable/Search/RecommendAccountSection.swift diff --git a/Mastodon/Diffiable/Section/Search/RecommendHashTagSection.swift b/Mastodon/Diffiable/Search/RecommendHashTagSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Search/RecommendHashTagSection.swift rename to Mastodon/Diffiable/Search/RecommendHashTagSection.swift diff --git a/Mastodon/Diffiable/Item/SearchHistoryItem.swift b/Mastodon/Diffiable/Search/SearchHistoryItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/SearchHistoryItem.swift rename to Mastodon/Diffiable/Search/SearchHistoryItem.swift diff --git a/Mastodon/Diffiable/Section/Search/SearchHistorySection.swift b/Mastodon/Diffiable/Search/SearchHistorySection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Search/SearchHistorySection.swift rename to Mastodon/Diffiable/Search/SearchHistorySection.swift diff --git a/Mastodon/Diffiable/Item/SearchResultItem.swift b/Mastodon/Diffiable/Search/SearchResultItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/SearchResultItem.swift rename to Mastodon/Diffiable/Search/SearchResultItem.swift diff --git a/Mastodon/Diffiable/Section/Search/SearchResultSection.swift b/Mastodon/Diffiable/Search/SearchResultSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Search/SearchResultSection.swift rename to Mastodon/Diffiable/Search/SearchResultSection.swift diff --git a/Mastodon/Diffiable/Item/SettingsItem.swift b/Mastodon/Diffiable/Settings/SettingsItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/SettingsItem.swift rename to Mastodon/Diffiable/Settings/SettingsItem.swift diff --git a/Mastodon/Diffiable/Section/SettingsSection.swift b/Mastodon/Diffiable/Settings/SettingsSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/SettingsSection.swift rename to Mastodon/Diffiable/Settings/SettingsSection.swift diff --git a/Mastodon/Diffiable/Item/Item.swift b/Mastodon/Diffiable/Status/Item.swift similarity index 100% rename from Mastodon/Diffiable/Item/Item.swift rename to Mastodon/Diffiable/Status/Item.swift diff --git a/Mastodon/Diffiable/Section/Status/ReportSection.swift b/Mastodon/Diffiable/Status/ReportSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Status/ReportSection.swift rename to Mastodon/Diffiable/Status/ReportSection.swift diff --git a/Mastodon/Diffiable/Section/Status/StatusSection.swift b/Mastodon/Diffiable/Status/StatusSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/Status/StatusSection.swift rename to Mastodon/Diffiable/Status/StatusSection.swift diff --git a/Mastodon/Diffiable/Item/UserItem.swift b/Mastodon/Diffiable/User/UserItem.swift similarity index 100% rename from Mastodon/Diffiable/Item/UserItem.swift rename to Mastodon/Diffiable/User/UserItem.swift diff --git a/Mastodon/Diffiable/Section/UserSection.swift b/Mastodon/Diffiable/User/UserSection.swift similarity index 100% rename from Mastodon/Diffiable/Section/UserSection.swift rename to Mastodon/Diffiable/User/UserSection.swift diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift index 54e7bc6d..17df72d3 100644 --- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift @@ -35,7 +35,7 @@ final class MastodonPickServerViewController: UIViewController, NeedsDependency let tableView: UITableView = { let tableView = ControlContainableTableView() - tableView.register(PickServerTitleCell.self, forCellReuseIdentifier: String(describing: PickServerTitleCell.self)) + tableView.register(OnboardingHeadlineTableViewCell.self, forCellReuseIdentifier: String(describing: OnboardingHeadlineTableViewCell.self)) tableView.register(PickServerCell.self, forCellReuseIdentifier: String(describing: PickServerCell.self)) tableView.register(PickServerLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: PickServerLoaderTableViewCell.self)) tableView.rowHeight = UITableView.automaticDimension @@ -236,6 +236,12 @@ extension MastodonPickServerViewController { viewModel.viewWillAppear.send() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + tableView.flashScrollIndicators() + } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift index e93d06e1..6633f6c7 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift @@ -14,8 +14,11 @@ import MetaTextKit final class MastodonServerRulesViewController: UIViewController, NeedsDependency { - var disposeBag = Set() + let logger = Logger(subsystem: "MastodonServerRulesViewController", category: "ViewController") + var disposeBag = Set() + private var observations = Set() + weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } @@ -25,8 +28,8 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency let largeTitleLabel: UILabel = { let label = UILabel() - label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold)) - label.textColor = .label + label.font = MastodonServerRulesViewController.largeTitleFont + label.textColor = MastodonServerRulesViewController.largeTitleTextColor label.text = L10n.Scene.ServerRules.title label.numberOfLines = 0 return label @@ -34,56 +37,33 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency private(set) lazy var subtitleLabel: UILabel = { let label = UILabel() - label.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: UIFont.systemFont(ofSize: 20)) - label.textColor = .secondaryLabel + label.font = MastodonServerRulesViewController.subTitleFont + label.textColor = MastodonServerRulesViewController.subTitleTextColor label.text = L10n.Scene.ServerRules.subtitle(viewModel.domain) label.numberOfLines = 0 return label }() - let rulesLabel: UILabel = { - let label = UILabel() - label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold)) - label.textColor = Asset.Colors.Label.primary.color - label.text = "Rules" - label.numberOfLines = 0 - return label + let tableView: UITableView = { + let tableView = UITableView() + tableView.register(OnboardingHeadlineTableViewCell.self, forCellReuseIdentifier: String(describing: OnboardingHeadlineTableViewCell.self)) + tableView.register(ServerRulesTableViewCell.self, forCellReuseIdentifier: String(describing: ServerRulesTableViewCell.self)) + tableView.rowHeight = UITableView.automaticDimension + tableView.separatorStyle = .none + tableView.backgroundColor = .clear + tableView.keyboardDismissMode = .onDrag + if #available(iOS 15.0, *) { + tableView.sectionHeaderTopPadding = 0 + } else { + // Fallback on earlier versions + } + return tableView }() - - let bottomContainerView: UIView = { - let view = UIView() - view.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color - return view - }() - - private(set) lazy var bottomPromptMetaText: MetaText = { - let metaText = MetaText() - metaText.textAttributes = [ - .font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular), maximumPointSize: 22), - .foregroundColor: UIColor.label, - ] - metaText.linkAttributes = [ - .font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular), maximumPointSize: 22), - .foregroundColor: Asset.Colors.brandBlue.color, - ] - metaText.textView.isEditable = false - metaText.textView.isSelectable = false - metaText.textView.isScrollEnabled = false - metaText.textView.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color // needs background color to prevent server rules text overlap - return metaText - }() - - let confirmButton: PrimaryActionButton = { - let button = PrimaryActionButton() - button.setTitle(L10n.Scene.ServerRules.Button.confirm, for: .normal) - return button - }() - - let scrollView: UIScrollView = { - let scrollView = UIScrollView() - scrollView.alwaysBounceVertical = true - scrollView.showsVerticalScrollIndicator = false - return scrollView + + let navigationActionView: NavigationActionView = { + let navigationActionView = NavigationActionView() + navigationActionView.backgroundColor = Asset.Scene.Onboarding.onboardingBackground.color + return navigationActionView }() deinit { @@ -97,224 +77,90 @@ extension MastodonServerRulesViewController { override func viewDidLoad() { super.viewDidLoad() + navigationItem.leftBarButtonItem = UIBarButtonItem() + setupOnboardingAppearance() - configureTitleLabel() - configureMargin() - configTextView() - defer { setupNavigationBarBackgroundView() } - bottomContainerView.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(bottomContainerView) + tableView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(tableView) NSLayoutConstraint.activate([ - view.bottomAnchor.constraint(equalTo: bottomContainerView.bottomAnchor), - bottomContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - bottomContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + tableView.topAnchor.constraint(equalTo: view.topAnchor), + tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) - bottomContainerView.preservesSuperviewLayoutMargins = true + + navigationActionView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(navigationActionView) defer { - view.bringSubviewToFront(bottomContainerView) + view.bringSubviewToFront(navigationActionView) } - - confirmButton.translatesAutoresizingMaskIntoConstraints = false - bottomContainerView.addSubview(confirmButton) NSLayoutConstraint.activate([ - bottomContainerView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: confirmButton.bottomAnchor, constant: MastodonServerRulesViewController.viewBottomPaddingHeight), - confirmButton.leadingAnchor.constraint(equalTo: bottomContainerView.layoutMarginsGuide.leadingAnchor), - bottomContainerView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: confirmButton.trailingAnchor), - confirmButton.heightAnchor.constraint(equalToConstant: MastodonServerRulesViewController.actionButtonHeight).priority(.defaultHigh), + navigationActionView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + navigationActionView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + view.bottomAnchor.constraint(equalTo: navigationActionView.bottomAnchor), ]) - bottomPromptMetaText.textView.translatesAutoresizingMaskIntoConstraints = false - bottomContainerView.addSubview(bottomPromptMetaText.textView) - NSLayoutConstraint.activate([ - bottomPromptMetaText.textView.frameLayoutGuide.topAnchor.constraint(equalTo: bottomContainerView.topAnchor, constant: 20), - bottomPromptMetaText.textView.frameLayoutGuide.leadingAnchor.constraint(equalTo: bottomContainerView.layoutMarginsGuide.leadingAnchor), - bottomPromptMetaText.textView.frameLayoutGuide.trailingAnchor.constraint(equalTo: bottomContainerView.layoutMarginsGuide.trailingAnchor), - confirmButton.topAnchor.constraint(equalTo: bottomPromptMetaText.textView.frameLayoutGuide.bottomAnchor, constant: 20), - ]) + navigationActionView + .observe(\.bounds, options: [.initial, .new]) { [weak self] navigationActionView, _ in + guard let self = self else { return } + let inset = navigationActionView.frame.height + self.tableView.contentInset.bottom = inset + } + .store(in: &observations) - scrollView.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(scrollView) - NSLayoutConstraint.activate([ - scrollView.frameLayoutGuide.topAnchor.constraint(equalTo: view.topAnchor), - scrollView.frameLayoutGuide.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor), - scrollView.frameLayoutGuide.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor), - scrollView.frameLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor), - scrollView.frameLayoutGuide.widthAnchor.constraint(equalTo: scrollView.contentLayoutGuide.widthAnchor), - ]) - - stackView.axis = .vertical - stackView.distribution = .fill - stackView.spacing = 10 - stackView.isLayoutMarginsRelativeArrangement = true - stackView.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0) - stackView.addArrangedSubview(largeTitleLabel) - stackView.addArrangedSubview(subtitleLabel) - stackView.addArrangedSubview(rulesLabel) + tableView.delegate = self + viewModel.setupDiffableDataSource(tableView: tableView) - stackView.translatesAutoresizingMaskIntoConstraints = false - scrollView.addSubview(stackView) - NSLayoutConstraint.activate([ - stackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor), - stackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor), - scrollView.contentLayoutGuide.bottomAnchor.constraint(equalTo: stackView.bottomAnchor), - ]) - - rulesLabel.attributedText = viewModel.rulesAttributedString - confirmButton.addTarget(self, action: #selector(MastodonServerRulesViewController.confirmButtonPressed(_:)), for: .touchUpInside) + navigationActionView.backButton.addTarget(self, action: #selector(MastodonServerRulesViewController.backButtonPressed(_:)), for: .touchUpInside) + navigationActionView.nextButton.addTarget(self, action: #selector(MastodonServerRulesViewController.nextButtonPressed(_:)), for: .touchUpInside) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - scrollView.flashScrollIndicators() - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - updateScrollViewContentInset() - } - - override func viewSafeAreaInsetsDidChange() { - super.viewSafeAreaInsetsDidChange() - updateScrollViewContentInset() - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - setupNavigationBarAppearance() - configureTitleLabel() - configureMargin() + tableView.flashScrollIndicators() } } extension MastodonServerRulesViewController { - private func configureTitleLabel() { - guard UIDevice.current.userInterfaceIdiom == .pad else { - return - } - - switch traitCollection.horizontalSizeClass { - case .regular: - navigationItem.largeTitleDisplayMode = .always - navigationItem.title = L10n.Scene.ServerRules.title.replacingOccurrences(of: "\n", with: " ") - largeTitleLabel.isHidden = true - default: - navigationItem.leftBarButtonItem = nil - navigationItem.largeTitleDisplayMode = .never - navigationItem.title = nil - largeTitleLabel.isHidden = false - } + + @objc private func backButtonPressed(_ sender: UIButton) { + logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)") + navigationController?.popViewController(animated: true) } - private func configureMargin() { - switch traitCollection.horizontalSizeClass { - case .regular: - let margin = MastodonPickServerViewController.viewEdgeMargin - stackView.layoutMargins = UIEdgeInsets(top: 32, left: margin, bottom: 20, right: margin) - bottomContainerView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin) - default: - stackView.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0) - bottomContainerView.layoutMargins = .zero - } - } -} + @objc private func nextButtonPressed(_ sender: UIButton) { + logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)") -extension MastodonServerRulesViewController { - func updateScrollViewContentInset() { - view.layoutIfNeeded() - scrollView.contentInset.bottom = bottomContainerView.frame.height - scrollView.verticalScrollIndicatorInsets.bottom = bottomContainerView.frame.height + let viewModel = MastodonRegisterViewModel(domain: viewModel.domain, context: context, authenticateInfo: viewModel.authenticateInfo, instance: viewModel.instance, applicationToken: viewModel.applicationToken) + coordinator.present(scene: .mastodonRegister(viewModel: viewModel), from: self, transition: .show) } - func configTextView() { - let metaContent = ServerRulesPromptMetaContent(domain: viewModel.domain) - bottomPromptMetaText.configure(content: metaContent) - bottomPromptMetaText.textView.linkDelegate = self - } - - struct ServerRulesPromptMetaContent: MetaContent { - let string: String - let entities: [Meta.Entity] - - init(domain: String) { - let _string = L10n.Scene.ServerRules.prompt(domain) - self.string = _string - - var _entities: [Meta.Entity] = [] - - let termsOfServiceText = L10n.Scene.ServerRules.termsOfService - if let termsOfServiceRange = _string.range(of: termsOfServiceText) { - let url = Mastodon.API.serverRulesURL(domain: domain) - let entity = Meta.Entity(range: NSRange(termsOfServiceRange, in: _string), meta: .url(termsOfServiceText, trimmed: termsOfServiceText, url: url.absoluteString, userInfo: nil)) - _entities.append(entity) - } - - let privacyPolicyText = L10n.Scene.ServerRules.privacyPolicy - if let privacyPolicyRange = _string.range(of: privacyPolicyText) { - let url = Mastodon.API.privacyURL(domain: domain) - let entity = Meta.Entity(range: NSRange(privacyPolicyRange, in: _string), meta: .url(privacyPolicyText, trimmed: privacyPolicyText, url: url.absoluteString, userInfo: nil)) - _entities.append(entity) - } - - self.entities = _entities - } - - func metaAttachment(for entity: Meta.Entity) -> MetaAttachment? { - return nil - } - } - -} - -extension MastodonServerRulesViewController: UITextViewDelegate { - func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { - return false - } -} - -// MARK: - MetaTextViewDelegate -extension MastodonServerRulesViewController: MetaTextViewDelegate { - func metaTextView(_ metaTextView: MetaTextView, didSelectMeta meta: Meta) { - switch meta { - case .url(_, _, let url, _): - guard let url = URL(string: url) else { return } - coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil)) - default: - break - } - } -} - -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 viewModel = MastodonRegisterViewModel(domain: self.viewModel.domain, context: self.context, authenticateInfo: self.viewModel.authenticateInfo, instance: self.viewModel.instance, applicationToken: self.viewModel.applicationToken) - self.coordinator.present(scene: .mastodonRegister(viewModel: viewModel), from: self, transition: .show) - } } // MARK: - OnboardingViewControllerAppearance extension MastodonServerRulesViewController: OnboardingViewControllerAppearance { } -#if canImport(SwiftUI) && DEBUG -import SwiftUI - -struct ServerRulesViewController_Previews: PreviewProvider { - - static var previews: some View { - UIViewControllerPreview { - let viewController = MastodonServerRulesViewController() - return viewController - } - .previewLayout(.fixed(width: 375, height: 800)) +// MARK: - UITableViewDelegate +extension MastodonServerRulesViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + return UIView() } + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + guard let diffableDataSource = viewModel.diffableDataSource, + section < diffableDataSource.snapshot().numberOfSections + else { return .leastNonzeroMagnitude } + + let sectionItem = diffableDataSource.snapshot().sectionIdentifiers[section] + switch sectionItem { + case .header: + return .leastNonzeroMagnitude + case .rules: + return 16 + } + } } - -#endif diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift new file mode 100644 index 00000000..f6385a52 --- /dev/null +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift @@ -0,0 +1,26 @@ +// +// MastodonServerRulesViewModel+Diffable.swift +// Mastodon +// +// Created by MainasuK on 2022-1-5. +// + +import UIKit + +extension MastodonServerRulesViewModel { + func setupDiffableDataSource( + tableView: UITableView + ) { + diffableDataSource = ServerRuleSection.tableViewDiffableDataSource(tableView: tableView) + + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([.header, .rules]) + snapshot.appendItems([.header(domain: domain)], toSection: .header) + let ruleItems: [ServerRuleItem] = rules.enumerated().map { i, rule in + let ruleContext = ServerRuleItem.RuleContext(index: i, rule: rule) + return ServerRuleItem.rule(ruleContext) + } + snapshot.appendItems(ruleItems, toSection: .rules) + diffableDataSource?.applySnapshot(snapshot, animated: false, completion: nil) + } +} diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift index 5936a2c0..f2664e0e 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel.swift @@ -18,6 +18,9 @@ final class MastodonServerRulesViewModel { let instance: Mastodon.Entity.Instance let applicationToken: Mastodon.Entity.Token + // output + var diffableDataSource: UITableViewDiffableDataSource? + init( domain: String, authenticateInfo: AuthenticationViewModel.AuthenticateInfo, diff --git a/Mastodon/Scene/Onboarding/ServerRules/ServerRulesTableViewCell.swift b/Mastodon/Scene/Onboarding/ServerRules/ServerRulesTableViewCell.swift new file mode 100644 index 00000000..83378b99 --- /dev/null +++ b/Mastodon/Scene/Onboarding/ServerRules/ServerRulesTableViewCell.swift @@ -0,0 +1,83 @@ +// +// ServerRulesTableViewCell.swift +// Mastodon +// +// Created by MainasuK on 2022-1-5. +// + +import UIKit + +final class ServerRulesTableViewCell: UITableViewCell { + + static let margin: CGFloat = 23 + + let indexImageView: UIImageView = { + let imageView = UIImageView() + imageView.tintColor = Asset.Colors.Label.primary.color + return imageView + }() + + let ruleLabel: UILabel = { + let label = UILabel() + label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold)) + label.textColor = Asset.Colors.Label.primary.color + label.numberOfLines = 0 + return label + }() + + let separalerLine: UIView = { + let view = UIView() + view.backgroundColor = Asset.Theme.System.separator.color + return view + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + +} + +extension ServerRulesTableViewCell { + + private func _init() { + selectionStyle = .none + backgroundColor = .clear + + indexImageView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(indexImageView) + NSLayoutConstraint.activate([ + indexImageView.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: ServerRulesTableViewCell.margin), + indexImageView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + contentView.bottomAnchor.constraint(greaterThanOrEqualTo: indexImageView.bottomAnchor, constant: ServerRulesTableViewCell.margin), + indexImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), + indexImageView.widthAnchor.constraint(equalToConstant: 32).priority(.required - 1), + indexImageView.heightAnchor.constraint(equalToConstant: 32).priority(.required - 1), + ]) + + ruleLabel.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(ruleLabel) + NSLayoutConstraint.activate([ + ruleLabel.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: ServerRulesTableViewCell.margin), + ruleLabel.leadingAnchor.constraint(equalTo: indexImageView.trailingAnchor, constant: 16), + ruleLabel.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + contentView.bottomAnchor.constraint(greaterThanOrEqualTo: ruleLabel.bottomAnchor, constant: ServerRulesTableViewCell.margin), + ruleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), + ]) + + separalerLine.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(separalerLine) + NSLayoutConstraint.activate([ + separalerLine.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + separalerLine.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + separalerLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + separalerLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: contentView)).priority(.required - 1), + ]) + } + +} diff --git a/Mastodon/Scene/Onboarding/Share/NavigationActionView.swift b/Mastodon/Scene/Onboarding/Share/NavigationActionView.swift index 4b5bb124..dc30227c 100644 --- a/Mastodon/Scene/Onboarding/Share/NavigationActionView.swift +++ b/Mastodon/Scene/Onboarding/Share/NavigationActionView.swift @@ -6,11 +6,14 @@ // import UIKit +import MastodonUI final class NavigationActionView: UIView { static let buttonHeight: CGFloat = 50 + private var observations = Set() + let buttonContainer: UIStackView = { let stackView = UIStackView() stackView.axis = .horizontal @@ -18,6 +21,7 @@ final class NavigationActionView: UIView { return stackView }() + let backButtonShadowContainer = ShadowBackgroundContainer() let backButton: PrimaryActionButton = { let button = PrimaryActionButton() button.action = .back @@ -25,6 +29,7 @@ final class NavigationActionView: UIView { return button }() + let nextButtonShadowContainer = ShadowBackgroundContainer() let nextButton: PrimaryActionButton = { let button = PrimaryActionButton() button.action = .next @@ -56,14 +61,33 @@ extension NavigationActionView { safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: buttonContainer.bottomAnchor, constant: 8), ]) - backButton.translatesAutoresizingMaskIntoConstraints = false - buttonContainer.addArrangedSubview(backButton) - nextButton.translatesAutoresizingMaskIntoConstraints = false - buttonContainer.addArrangedSubview(nextButton) + backButtonShadowContainer.translatesAutoresizingMaskIntoConstraints = false + buttonContainer.addArrangedSubview(backButtonShadowContainer) + nextButtonShadowContainer.translatesAutoresizingMaskIntoConstraints = false + buttonContainer.addArrangedSubview(nextButtonShadowContainer) NSLayoutConstraint.activate([ - backButton.heightAnchor.constraint(equalToConstant: NavigationActionView.buttonHeight).priority(.required - 1), - nextButton.heightAnchor.constraint(equalToConstant: NavigationActionView.buttonHeight).priority(.required - 1), - nextButton.widthAnchor.constraint(equalTo: backButton.widthAnchor, multiplier: 2).priority(.required - 1), + backButtonShadowContainer.heightAnchor.constraint(equalToConstant: NavigationActionView.buttonHeight).priority(.required - 1), + nextButtonShadowContainer.heightAnchor.constraint(equalToConstant: NavigationActionView.buttonHeight).priority(.required - 1), + nextButtonShadowContainer.widthAnchor.constraint(equalTo: backButtonShadowContainer.widthAnchor, multiplier: 2).priority(.required - 1), + ]) + + backButton.translatesAutoresizingMaskIntoConstraints = false + backButtonShadowContainer.addSubview(backButton) + NSLayoutConstraint.activate([ + backButton.topAnchor.constraint(equalTo: backButtonShadowContainer.topAnchor), + backButton.leadingAnchor.constraint(equalTo: backButtonShadowContainer.leadingAnchor), + backButton.trailingAnchor.constraint(equalTo: backButtonShadowContainer.trailingAnchor), + backButton.bottomAnchor.constraint(equalTo: backButtonShadowContainer.bottomAnchor), + ]) + + nextButton.translatesAutoresizingMaskIntoConstraints = false + nextButtonShadowContainer.addSubview(nextButton) + NSLayoutConstraint.activate([ + nextButton.topAnchor.constraint(equalTo: nextButtonShadowContainer.topAnchor), + nextButton.leadingAnchor.constraint(equalTo: nextButtonShadowContainer.leadingAnchor), + nextButton.trailingAnchor.constraint(equalTo: nextButtonShadowContainer.trailingAnchor), + nextButton.bottomAnchor.constraint(equalTo: nextButtonShadowContainer.bottomAnchor), ]) } + } diff --git a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift b/Mastodon/Scene/Onboarding/Share/OnboardingHeadlineTableViewCell.swift similarity index 79% rename from Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift rename to Mastodon/Scene/Onboarding/Share/OnboardingHeadlineTableViewCell.swift index 161b15d0..f8090734 100644 --- a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift +++ b/Mastodon/Scene/Onboarding/Share/OnboardingHeadlineTableViewCell.swift @@ -1,5 +1,5 @@ // -// PickServerTitleCell.swift +// OnboardingHeadlineTableViewCell.swift // Mastodon // // Created by BradGao on 2021/2/23. @@ -7,12 +7,12 @@ import UIKit -final class PickServerTitleCell: UITableViewCell { +final class OnboardingHeadlineTableViewCell: UITableViewCell { let titleLabel: UILabel = { let label = UILabel() - label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 28, weight: .bold)) - label.textColor = Asset.Colors.Label.primary.color + label.font = MastodonPickServerViewController.largeTitleFont + label.textColor = MastodonPickServerViewController.largeTitleTextColor label.text = L10n.Scene.ServerPicker.title label.adjustsFontForContentSizeCategory = true label.numberOfLines = 0 @@ -21,8 +21,8 @@ final class PickServerTitleCell: UITableViewCell { let subTitleLabel: UILabel = { let label = UILabel() - label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) - label.textColor = Asset.Colors.Label.secondary.color + label.font = MastodonPickServerViewController.subTitleFont + label.textColor = MastodonPickServerViewController.subTitleTextColor label.text = "Pick a community based on your interests, region, or a general purpose one. Each community is operated by an entirely independent organization or individual." label.adjustsFontForContentSizeCategory = true label.numberOfLines = 0 @@ -40,7 +40,7 @@ final class PickServerTitleCell: UITableViewCell { } } -extension PickServerTitleCell { +extension OnboardingHeadlineTableViewCell { private func _init() { selectionStyle = .none diff --git a/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift index c4fae4dd..aef6a8ab 100644 --- a/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift +++ b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift @@ -21,6 +21,22 @@ extension OnboardingViewControllerAppearance { static var viewBottomPaddingHeight: CGFloat { return 11 } static var viewBottomPaddingHeightExtend: CGFloat { return 22 } + static var largeTitleFont: UIFont { + return UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 28, weight: .bold)) + } + + static var largeTitleTextColor: UIColor { + return Asset.Colors.Label.primary.color + } + + static var subTitleFont: UIFont { + return UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) + } + + static var subTitleTextColor: UIColor { + return Asset.Colors.Label.secondary.color + } + func setupOnboardingAppearance() { view.backgroundColor = Asset.Scene.Onboarding.onboardingBackground.color diff --git a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift index c86b059b..1dff6965 100644 --- a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift +++ b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift @@ -56,6 +56,7 @@ final class WelcomeViewController: UIViewController, NeedsDependency { button.setTitleColor(.black, for: .normal) return button }() + let signUpButtonShadowView = UIView() private(set) lazy var signInButton: PrimaryActionButton = { let button = PrimaryActionButton() @@ -72,7 +73,6 @@ final class WelcomeViewController: UIViewController, NeedsDependency { }() let signInButtonShadowView = UIView() - deinit { os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) } @@ -117,6 +117,16 @@ extension WelcomeViewController { signInButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1), ]) + signUpButtonShadowView.translatesAutoresizingMaskIntoConstraints = false + buttonContainer.addSubview(signUpButtonShadowView) + buttonContainer.sendSubviewToBack(signUpButtonShadowView) + NSLayoutConstraint.activate([ + signUpButtonShadowView.topAnchor.constraint(equalTo: signUpButton.topAnchor), + signUpButtonShadowView.leadingAnchor.constraint(equalTo: signUpButton.leadingAnchor), + signUpButtonShadowView.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor), + signUpButtonShadowView.bottomAnchor.constraint(equalTo: signUpButton.bottomAnchor), + ]) + signInButtonShadowView.translatesAutoresizingMaskIntoConstraints = false buttonContainer.addSubview(signInButtonShadowView) buttonContainer.sendSubviewToBack(signInButtonShadowView) @@ -168,6 +178,17 @@ extension WelcomeViewController { extension WelcomeViewController { private func setupButtonShadowView() { + signUpButtonShadowView.layer.setupShadow( + color: .black, + alpha: 0.25, + x: 0, + y: 1, + blur: 2, + spread: 0, + roundedRect: signInButtonShadowView.bounds, + byRoundingCorners: .allCorners, + cornerRadii: CGSize(width: 10, height: 10) + ) signInButtonShadowView.layer.setupShadow( color: .black, alpha: 0.25, diff --git a/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift b/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift index 3bda63de..676d558a 100644 --- a/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift +++ b/Mastodon/Scene/Share/View/Button/PrimaryActionButton.swift @@ -9,8 +9,8 @@ import UIKit class PrimaryActionButton: UIButton { - var isLoading: Bool = false - + private var originalButtonTitle: String? + lazy var activityIndicator: UIActivityIndicatorView = { let indicator = UIActivityIndicatorView(style: .medium) indicator.color = .white @@ -18,16 +18,13 @@ class PrimaryActionButton: UIButton { indicator.translatesAutoresizingMaskIntoConstraints = false return indicator }() - - private var originalButtonTitle: String? - + var adjustsBackgroundImageWhenUserInterfaceStyleChanges = true var action: Action = .next { didSet { setupAppearance(action: action) } } - - var adjustsBackgroundImageWhenUserInterfaceStyleChanges = true + var isLoading: Bool = false override init(frame: CGRect) { super.init(frame: frame) diff --git a/Mastodon/Extension/CALayer.swift b/MastodonSDK/Sources/MastodonExtension/CALayer.swift similarity index 95% rename from Mastodon/Extension/CALayer.swift rename to MastodonSDK/Sources/MastodonExtension/CALayer.swift index 41ce739e..684a4a70 100644 --- a/Mastodon/Extension/CALayer.swift +++ b/MastodonSDK/Sources/MastodonExtension/CALayer.swift @@ -9,7 +9,7 @@ import UIKit extension CALayer { - func setupShadow( + public func setupShadow( color: UIColor = .black, alpha: Float = 0.5, x: CGFloat = 0, @@ -43,9 +43,8 @@ extension CALayer { } } - func removeShadow() { + public func removeShadow() { shadowRadius = 0 } - - + } diff --git a/MastodonSDK/Sources/MastodonExtension/UIImage.swift b/MastodonSDK/Sources/MastodonExtension/UIImage.swift index 178d289d..e3560af6 100644 --- a/MastodonSDK/Sources/MastodonExtension/UIImage.swift +++ b/MastodonSDK/Sources/MastodonExtension/UIImage.swift @@ -10,12 +10,28 @@ import CoreImage.CIFilterBuiltins import UIKit extension UIImage { - public static func placeholder(size: CGSize = CGSize(width: 1, height: 1), color: UIColor) -> UIImage { + public static func placeholder( + size: CGSize = CGSize(width: 1, height: 1), + color: UIColor, + cornerRadius: CGFloat = 0 + ) -> UIImage { let render = UIGraphicsImageRenderer(size: size) return render.image { (context: UIGraphicsImageRendererContext) in + // set clear fill context.cgContext.setFillColor(color.cgColor) - context.fill(CGRect(origin: .zero, size: size)) + + let rect = CGRect(origin: .zero, size: size) + + // clip corner if needs + if cornerRadius > 0 { + let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath + context.cgContext.addPath(path) + context.cgContext.clip(using: .evenOdd) + } + + // set fill + context.fill(rect) } } } diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Instance.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Instance.swift index d0d16ee4..f245d741 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Instance.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Instance.swift @@ -86,7 +86,7 @@ extension Mastodon.Entity.Instance { } extension Mastodon.Entity.Instance { - public struct Rule: Codable { + public struct Rule: Codable, Hashable { public let id: String public let text: String } diff --git a/MastodonSDK/Sources/MastodonUI/View/Container/ShadowBackgroundContainer.swift b/MastodonSDK/Sources/MastodonUI/View/Container/ShadowBackgroundContainer.swift new file mode 100644 index 00000000..6b45a050 --- /dev/null +++ b/MastodonSDK/Sources/MastodonUI/View/Container/ShadowBackgroundContainer.swift @@ -0,0 +1,48 @@ +// +// ShadowBackgroundContainer.swift +// +// +// Created by MainasuK on 2022-1-5. +// + +import UIKit +import MastodonExtension + +public final class ShadowBackgroundContainer: UIView { + + public let shadowLayer = CALayer() + + override init(frame: CGRect) { + super.init(frame: frame) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + +} + +extension ShadowBackgroundContainer { + private func _init() { + layer.insertSublayer(shadowLayer, at: 0) + } + + public override func layoutSubviews() { + super.layoutSubviews() + + shadowLayer.frame = bounds + shadowLayer.setupShadow( + color: .black, + alpha: 0.25, + x: 0, + y: 1, + blur: 2, + spread: 0, + roundedRect: bounds, + byRoundingCorners: .allCorners, + cornerRadii: CGSize(width: 10, height: 10) + ) + } +}