feat: update server rule scene UI

This commit is contained in:
CMK 2022-01-05 15:11:35 +08:00
parent d08cb9ece9
commit a7a36d503a
53 changed files with 652 additions and 315 deletions

View File

@ -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 = "<group>"; };
0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewController.swift; sourceTree = "<group>"; };
0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewModel.swift; sourceTree = "<group>"; };
0FB3D2FD25E4CB6400AAD544 /* PickServerTitleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerTitleCell.swift; sourceTree = "<group>"; };
0FB3D2FD25E4CB6400AAD544 /* OnboardingHeadlineTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingHeadlineTableViewCell.swift; sourceTree = "<group>"; };
0FB3D30725E524C600AAD544 /* PickServerCategoriesCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoriesCell.swift; sourceTree = "<group>"; };
0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoryView.swift; sourceTree = "<group>"; };
0FB3D31D25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoryCollectionViewCell.swift; sourceTree = "<group>"; };
@ -977,6 +980,10 @@
DB0617EC277F02C50030EE79 /* OnboardingNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingNavigationController.swift; sourceTree = "<group>"; };
DB0617EE277F12720030EE79 /* NavigationActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationActionView.swift; sourceTree = "<group>"; };
DB0617F0278413D00030EE79 /* PickServerServerSectionTableHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerServerSectionTableHeaderView.swift; sourceTree = "<group>"; };
DB0617F427855AB90030EE79 /* ServerRuleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerRuleSection.swift; sourceTree = "<group>"; };
DB0617FC27855BFE0030EE79 /* ServerRuleItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerRuleItem.swift; sourceTree = "<group>"; };
DB0617FE27855D6C0030EE79 /* MastodonServerRulesViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonServerRulesViewModel+Diffable.swift"; sourceTree = "<group>"; };
DB0618002785732C0030EE79 /* ServerRulesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerRulesTableViewCell.swift; sourceTree = "<group>"; };
DB084B5625CBC56C00F898ED /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
DB0AC6FB25CD02E600D75117 /* APIService+Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Instance.swift"; sourceTree = "<group>"; };
DB0C946A26A700AB0088FB11 /* MastodonUser+Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonUser+Property.swift"; sourceTree = "<group>"; };
@ -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 = "<group>"; };
DB427DF925BAA00100D1B89D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DB44384E25E8C1FA008912A2 /* CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CALayer.swift; sourceTree = "<group>"; };
DB443CD32694627B00159B29 /* AppearanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceView.swift; sourceTree = "<group>"; };
DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerInputView.swift; sourceTree = "<group>"; };
DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerSection.swift; sourceTree = "<group>"; };
@ -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 = "<group>";
};
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 = "<group>";
};
2D7631A425C1532200929FB9 /* Share */ = {
isa = PBXGroup;
children = (
@ -1930,29 +1928,6 @@
path = TableviewCell;
sourceTree = "<group>";
};
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 = "<group>";
};
2DA504672601ADBA008F4E6C /* Decoration */ = {
isa = PBXGroup;
children = (
@ -2109,6 +2084,60 @@
path = Deprecated;
sourceTree = "<group>";
};
DB0617F627855AF30030EE79 /* Poll */ = {
isa = PBXGroup;
children = (
DB4481C525EE2ADA00BEFB67 /* PollSection.swift */,
DB4481CB25EE2AFE00BEFB67 /* PollItem.swift */,
);
path = Poll;
sourceTree = "<group>";
};
DB0617F727855B010030EE79 /* Notification */ = {
isa = PBXGroup;
children = (
2D35237926256D920031AF25 /* NotificationSection.swift */,
2D7867182625B77500211898 /* NotificationItem.swift */,
);
path = Notification;
sourceTree = "<group>";
};
DB0617F827855B170030EE79 /* User */ = {
isa = PBXGroup;
children = (
DB6B74FB272FF55800C70B6E /* UserSection.swift */,
DB6B74FD272FF59000C70B6E /* UserItem.swift */,
);
path = User;
sourceTree = "<group>";
};
DB0617F927855B460030EE79 /* Profile */ = {
isa = PBXGroup;
children = (
DBA94433265CBB5300C537E1 /* ProfileFieldSection.swift */,
DBA94435265CBB7400C537E1 /* ProfileFieldItem.swift */,
);
path = Profile;
sourceTree = "<group>";
};
DB0617FA27855B660030EE79 /* Settings */ = {
isa = PBXGroup;
children = (
DB6D9F7C26358ED4008423CD /* SettingsSection.swift */,
DB6D9F8326358EEC008423CD /* SettingsItem.swift */,
);
path = Settings;
sourceTree = "<group>";
};
DB0617FB27855B740030EE79 /* Account */ = {
isa = PBXGroup;
children = (
2D4AD8A126316CD200613EFC /* SelectedAccountSection.swift */,
2D4AD8A726316D3500613EFC /* SelectedAccountItem.swift */,
);
path = Account;
sourceTree = "<group>";
};
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 = "<group>";
@ -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 = "<group>";
@ -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 = "<group>";
@ -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 = "<group>";
@ -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 */,
);

View File

@ -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"
}
}
]
},

View File

@ -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

View File

@ -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
}
}

View File

@ -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<ServerRuleSection, ServerRuleItem> {
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
}
}
}
}

View File

@ -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)

View File

@ -14,8 +14,11 @@ import MetaTextKit
final class MastodonServerRulesViewController: UIViewController, NeedsDependency {
var disposeBag = Set<AnyCancellable>()
let logger = Logger(subsystem: "MastodonServerRulesViewController", category: "ViewController")
var disposeBag = Set<AnyCancellable>()
private var observations = Set<NSKeyValueObservation>()
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

View File

@ -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<ServerRuleSection, ServerRuleItem>()
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)
}
}

View File

@ -18,6 +18,9 @@ final class MastodonServerRulesViewModel {
let instance: Mastodon.Entity.Instance
let applicationToken: Mastodon.Entity.Token
// output
var diffableDataSource: UITableViewDiffableDataSource<ServerRuleSection, ServerRuleItem>?
init(
domain: String,
authenticateInfo: AuthenticationViewModel.AuthenticateInfo,

View File

@ -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),
])
}
}

View File

@ -6,11 +6,14 @@
//
import UIKit
import MastodonUI
final class NavigationActionView: UIView {
static let buttonHeight: CGFloat = 50
private var observations = Set<NSKeyValueObservation>()
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),
])
}
}

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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
}
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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)
)
}
}