diff --git a/Localization/StringsConvertor/input/Base.lproj/app.json b/Localization/StringsConvertor/input/Base.lproj/app.json index b57f0f4e4..978e865c2 100644 --- a/Localization/StringsConvertor/input/Base.lproj/app.json +++ b/Localization/StringsConvertor/input/Base.lproj/app.json @@ -715,6 +715,7 @@ "notifications": "Notifications", "support_mastodon": "Support Mastodon", "about_mastodon": "About Mastodon", + "server_details": "Server Details", "logout": "Logout %@" } @@ -725,6 +726,9 @@ "privacy_policy": "Privacy Policy", "clear_media_storage": "Clear Media Storage" }, + "about_instance": { + "message_admin": "Message Admin" + }, "general": { "title": "General", "appearance": { diff --git a/Localization/StringsConvertor/input/en.lproj/app.json b/Localization/StringsConvertor/input/en.lproj/app.json index b57f0f4e4..30630361f 100644 --- a/Localization/StringsConvertor/input/en.lproj/app.json +++ b/Localization/StringsConvertor/input/en.lproj/app.json @@ -715,6 +715,7 @@ "notifications": "Notifications", "support_mastodon": "Support Mastodon", "about_mastodon": "About Mastodon", + "server_details": "Server Details", "logout": "Logout %@" } diff --git a/Localization/app.json b/Localization/app.json index b57f0f4e4..90739402a 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -715,6 +715,7 @@ "notifications": "Notifications", "support_mastodon": "Support Mastodon", "about_mastodon": "About Mastodon", + "server_details": "Server Details", "logout": "Logout %@" } @@ -725,6 +726,16 @@ "privacy_policy": "Privacy Policy", "clear_media_storage": "Clear Media Storage" }, + + "server_details": { + "about": "About", + "rules": "Rules" + "about_instance": { + "title": "Adminstrator" + "message_admin": "Message Admin", + "legal_notice": "A legal notice" + } + }, "general": { "title": "General", "appearance": { diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index adb7dc3c4..63e5fdc37 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ @@ -20,7 +20,6 @@ 0FB3D31E25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D31D25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift */; }; 0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D33725E6401400AAD544 /* PickServerCell.swift */; }; 164F0EBC267D4FE400249499 /* BoopSound.caf in Resources */ = {isa = PBXBuildFile; fileRef = 164F0EBB267D4FE400249499 /* BoopSound.caf */; }; - 18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */; }; 27D701F5292FC2D60031BCBB /* DataSourceFacade+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */; }; 2A1BF99529F7E68400FA1BA5 /* DataSourceFacade+UserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */; }; 2A1FE47C2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */; }; @@ -109,29 +108,31 @@ 5D0393962612D266007FE196 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0393952612D266007FE196 /* WebViewModel.swift */; }; 5DA732CC2629CEF500A92342 /* UIView+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA732CB2629CEF500A92342 /* UIView+Remove.swift */; }; 5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */; }; - 5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */; }; - 5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; }; 6213AF5A28939C8400BCADB6 /* BookmarkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213AF5928939C8400BCADB6 /* BookmarkViewModel.swift */; }; 6213AF5C28939C8A00BCADB6 /* BookmarkViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213AF5B28939C8A00BCADB6 /* BookmarkViewModel+State.swift */; }; 6213AF5E2893A8B200BCADB6 /* DataSourceFacade+Bookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213AF5D2893A8B200BCADB6 /* DataSourceFacade+Bookmark.swift */; }; 62FD27D12893707600B205C5 /* BookmarkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FD27D02893707600B205C5 /* BookmarkViewController.swift */; }; 62FD27D32893707B00B205C5 /* BookmarkViewController+DataSourceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FD27D22893707B00B205C5 /* BookmarkViewController+DataSourceProvider.swift */; }; 62FD27D52893708A00B205C5 /* BookmarkViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FD27D42893708A00B205C5 /* BookmarkViewModel+Diffable.swift */; }; + 71458AF57697DB405CFEC37C /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C2A2448AEEDA65B4CD099FC /* Pods_Mastodon.framework */; }; + 7910197261F9D06EFCDCCDBC /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 89FE8B85A00419CEF4678056 /* Pods_MastodonTests.framework */; }; 855149C8295F1C5F00943D96 /* UIInterfaceOrientationMask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855149C7295F1C5F00943D96 /* UIInterfaceOrientationMask.swift */; }; 855149CA29606D6400943D96 /* PortraitAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855149C929606D6400943D96 /* PortraitAlertController.swift */; }; 85904C02293BC0EB0011C817 /* ImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85904C01293BC0EB0011C817 /* ImageProvider.swift */; }; 85904C04293BC1940011C817 /* URLActivityItemWithMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85904C03293BC1940011C817 /* URLActivityItemWithMetadata.swift */; }; 85BC11B32932414900E191CD /* AltTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BC11B22932414900E191CD /* AltTextViewController.swift */; }; - 87FFDA5D898A5C42ADCB35E7 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */; }; 9E44C7202967AD17004B2A72 /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 9E44C71F2967AD17004B2A72 /* MastodonSDKDynamic */; }; 9E44C7222967AD17004B2A72 /* MastodonSDKDynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 9E44C71F2967AD17004B2A72 /* MastodonSDKDynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; C24C97032922F30500BAE8CB /* RefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24C97022922F30500BAE8CB /* RefreshControl.swift */; }; D807C6C029DE197900A4E17C /* EducationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D807C6BF29DE197900A4E17C /* EducationViewController.swift */; }; D808B94C296ECFDC0031EB1E /* StatusEditHistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808B94B296ECFDC0031EB1E /* StatusEditHistoryViewModel.swift */; }; D808B94E296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808B94D296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift */; }; + D80911082AC4BFDE00EB4D15 /* ServerDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80911072AC4BFDE00EB4D15 /* ServerDetailsViewController.swift */; }; D8099078294BC8A30050219F /* PrivacyTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8099077294BC8A30050219F /* PrivacyTableViewController.swift */; }; D809907A294BC9390050219F /* PrivacyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8099079294BC9390050219F /* PrivacyTableViewCell.swift */; }; D809907C294D25510050219F /* PrivacyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D809907B294D25510050219F /* PrivacyViewModel.swift */; }; + D81439862AD415DE0071A88F /* AboutInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81439852AD415DE0071A88F /* AboutInstance.swift */; }; + D81439882AD450A40071A88F /* AboutInstanceTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81439872AD450A40071A88F /* AboutInstanceTableViewDataSource.swift */; }; D81A22752AB4643200905D71 /* SearchResultsOverviewTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */; }; D81A22782AB4782400905D71 /* SearchResultOverviewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22772AB4782400905D71 /* SearchResultOverviewSection.swift */; }; D81A227B2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A227A2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift */; }; @@ -144,6 +145,8 @@ D8318A882A4468D300C0FB73 /* NotificationSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */; }; D8318A8A2A4468DC00C0FB73 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A892A4468DC00C0FB73 /* AboutViewController.swift */; }; D8363B1629469CE200A74079 /* OnboardingNextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8363B1529469CE200A74079 /* OnboardingNextView.swift */; }; + D852C23C2AC5D02C00309232 /* AboutInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D852C23B2AC5D02C00309232 /* AboutInstanceViewController.swift */; }; + D852C23E2AC5D03300309232 /* InstanceRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D852C23D2AC5D03300309232 /* InstanceRulesViewController.swift */; }; D87BFC8B291D5C6B00FEE264 /* MastodonLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */; }; D87BFC8D291EB81200FEE264 /* MastodonLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8C291EB81200FEE264 /* MastodonLoginViewModel.swift */; }; D87BFC8F291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */; }; @@ -173,6 +176,10 @@ D8F917112A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F917102A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift */; }; D8F917122A4C6B67008A5370 /* GeneralSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A832A4468A800C0FB73 /* GeneralSettingsViewController.swift */; }; D8F917142A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F917132A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift */; }; + D8FAAE3D2AD042E700DC1832 /* AdminTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */; }; + D8FAAE3F2AD0430E00DC1832 /* ContactAdminTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */; }; + D8FAAE412AD0475900DC1832 /* AboutInstanceTableViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE402AD0475900DC1832 /* AboutInstanceTableViewHeader.swift */; }; + D8FAAE432AD047B200DC1832 /* AboutInstanceTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE422AD047B200DC1832 /* AboutInstanceTableFooterView.swift */; }; DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; settings = {ATTRIBUTES = (codegen, ); }; }; DB0009A726AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; }; DB023D26279FFB0A005AC798 /* ShareActivityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB023D25279FFB0A005AC798 /* ShareActivityProvider.swift */; }; @@ -489,6 +496,7 @@ DBFEEC99279BDCDE004F81DD /* ProfileAboutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC98279BDCDE004F81DD /* ProfileAboutViewModel.swift */; }; DBFEEC9B279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC9A279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift */; }; DBFEEC9D279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC9C279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift */; }; + EF7771EAA493A869D65A105C /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD12474E169668871A9F6AB4 /* Pods_Mastodon_MastodonUITests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -714,7 +722,6 @@ 2D8434FA25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineNavigationBarTitleView.swift; sourceTree = ""; }; 2D84350425FF858100EECE90 /* UIScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = ""; }; 2D939AB425EDD8A90076FA61 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; - 2DA7D05025CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = ""; }; 2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewController.swift; sourceTree = ""; }; 2DAC9E3D262FC2400062E1A6 /* SuggestionAccountViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewModel.swift; sourceTree = ""; }; 2DAC9E45262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountTableViewCell.swift; sourceTree = ""; }; @@ -722,9 +729,7 @@ 2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendAccountSection.swift; sourceTree = ""; }; 2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.debug.xcconfig"; sourceTree = ""; }; 3B7FD8F28DDA8FBCE5562B78 /* Pods-NotificationService.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.asdk - debug.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.asdk - debug.xcconfig"; sourceTree = ""; }; - 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3E08A432F40BA7B9CAA9DB68 /* Pods-AppShared.release snapshot.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.release snapshot.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.release snapshot.xcconfig"; sourceTree = ""; }; - 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.debug.xcconfig"; sourceTree = ""; }; 46DAB0EBDDFB678347CD96FF /* Pods-MastodonTests.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.asdk - release.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.asdk - release.xcconfig"; sourceTree = ""; }; 5B24BBD7262DB14800A9381B /* ReportViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportViewModel.swift; sourceTree = ""; }; @@ -756,12 +761,13 @@ 85904C03293BC1940011C817 /* URLActivityItemWithMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLActivityItemWithMetadata.swift; sourceTree = ""; }; 85BC11B22932414900E191CD /* AltTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AltTextViewController.swift; sourceTree = ""; }; 8850E70A1D5FF51432E43653 /* Pods-Mastodon-MastodonUITests.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.asdk - release.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.asdk - release.xcconfig"; sourceTree = ""; }; + 89FE8B85A00419CEF4678056 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8C2A2448AEEDA65B4CD099FC /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8E79CCBE51FBC3F7FE8CF49F /* Pods-MastodonTests.release snapshot.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.release snapshot.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.release snapshot.xcconfig"; sourceTree = ""; }; 8ED8C4B1F1BA2DCFF2926BB1 /* Pods-Mastodon-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-NotificationService/Pods-Mastodon-NotificationService.debug.xcconfig"; sourceTree = ""; }; 9780A4C98FFC65B32B50D1C0 /* Pods-MastodonTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.release.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.release.xcconfig"; sourceTree = ""; }; 9A0982D8F349244EB558CDFD /* Pods-AppShared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.debug.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.debug.xcconfig"; sourceTree = ""; }; 9CFF58FD900AC059428700E7 /* Pods-NotificationService.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.asdk - release.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.asdk - release.xcconfig"; sourceTree = ""; }; - A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A67FD038ECDA0E411AF8DB4D /* Pods-Mastodon-MastodonUITests.asdk.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.asdk.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.asdk.xcconfig"; sourceTree = ""; }; A9B1FB898DFD6063B044298C /* Pods-AppShared.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.asdk - debug.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.asdk - debug.xcconfig"; sourceTree = ""; }; B31D44635FCF6452F7E1B865 /* Pods-Mastodon-AppShared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-AppShared.release.xcconfig"; path = "Target Support Files/Pods-Mastodon-AppShared/Pods-Mastodon-AppShared.release.xcconfig"; sourceTree = ""; }; @@ -770,14 +776,16 @@ BD7598A87F4497045EDEF252 /* Pods-Mastodon.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.asdk - release.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.asdk - release.xcconfig"; sourceTree = ""; }; C24C97022922F30500BAE8CB /* RefreshControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshControl.swift; sourceTree = ""; }; C3789232A52F43529CA67E95 /* Pods-MastodonIntent.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonIntent.asdk - debug.xcconfig"; path = "Target Support Files/Pods-MastodonIntent/Pods-MastodonIntent.asdk - debug.xcconfig"; sourceTree = ""; }; - CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D7D7CF93E262178800077512 /* Pods-Mastodon-AppShared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-AppShared.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-AppShared/Pods-Mastodon-AppShared.debug.xcconfig"; sourceTree = ""; }; D807C6BF29DE197900A4E17C /* EducationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EducationViewController.swift; sourceTree = ""; }; D808B94B296ECFDC0031EB1E /* StatusEditHistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusEditHistoryViewModel.swift; sourceTree = ""; }; D808B94D296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusEditHistoryTableViewCell.swift; sourceTree = ""; }; + D80911072AC4BFDE00EB4D15 /* ServerDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetailsViewController.swift; sourceTree = ""; }; D8099077294BC8A30050219F /* PrivacyTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyTableViewController.swift; sourceTree = ""; }; D8099079294BC9390050219F /* PrivacyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyTableViewCell.swift; sourceTree = ""; }; D809907B294D25510050219F /* PrivacyViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyViewModel.swift; sourceTree = ""; }; + D81439852AD415DE0071A88F /* AboutInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstance.swift; sourceTree = ""; }; + D81439872AD450A40071A88F /* AboutInstanceTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceTableViewDataSource.swift; sourceTree = ""; }; D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsOverviewTableViewController.swift; sourceTree = ""; }; D81A22772AB4782400905D71 /* SearchResultOverviewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultOverviewSection.swift; sourceTree = ""; }; D81A227A2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultDefaultSectionTableViewCell.swift; sourceTree = ""; }; @@ -787,7 +795,6 @@ D82463532A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/WidgetExtension.strings; sourceTree = ""; }; D82463542A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/InfoPlist.strings; sourceTree = ""; }; D82463552A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = be; path = be.lproj/Intents.stringsdict; sourceTree = ""; }; - D82BD7512ABC42D6009A374A /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; D82BD7542ABC73AF009A374A /* NotificationPolicyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPolicyTableViewCell.swift; sourceTree = ""; }; D8318A7F2A4466D300C0FB73 /* SettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCoordinator.swift; sourceTree = ""; }; D8318A832A4468A800C0FB73 /* GeneralSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsViewController.swift; sourceTree = ""; }; @@ -795,6 +802,8 @@ D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsViewController.swift; sourceTree = ""; }; D8318A892A4468DC00C0FB73 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; D8363B1529469CE200A74079 /* OnboardingNextView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = OnboardingNextView.swift; sourceTree = ""; tabWidth = 4; }; + D852C23B2AC5D02C00309232 /* AboutInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceViewController.swift; sourceTree = ""; }; + D852C23D2AC5D03300309232 /* InstanceRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceRulesViewController.swift; sourceTree = ""; }; D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginView.swift; sourceTree = ""; }; D87BFC8C291EB81200FEE264 /* MastodonLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginViewModel.swift; sourceTree = ""; }; D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginServerTableViewCell.swift; sourceTree = ""; }; @@ -834,6 +843,10 @@ D8F9170E2A4B47EF008A5370 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; D8F917102A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingToggleTableViewCell.swift; sourceTree = ""; }; D8F917132A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsDiffableTableViewDataSource.swift; sourceTree = ""; }; + D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminTableViewCell.swift; sourceTree = ""; }; + D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactAdminTableViewCell.swift; sourceTree = ""; }; + D8FAAE402AD0475900DC1832 /* AboutInstanceTableViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceTableViewHeader.swift; sourceTree = ""; }; + D8FAAE422AD047B200DC1832 /* AboutInstanceTableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceTableFooterView.swift; sourceTree = ""; }; DB0009A826AEE5DC009B9D2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = ""; }; DB0009AD26AEE5E4009B9D2D /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Intents.strings; sourceTree = ""; }; DB023D25279FFB0A005AC798 /* ShareActivityProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareActivityProvider.swift; sourceTree = ""; }; @@ -897,7 +910,6 @@ DB1FD44325F26CCC004CFCFC /* PickServerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerSection.swift; sourceTree = ""; }; DB1FD44F25F26FA1004CFCFC /* MastodonPickServerViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonPickServerViewModel+Diffable.swift"; sourceTree = ""; }; DB1FD45925F27898004CFCFC /* CategoryPickerItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryPickerItem.swift; sourceTree = ""; }; - DB1FD45F25F278AF004CFCFC /* CategoryPickerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryPickerSection.swift; sourceTree = ""; }; DB2B3ABD25E37E15007045F9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = ""; }; DB336F3E278E668C0031E64B /* StatusTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusTableViewCell+ViewModel.swift"; sourceTree = ""; }; @@ -1239,6 +1251,7 @@ DBFEEC9A279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileAboutViewModel+Diffable.swift"; sourceTree = ""; }; DBFEEC9C279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldEditCollectionViewCell.swift; sourceTree = ""; }; DBFEF06726A58D07006D7ED1 /* ShareActionExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareActionExtension.entitlements; sourceTree = ""; }; + DD12474E169668871A9F6AB4 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DDB1B139FA8EA26F510D58B6 /* Pods-AppShared.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.asdk - release.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.asdk - release.xcconfig"; sourceTree = ""; }; DF65937EC1FF64462BC002EE /* Pods-MastodonTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.profile.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.profile.xcconfig"; sourceTree = ""; }; E5C7236E58D14A0322FE00F2 /* Pods-Mastodon-MastodonUITests.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.asdk - debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.asdk - debug.xcconfig"; sourceTree = ""; }; @@ -1248,7 +1261,6 @@ ECA373ABA86BE3C2D7ED878E /* Pods-AppShared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.release.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.release.xcconfig"; sourceTree = ""; }; EE13214BC0246BE5210CCC10 /* Pods-AppShared.asdk.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.asdk.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.asdk.xcconfig"; sourceTree = ""; }; F31E7502A7E3945B98C6CBAF /* Pods-NotificationService.asdk.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.asdk.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.asdk.xcconfig"; sourceTree = ""; }; - F4A2A2D7000E477CA459ADA9 /* Pods_AppShared.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppShared.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F920AD4EC23B0D00F5CCA58E /* Pods-MastodonIntent.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonIntent.asdk - release.xcconfig"; path = "Target Support Files/Pods-MastodonIntent/Pods-MastodonIntent.asdk - release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1279,7 +1291,7 @@ files = ( 357FEEAF29523D470021C9DC /* MastodonSDKDynamic in Frameworks */, DBF96326262EC0A6001D8D25 /* AuthenticationServices.framework in Frameworks */, - 87FFDA5D898A5C42ADCB35E7 /* Pods_Mastodon.framework in Frameworks */, + 71458AF57697DB405CFEC37C /* Pods_Mastodon.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1288,8 +1300,7 @@ buildActionMask = 2147483647; files = ( 9E44C7202967AD17004B2A72 /* MastodonSDKDynamic in Frameworks */, - 5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */, - 5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */, + 7910197261F9D06EFCDCCDBC /* Pods_MastodonTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1297,7 +1308,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */, + EF7771EAA493A869D65A105C /* Pods_Mastodon_MastodonUITests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1722,15 +1733,14 @@ isa = PBXGroup; children = ( DBF96325262EC0A6001D8D25 /* AuthenticationServices.framework */, - A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */, - 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */, - 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */, - F4A2A2D7000E477CA459ADA9 /* Pods_AppShared.framework */, DB8FAB9E26AEC3A2008E5AF4 /* Intents.framework */, DB8FABA926AEC3A2008E5AF4 /* IntentsUI.framework */, 2A6451022964223800CD8B8A /* UniformTypeIdentifiers.framework */, 2A728121297EA9D7004138C5 /* WidgetKit.framework */, 2A728123297EA9D7004138C5 /* SwiftUI.framework */, + 8C2A2448AEEDA65B4CD099FC /* Pods_Mastodon.framework */, + DD12474E169668871A9F6AB4 /* Pods_Mastodon_MastodonUITests.framework */, + 89FE8B85A00419CEF4678056 /* Pods_MastodonTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -1756,6 +1766,7 @@ D8F916FF2A4AD898008A5370 /* Settings Overview */, D8F917042A4B0657008A5370 /* General Settings */, D81D12432A4E181C005009D4 /* Notification Settings */, + D80911062AC4BFD100EB4D15 /* Server Details */, D8F917092A4B2AFF008A5370 /* About Mastodon */, D8318A7F2A4466D300C0FB73 /* SettingsCoordinator.swift */, ); @@ -1783,6 +1794,17 @@ path = Bookmark; sourceTree = ""; }; + D80911062AC4BFD100EB4D15 /* Server Details */ = { + isa = PBXGroup; + children = ( + D8FAAE3B2AD042CD00DC1832 /* Table View Components */, + D80911072AC4BFDE00EB4D15 /* ServerDetailsViewController.swift */, + D852C23B2AC5D02C00309232 /* AboutInstanceViewController.swift */, + D852C23D2AC5D03300309232 /* InstanceRulesViewController.swift */, + ); + path = "Server Details"; + sourceTree = ""; + }; D8099076294BC2BA0050219F /* Privacy */ = { isa = PBXGroup; children = ( @@ -1929,6 +1951,19 @@ path = "About Mastodon"; sourceTree = ""; }; + D8FAAE3B2AD042CD00DC1832 /* Table View Components */ = { + isa = PBXGroup; + children = ( + D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */, + D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */, + D8FAAE402AD0475900DC1832 /* AboutInstanceTableViewHeader.swift */, + D8FAAE422AD047B200DC1832 /* AboutInstanceTableFooterView.swift */, + D81439852AD415DE0071A88F /* AboutInstance.swift */, + D81439872AD450A40071A88F /* AboutInstanceTableViewDataSource.swift */, + ); + path = "Table View Components"; + sourceTree = ""; + }; DB01409B25C40BB600F9F3CF /* Onboarding */ = { isa = PBXGroup; children = ( @@ -2110,7 +2145,6 @@ DB427DD325BAA00100D1B89D /* Products */, 1EBA4F56E920856A3FC84ACB /* Pods */, 3FE14AD363ED19AE7FF210A6 /* Frameworks */, - DB98335F25C93B0400AD9700 /* Recovered References */, D8A6FE6029325F5900666A47 /* Localization */, ); indentWidth = 4; @@ -2638,17 +2672,6 @@ path = Thread; sourceTree = ""; }; - DB98335F25C93B0400AD9700 /* Recovered References */ = { - isa = PBXGroup; - children = ( - CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */, - 2DA7D05025CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift */, - DB1FD45F25F278AF004CFCFC /* CategoryPickerSection.swift */, - D82BD7512ABC42D6009A374A /* Coordinator.swift */, - ); - name = "Recovered References"; - sourceTree = ""; - }; DB98EB4A27B0F0F50082E365 /* Cell */ = { isa = PBXGroup; children = ( @@ -3269,7 +3292,7 @@ }; }; buildConfigurationList = DB427DCD25BAA00100D1B89D /* Build configuration list for PBXProject "Mastodon" */; - compatibilityVersion = "Xcode 9.3"; + compatibilityVersion = "Xcode 15.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -3615,6 +3638,7 @@ DB023D26279FFB0A005AC798 /* ShareActivityProvider.swift in Sources */, 5D0393962612D266007FE196 /* WebViewModel.swift in Sources */, 5B24BBDA262DB14800A9381B /* ReportViewModel.swift in Sources */, + D80911082AC4BFDE00EB4D15 /* ServerDetailsViewController.swift in Sources */, 2D5A3D3825CF8D9F002347D6 /* ScrollViewContainer.swift in Sources */, DB6180EF26391CA50018D199 /* MediaPreviewImageViewController.swift in Sources */, DB1E347825F519300079D7DF /* PickServerItem.swift in Sources */, @@ -3642,11 +3666,14 @@ DB697DD6278F4C29004EF2F7 /* DataSourceProvider.swift in Sources */, DB0FCB8E2796C0B7006C02E2 /* TrendCollectionViewCell.swift in Sources */, 0F1E2D0B2615C39400C38565 /* DoubleTitleLabelNavigationBarTitleView.swift in Sources */, + D81439862AD415DE0071A88F /* AboutInstance.swift in Sources */, DBDFF1902805543100557A48 /* DiscoveryPostsViewController.swift in Sources */, DB697DD9278F4CED004EF2F7 /* HomeTimelineViewController+DataSourceProvider.swift in Sources */, DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */, + D8FAAE432AD047B200DC1832 /* AboutInstanceTableFooterView.swift in Sources */, D808B94E296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift in Sources */, 2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */, + D852C23E2AC5D03300309232 /* InstanceRulesViewController.swift in Sources */, DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */, DB6180F226391CF40018D199 /* MediaPreviewImageViewModel.swift in Sources */, 62FD27D12893707600B205C5 /* BookmarkViewController.swift in Sources */, @@ -3665,6 +3692,7 @@ DBDFF19C28055BD600557A48 /* DiscoveryViewModel.swift in Sources */, DBB3BA2A26A81C020004F2D4 /* FLAnimatedImageView.swift in Sources */, DB3E6FF32806D97400B035AE /* DiscoveryNewsViewModel+State.swift in Sources */, + D8FAAE3F2AD0430E00DC1832 /* ContactAdminTableViewCell.swift in Sources */, DB6746ED278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift in Sources */, DB0618032785A7100030EE79 /* RegisterSection.swift in Sources */, DB63F76B279A5ED300455B82 /* NotificationTimelineViewModel+LoadOldestState.swift in Sources */, @@ -3693,6 +3721,7 @@ DBFEEC9D279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift in Sources */, DB3E6FEC2806D7F100B035AE /* DiscoveryNewsViewController.swift in Sources */, DBCBED1726132DB500B49291 /* UserTimelineViewModel+Diffable.swift in Sources */, + D8FAAE412AD0475900DC1832 /* AboutInstanceTableViewHeader.swift in Sources */, 2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */, 2DAC9E3E262FC2400062E1A6 /* SuggestionAccountViewModel.swift in Sources */, DB603113279EBEBA00A935FE /* DataSourceFacade+Block.swift in Sources */, @@ -3856,6 +3885,7 @@ DB68A04A25E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift in Sources */, 0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */, 6213AF5C28939C8A00BCADB6 /* BookmarkViewModel+State.swift in Sources */, + D81439882AD450A40071A88F /* AboutInstanceTableViewDataSource.swift in Sources */, D807C6C029DE197900A4E17C /* EducationViewController.swift in Sources */, 2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */, DBEFCD7B282A162400C0ABEA /* ReportReasonView.swift in Sources */, @@ -3923,6 +3953,7 @@ D8B5E4F42A4ED0240008970C /* NotificationSettingsViewModel.swift in Sources */, DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */, DB4F0966269ED52200D62E92 /* SearchResultViewModel.swift in Sources */, + D852C23C2AC5D02C00309232 /* AboutInstanceViewController.swift in Sources */, D8F917142A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift in Sources */, DB6180FA26391F2E0018D199 /* MediaPreviewViewModel.swift in Sources */, 2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */, @@ -3938,6 +3969,7 @@ DB3E6FE42806A5B800B035AE /* DiscoverySection.swift in Sources */, DB697DDB278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift in Sources */, DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */, + D8FAAE3D2AD042E700DC1832 /* AdminTableViewCell.swift in Sources */, DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */, D8A6AB6C291C5136003AB663 /* MastodonLoginViewController.swift in Sources */, DB0FCB8027968F70006C02E2 /* MastodonStatusThreadViewModel.swift in Sources */, diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index c6e38eff3..6e737d55c 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -46,6 +46,15 @@ "version": "3.1.3" } }, + { + "package": "FXPageControl", + "repositoryURL": "https://github.com/nicklockwood/FXPageControl.git", + "state": { + "branch": null, + "revision": "a94633402ba98c52f86c2a70e61ff086dec9de78", + "version": "1.6.0" + } + }, { "package": "KeychainAccess", "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess.git", @@ -91,15 +100,6 @@ "version": "10.11.2" } }, - { - "package": "NukeFLAnimatedImagePlugin", - "repositoryURL": "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git", - "state": { - "branch": null, - "revision": "b59c346a7d536336db3b0f12c72c6e53ee709e16", - "version": "8.0.0" - } - }, { "package": "Pageboy", "repositoryURL": "https://github.com/uias/Pageboy", @@ -216,6 +216,15 @@ "revision": "20f513ded04a040cdf5467f0891849b1763ede3b", "version": "1.4.1" } + }, + { + "package": "XLPagerTabStrip", + "repositoryURL": "https://github.com/xmartlabs/XLPagerTabStrip.git", + "state": { + "branch": null, + "revision": "211ed62aa376722cf93c429802a8b6ff66a8bd52", + "version": "9.1.0" + } } ] }, diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 83e72ae63..6b12506d3 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -546,7 +546,9 @@ private extension SceneCoordinator { accountName: accountName, setting: setting, appContext: appContext, - authContext: authContext) + authContext: authContext, + sceneCoordinator: self + ) settingsCoordinator.delegate = self settingsCoordinator.start() @@ -648,7 +650,5 @@ extension SceneCoordinator: SettingsCoordinatorDelegate { authenticationController.authenticationSession?.start() self.mastodonAuthenticationController = authenticationController - - } } diff --git a/Mastodon/Scene/Onboarding/ServerRules/Cell/ServerRulesTableViewCell.swift b/Mastodon/Scene/Onboarding/ServerRules/Cell/ServerRulesTableViewCell.swift index 7eeec22e5..6bf513878 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/Cell/ServerRulesTableViewCell.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/Cell/ServerRulesTableViewCell.swift @@ -10,7 +10,7 @@ import MastodonAsset import MastodonLocalization final class ServerRulesTableViewCell: UITableViewCell { - + static let reuseIdentifier = "ServerRulesTableViewCell" static let margin: CGFloat = 23 let indexImageView: UIImageView = { diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift index 1f8d55c76..bbae5c324 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift @@ -15,10 +15,7 @@ extension MastodonServerRulesViewModel { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.rules]) - let ruleItems: [ServerRuleItem] = rules.enumerated().map { i, rule in - let ruleContext = ServerRuleItem.RuleContext(index: i, rule: rule) - return ServerRuleItem.rule(ruleContext) - } + let ruleItems: [ServerRuleItem] = rules.enumerated().map { index, rule in return ServerRuleItem.rule(index: index, rule: rule) } snapshot.appendItems(ruleItems, toSection: .rules) diffableDataSource?.apply(snapshot, animatingDifferences: false) } diff --git a/Mastodon/Scene/Onboarding/ServerRules/ServerRuleItem.swift b/Mastodon/Scene/Onboarding/ServerRules/ServerRuleItem.swift index f82b1e5e4..dd6695386 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/ServerRuleItem.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/ServerRuleItem.swift @@ -9,12 +9,5 @@ import Foundation import MastodonSDK enum ServerRuleItem: Hashable { - case rule(RuleContext) -} - -extension ServerRuleItem { - struct RuleContext: Hashable { - let index: Int - let rule: Mastodon.Entity.Instance.Rule - } + case rule(index: Int, rule: Mastodon.Entity.Instance.Rule) } diff --git a/Mastodon/Scene/Onboarding/ServerRules/ServerRuleSection.swift b/Mastodon/Scene/Onboarding/ServerRules/ServerRuleSection.swift index ca4cd9c13..0f121baf5 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/ServerRuleSection.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/ServerRuleSection.swift @@ -19,11 +19,11 @@ extension ServerRuleSection { ) -> UITableViewDiffableDataSource { return UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item in switch item { - case .rule(let ruleContext): + case .rule(let index, let rule): let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ServerRulesTableViewCell.self), for: indexPath) as! ServerRulesTableViewCell - cell.indexImageView.image = UIImage(systemName: "\(ruleContext.index + 1).circle") ?? UIImage(systemName: "questionmark.circle") + cell.indexImageView.image = UIImage(systemName: "\(index + 1).circle") ?? UIImage(systemName: "questionmark.circle") cell.indexImageView.tintColor = Asset.Colors.Brand.lightBlurple.color - cell.ruleLabel.text = ruleContext.rule.text + cell.ruleLabel.text = rule.text return cell } } diff --git a/Mastodon/Scene/Settings/About Mastodon/AboutSettings.swift b/Mastodon/Scene/Settings/About Mastodon/AboutSettings.swift index fcb952f30..3c6ca64bc 100644 --- a/Mastodon/Scene/Settings/About Mastodon/AboutSettings.swift +++ b/Mastodon/Scene/Settings/About Mastodon/AboutSettings.swift @@ -23,7 +23,7 @@ enum AboutSettingsEntry: Hashable { case .privacyPolicy: return L10n.Scene.Settings.AboutMastodon.privacyPolicy case .clearMediaCache(_): - return L10n.Scene.Settings.AboutMastodon.cleaerMediaStorage + return L10n.Scene.Settings.AboutMastodon.clearMediaStorage } } diff --git a/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift b/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift new file mode 100644 index 000000000..59c1d5b01 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift @@ -0,0 +1,155 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonSDK + +protocol AboutInstanceViewControllerDelegate: AnyObject { + func showAdminAccount(_ viewController: AboutInstanceViewController, account: Mastodon.Entity.Account) + func sendEmailToAdmin(_ viewController: AboutInstanceViewController, emailAddress: String) +} + +class AboutInstanceViewController: UIViewController { + + weak var delegate: AboutInstanceViewControllerDelegate? + var dataSource: AboutInstanceTableViewDataSource? + + let tableView: UITableView + let headerView: AboutInstanceTableHeaderView + let footerView: AboutInstanceTableFooterView + + var instance: Mastodon.Entity.V2.Instance? + + init() { + tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.register(ContactAdminTableViewCell.self, forCellReuseIdentifier: ContactAdminTableViewCell.reuseIdentifier) + tableView.register(AdminTableViewCell.self, forCellReuseIdentifier: AdminTableViewCell.reuseIdentifier) + + headerView = AboutInstanceTableHeaderView() + footerView = AboutInstanceTableFooterView() + + super.init(nibName: nil, bundle: nil) + + let dataSource = AboutInstanceTableViewDataSource(tableView: tableView) { tableView, indexPath, itemIdentifier in + switch itemIdentifier { + + case .adminAccount(let account): + guard let cell = tableView.dequeueReusableCell(withIdentifier: AdminTableViewCell.reuseIdentifier, for: indexPath) as? AdminTableViewCell else { fatalError("WTF?! Wrong cell.") } + + cell.condensedUserView.configure(with: account, showFollowers: false) + + return cell + + case .contactAdmin: + guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactAdminTableViewCell.reuseIdentifier, for: indexPath) as? ContactAdminTableViewCell else { fatalError("WTF?! Wrong cell.") } + + cell.configure() + + return cell + } + } + + tableView.delegate = self + tableView.dataSource = dataSource + + self.dataSource = dataSource + + view.addSubview(tableView) + + setupConstraints() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + let constraints = [ + tableView.topAnchor.constraint(equalTo: view.topAnchor), + tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view.trailingAnchor.constraint(equalTo: tableView.trailingAnchor), + view.bottomAnchor.constraint(equalTo: tableView.bottomAnchor), + ] + + NSLayoutConstraint.activate(constraints) + } + + override func viewDidLoad() { + super.viewDidLoad() + + tableView.tableHeaderView = headerView + tableView.tableFooterView = footerView + } + + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + + if let tableHeaderView = tableView.tableHeaderView { + let size = tableHeaderView.systemLayoutSizeFitting(.init(width: self.tableView.frame.width, height: 10_000)) + tableHeaderView.frame.size = size + tableView.tableHeaderView = tableHeaderView + } + + if let tableFooterView = tableView.tableFooterView { + let size = tableFooterView.systemLayoutSizeFitting(.init(width: self.tableView.frame.width, height: 10_000)) + tableFooterView.frame.size = size + tableView.tableFooterView = tableFooterView + } + + super.viewWillLayoutSubviews() + } + + func update(with instance: Mastodon.Entity.V2.Instance) { + + self.instance = instance + var snapshot = NSDiffableDataSourceSnapshot() + + snapshot.appendSections([.main]) + if let account = instance.contact?.account { + snapshot.appendItems([.adminAccount(account)], toSection: .main) + } + + if let email = instance.contact?.email { + snapshot.appendItems([.contactAdmin(email)], toSection: .main) + } + + dataSource?.apply(snapshot, animatingDifferences: false) + + guard let thumbnailUrlString = instance.thumbnail?.url, let thumbnailUrl = URL(string: thumbnailUrlString) else { return } + + DispatchQueue.main.async { + self.headerView.updateImage(with: thumbnailUrl) { [weak self] in + DispatchQueue.main.async { + guard let self else { return } + + self.view.setNeedsLayout() + self.view.layoutIfNeeded() + } + } + } + } + + func updateFooter(with extendedDescription: Mastodon.Entity.ExtendedDescription) { + DispatchQueue.main.async { + self.footerView.update(with: extendedDescription) + + self.view.setNeedsLayout() + self.view.layoutIfNeeded() + } + } +} + +extension AboutInstanceViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let snapshot = dataSource?.snapshot() else { + return tableView.deselectRow(at: indexPath, animated: true) + } + + switch snapshot.itemIdentifiers(inSection: .main)[indexPath.row] { + case .adminAccount(let account): + delegate?.showAdminAccount(self, account: account) + case .contactAdmin(let email): + delegate?.sendEmailToAdmin(self, emailAddress: email) + } + + tableView.deselectRow(at: indexPath, animated: true) + } +} diff --git a/Mastodon/Scene/Settings/Server Details/InstanceRulesViewController.swift b/Mastodon/Scene/Settings/Server Details/InstanceRulesViewController.swift new file mode 100644 index 000000000..b4d934214 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/InstanceRulesViewController.swift @@ -0,0 +1,58 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonSDK + +protocol InstanceRulesViewControllerDelegate: AnyObject { + +} + +class InstanceRulesViewController: UIViewController { + + weak var delegate: InstanceRulesViewControllerDelegate? + let tableView: UITableView + var dataSource: UITableViewDiffableDataSource? + + var sections: [ServerRuleSection] = [] + + init() { + tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.register(ServerRulesTableViewCell.self, forCellReuseIdentifier: ServerRulesTableViewCell.reuseIdentifier) + + super.init(nibName: nil, bundle: nil) + view.addSubview(tableView) + + let dataSource = ServerRuleSection.tableViewDiffableDataSource(tableView: tableView) + + tableView.dataSource = dataSource + self.dataSource = dataSource + + setupConstraints() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + let constraints = [ + tableView.topAnchor.constraint(equalTo: view.topAnchor), + tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view.trailingAnchor.constraint(equalTo: tableView.trailingAnchor), + view.bottomAnchor.constraint(equalTo: tableView.bottomAnchor), + ] + + NSLayoutConstraint.activate(constraints) + } + + func update(with instance: Mastodon.Entity.V2.Instance) { + guard let dataSource, let rules = instance.rules, rules.isNotEmpty else { return } + + var snapshot = NSDiffableDataSourceSnapshot() + + snapshot.appendSections([.rules]) + let ruleItems = rules.enumerated().compactMap { index, rule in ServerRuleItem.rule(index: index, rule: rule) } + snapshot.appendItems(ruleItems, toSection: .rules) + + dataSource.apply(snapshot) + } +} diff --git a/Mastodon/Scene/Settings/Server Details/ServerDetailsViewController.swift b/Mastodon/Scene/Settings/Server Details/ServerDetailsViewController.swift new file mode 100644 index 000000000..fb724f8f9 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/ServerDetailsViewController.swift @@ -0,0 +1,165 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonSDK +import MastodonLocalization +import MetaTextKit + +enum ServerDetailsTab: Int, CaseIterable { + case about = 0 + case rules = 1 + + var title: String { + switch self { + case .about: return L10n.Scene.Settings.ServerDetails.about + case .rules: return L10n.Scene.Settings.ServerDetails.rules + } + } +} + +protocol ServerDetailsViewControllerDelegate: AnyObject {} + +class ServerDetailsViewController: UIViewController { + + weak var delegate: (ServerDetailsViewControllerDelegate & AboutInstanceViewControllerDelegate & InstanceRulesViewControllerDelegate & MetaLabelDelegate)? { + didSet { + aboutInstanceViewController.delegate = delegate + instanceRulesViewController.delegate = delegate + aboutInstanceViewController.footerView.contentLabel.linkDelegate = delegate + } + } + let pageController: UIPageViewController + + private let segmentedControlWrapper: UIView + let segmentedControl: UISegmentedControl + let aboutInstanceViewController: AboutInstanceViewController + let instanceRulesViewController: InstanceRulesViewController + let containerView: UIView + + init(domain: String) { + segmentedControl = UISegmentedControl() + segmentedControl.translatesAutoresizingMaskIntoConstraints = false + + segmentedControlWrapper = UIView() + segmentedControlWrapper.translatesAutoresizingMaskIntoConstraints = false + segmentedControlWrapper.addSubview(segmentedControl) + + containerView = UIView() + containerView.translatesAutoresizingMaskIntoConstraints = false + + aboutInstanceViewController = AboutInstanceViewController() + instanceRulesViewController = InstanceRulesViewController() + + pageController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) + pageController.setViewControllers([aboutInstanceViewController], direction: .forward, animated: false) + pageController.view.translatesAutoresizingMaskIntoConstraints = false + + super.init(nibName: nil, bundle: nil) + + view.addSubview(segmentedControlWrapper) + view.addSubview(containerView) + view.backgroundColor = .systemGroupedBackground + + containerView.addSubview(pageController.view) + addChild(pageController) + pageController.didMove(toParent: self) + pageController.delegate = self + pageController.dataSource = self + + ServerDetailsTab.allCases.forEach { + segmentedControl.insertSegment(withTitle: $0.title, at: $0.rawValue, animated: false) + } + segmentedControl.selectedSegmentIndex = ServerDetailsTab.about.rawValue + segmentedControl.addTarget(self, action: #selector(ServerDetailsViewController.segmentedControlValueChanged(_:)), for: .valueChanged) + + setupConstraints() + + title = domain + + setupNavigationBarAppearance() + setupNavigationBarBackgroundView() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + let constraints = [ + segmentedControlWrapper.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + segmentedControlWrapper.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view.trailingAnchor.constraint(equalTo: segmentedControlWrapper.trailingAnchor), + + containerView.topAnchor.constraint(equalTo: segmentedControlWrapper.bottomAnchor), + containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), + view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), + + segmentedControl.topAnchor.constraint(equalTo: segmentedControlWrapper.topAnchor, constant: 4), + segmentedControl.leadingAnchor.constraint(equalTo: segmentedControlWrapper.leadingAnchor, constant: 16), + segmentedControlWrapper.trailingAnchor.constraint(equalTo: segmentedControl.trailingAnchor, constant: 16), + segmentedControlWrapper.bottomAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8), + + pageController.view.topAnchor.constraint(equalTo: containerView.topAnchor), + pageController.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), + containerView.trailingAnchor.constraint(equalTo: pageController.view.trailingAnchor), + containerView.bottomAnchor.constraint(equalTo: pageController.view.bottomAnchor), + ] + + NSLayoutConstraint.activate(constraints) + } + + //MARK: - Actions + @objc + func segmentedControlValueChanged(_ sender: UISegmentedControl) { + guard let selectedTab = ServerDetailsTab(rawValue: sender.selectedSegmentIndex) else { return } + + switch selectedTab { + case .about: + pageController.setViewControllers([aboutInstanceViewController], direction: .reverse, animated: true) + case .rules: + pageController.setViewControllers([instanceRulesViewController], direction: .forward, animated: true) + } + } + + func update(with instance: Mastodon.Entity.V2.Instance) { + aboutInstanceViewController.update(with: instance) + instanceRulesViewController.update(with: instance) + } + + func updateFooter(with extendedDescription: Mastodon.Entity.ExtendedDescription) { + aboutInstanceViewController.updateFooter(with: extendedDescription) + } +} + +//MARK: - UIPageViewControllerDataSource +extension ServerDetailsViewController: UIPageViewControllerDataSource { + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + if viewController == instanceRulesViewController { + return aboutInstanceViewController + } else { + return nil + } + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + if viewController == aboutInstanceViewController { + return instanceRulesViewController + } else { + return nil + } + } +} + +//MARK: - UIPageViewControllerDelegate +extension ServerDetailsViewController: UIPageViewControllerDelegate { + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + guard let currentViewController = pageViewController.viewControllers?.first else { return } + + if currentViewController == aboutInstanceViewController { + segmentedControl.selectedSegmentIndex = ServerDetailsTab.about.rawValue + } else if currentViewController == instanceRulesViewController { + segmentedControl.selectedSegmentIndex = ServerDetailsTab.rules.rawValue + } + } +} + +extension ServerDetailsViewController: OnboardingViewControllerAppearance {} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstance.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstance.swift new file mode 100644 index 000000000..e0d5b61ba --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstance.swift @@ -0,0 +1,18 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation +import MastodonSDK +import MastodonLocalization + +enum AboutInstanceSection: Int, Hashable { + case main = 0 + + var title: String { + return L10n.Scene.Settings.ServerDetails.AboutInstance.title + } +} + +enum AboutInstanceItem: Hashable { + case adminAccount(Mastodon.Entity.Account) + case contactAdmin(String) +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableFooterView.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableFooterView.swift new file mode 100644 index 000000000..4118e1e40 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableFooterView.swift @@ -0,0 +1,68 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MetaTextKit +import MastodonSDK +import MastodonMeta +import MastodonCore +import MastodonAsset +import MastodonLocalization + +class AboutInstanceTableFooterView: UIView { + let headlineLabel: UILabel + let contentLabel: MetaLabel + + init() { + + headlineLabel = UILabel() + headlineLabel.translatesAutoresizingMaskIntoConstraints = false + headlineLabel.font = UIFontMetrics(forTextStyle: .title2).scaledFont(for: .systemFont(ofSize: 22, weight: .bold)) + + contentLabel = MetaLabel(style: .aboutInstance) + contentLabel.numberOfLines = 0 + contentLabel.translatesAutoresizingMaskIntoConstraints = false + super.init(frame: .zero) + + addSubview(headlineLabel) + addSubview(contentLabel) + + setupConstraints() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + + let horizontalMargin = 16.0 + let verticalMargin = 24.0 + + let constraints = [ + headlineLabel.topAnchor.constraint(equalTo: topAnchor, constant: verticalMargin), + headlineLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalMargin), + trailingAnchor.constraint(equalTo: headlineLabel.trailingAnchor, constant: horizontalMargin), + + contentLabel.topAnchor.constraint(equalTo: headlineLabel.bottomAnchor, constant: 8), + contentLabel.leadingAnchor.constraint(equalTo: headlineLabel.leadingAnchor), + contentLabel.trailingAnchor.constraint(equalTo: headlineLabel.trailingAnchor), + bottomAnchor.constraint(equalTo: contentLabel.bottomAnchor, constant: verticalMargin), + ] + + NSLayoutConstraint.activate(constraints) + } + + func update(with extendedDescription: Mastodon.Entity.ExtendedDescription) { + headlineLabel.text = L10n.Scene.Settings.ServerDetails.AboutInstance.legalNotice + + let content = extendedDescription.content + .replacingOccurrences(of: "
", with: "\n") + .replacingOccurrences(of: "\n\n", with: "\n") + + + if let metaContent = try? MastodonMetaContent.convert(document: MastodonContent(content: content, emojis: [:])) { + contentLabel.configure(content: metaContent) + } else { + let content = PlaintextMetaContent(string: content) + contentLabel.configure(content: content) + } + } +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewDataSource.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewDataSource.swift new file mode 100644 index 000000000..508097119 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewDataSource.swift @@ -0,0 +1,16 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit + +class AboutInstanceTableViewDataSource: UITableViewDiffableDataSource { + + override init(tableView: UITableView, cellProvider: @escaping UITableViewDiffableDataSource.CellProvider) { + super.init(tableView: tableView, cellProvider: cellProvider) + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + guard let section = AboutInstanceSection(rawValue: section) else { return nil } + + return section.title.uppercased() + } +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewHeader.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewHeader.swift new file mode 100644 index 000000000..0e8d731f1 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewHeader.swift @@ -0,0 +1,40 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonAsset +import AlamofireImage + +class AboutInstanceTableHeaderView: UIView { + let thumbnailImageView: UIImageView + + init() { + thumbnailImageView = UIImageView(image: Asset.Settings.aboutInstancePlaceholder.image) + thumbnailImageView.contentMode = .scaleAspectFill + thumbnailImageView.translatesAutoresizingMaskIntoConstraints = false + + super.init(frame: .zero) + + addSubview(thumbnailImageView) + + setupConstraints() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + let constraints = [ + thumbnailImageView.topAnchor.constraint(equalTo: topAnchor), + thumbnailImageView.leadingAnchor.constraint(equalTo: leadingAnchor), + trailingAnchor.constraint(equalTo: thumbnailImageView.trailingAnchor), + bottomAnchor.constraint(equalTo: thumbnailImageView.bottomAnchor, constant: 16), + ] + + NSLayoutConstraint.activate(constraints) + } + + func updateImage(with thumbnailURL: URL, completion: (() -> Void)? = nil) { + thumbnailImageView.af.setImage(withURL: thumbnailURL, placeholderImage: Asset.Settings.aboutInstancePlaceholder.image, completion: { _ in + completion?() + }) + } +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AdminTableViewCell.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AdminTableViewCell.swift new file mode 100644 index 000000000..80acbc3d1 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AdminTableViewCell.swift @@ -0,0 +1,5 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit + +typealias AdminTableViewCell = SearchResultsProfileTableViewCell diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/ContactAdminTableViewCell.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/ContactAdminTableViewCell.swift new file mode 100644 index 000000000..69ffaef86 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/ContactAdminTableViewCell.swift @@ -0,0 +1,22 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonAsset +import MastodonLocalization + +class ContactAdminTableViewCell: UITableViewCell { + + static let reuseIdentifier = "ContactAdminTableViewCell" + + func configure() { + var configuration = defaultContentConfiguration() + + configuration.textProperties.color = Asset.Colors.Brand.blurple.color + configuration.image = UIImage(systemName: "envelope") + configuration.imageProperties.tintColor = Asset.Colors.Brand.blurple.color + configuration.text = L10n.Scene.Settings.ServerDetails.AboutInstance.messageAdmin + backgroundColor = .secondarySystemGroupedBackground + + contentConfiguration = configuration + } +} diff --git a/Mastodon/Scene/Settings/Settings Overview/Settings.swift b/Mastodon/Scene/Settings/Settings Overview/Settings.swift index bf3fba0bd..64c4c3ad3 100644 --- a/Mastodon/Scene/Settings/Settings Overview/Settings.swift +++ b/Mastodon/Scene/Settings/Settings Overview/Settings.swift @@ -10,62 +10,78 @@ struct SettingsSection: Hashable { enum SettingsEntry: Hashable { case general case notifications + case serverDetails(domain: String) case aboutMastodon case logout(accountName: String) var title: String { switch self { - case .general: - return L10n.Scene.Settings.Overview.general - case .notifications: - return L10n.Scene.Settings.Overview.notifications - case .aboutMastodon: - return L10n.Scene.Settings.Overview.aboutMastodon - case .logout(let accountName): - return L10n.Scene.Settings.Overview.logout(accountName) + case .general: + return L10n.Scene.Settings.Overview.general + case .notifications: + return L10n.Scene.Settings.Overview.notifications + case .serverDetails(_): + return L10n.Scene.Settings.Overview.serverDetails + case .aboutMastodon: + return L10n.Scene.Settings.Overview.aboutMastodon + case .logout(let accountName): + return L10n.Scene.Settings.Overview.logout(accountName) + } + } + + var secondaryTitle: String? { + switch self { + case .serverDetails(domain: let domain): + return domain + case .general, .notifications, .aboutMastodon, .logout(_): + return nil } } var accessoryType: UITableViewCell.AccessoryType { switch self { - case .general, .notifications, .aboutMastodon, .logout(_): - return .disclosureIndicator + case .general, .notifications, .serverDetails(_), .aboutMastodon, .logout(_): + return .disclosureIndicator } } var icon: UIImage? { switch self { - case .general: - return UIImage(systemName: "gear") - case .notifications: - return UIImage(systemName: "bell.badge") - case .aboutMastodon: - return UIImage(systemName: "info.circle.fill") - case .logout(_): - return nil + case .general: + return UIImage(systemName: "gear") + case .notifications: + return UIImage(systemName: "bell.badge") + case .serverDetails(_): + return UIImage(systemName: "server.rack") + case .aboutMastodon: + return UIImage(systemName: "info.circle.fill") + case .logout(_): + return nil } } var iconBackgroundColor: UIColor? { switch self { - case .general: - return .systemGray - case .notifications: - return .systemRed - case .aboutMastodon: - return .systemPurple - case .logout(_): - return nil + case .general: + return .systemGray + case .notifications: + return .systemRed + case .serverDetails(_): + return .systemTeal + case .aboutMastodon: + return .systemPurple + case .logout(_): + return nil } } var textColor: UIColor { switch self { - case .general, .notifications, .aboutMastodon: - return .label - case .logout(_): - return .red + case .general, .notifications, .aboutMastodon, .serverDetails(_): + return .label + case .logout(_): + return .red } } diff --git a/Mastodon/Scene/Settings/Settings Overview/SettingsTableViewCell.swift b/Mastodon/Scene/Settings/Settings Overview/SettingsTableViewCell.swift index 7e09f0fce..fcb181320 100644 --- a/Mastodon/Scene/Settings/Settings Overview/SettingsTableViewCell.swift +++ b/Mastodon/Scene/Settings/Settings Overview/SettingsTableViewCell.swift @@ -9,7 +9,9 @@ class SettingsTableViewCell: UITableViewCell { let iconImageView: UIImageView let iconImageBackgroundView: UIView let titleLabel: UILabel + let secondaryLabel: UILabel + private let labelStackView: UIStackView private let contentStackView: UIStackView override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { @@ -21,8 +23,17 @@ class SettingsTableViewCell: UITableViewCell { iconImageBackgroundView.addSubview(iconImageView) titleLabel = UILabel() + titleLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) + secondaryLabel = UILabel() + secondaryLabel.font = UIFontMetrics(forTextStyle: .caption1).scaledFont(for: .systemFont(ofSize: 12, weight: .regular)) + secondaryLabel.textColor = .secondaryLabel - contentStackView = UIStackView(arrangedSubviews: [iconImageBackgroundView, titleLabel]) + labelStackView = UIStackView(arrangedSubviews: [titleLabel, secondaryLabel]) + labelStackView.axis = .vertical + labelStackView.spacing = 2 + labelStackView.alignment = .leading + + contentStackView = UIStackView(arrangedSubviews: [iconImageBackgroundView, labelStackView]) contentStackView.translatesAutoresizingMaskIntoConstraints = false contentStackView.axis = .horizontal contentStackView.alignment = .center @@ -50,8 +61,8 @@ class SettingsTableViewCell: UITableViewCell { iconImageView.centerXAnchor.constraint(equalTo: iconImageBackgroundView.centerXAnchor), iconImageView.widthAnchor.constraint(equalToConstant: 20), - titleLabel.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 12), - contentView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLabel.bottomAnchor, constant: 12), + labelStackView.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 12), + contentView.bottomAnchor.constraint(greaterThanOrEqualTo: labelStackView.bottomAnchor, constant: 12), ] NSLayoutConstraint.activate(constraints) @@ -61,6 +72,13 @@ class SettingsTableViewCell: UITableViewCell { titleLabel.textColor = entry.textColor titleLabel.text = entry.title + if let secondaryTitle = entry.secondaryTitle { + secondaryLabel.isHidden = false + secondaryLabel.text = secondaryTitle + } else { + secondaryLabel.isHidden = true + } + if let icon = entry.icon { iconImageView.image = icon iconImageView.tintColor = .white @@ -73,7 +91,6 @@ class SettingsTableViewCell: UITableViewCell { iconImageBackgroundView.backgroundColor = entry.iconBackgroundColor accessoryType = entry.accessoryType - } } diff --git a/Mastodon/Scene/Settings/Settings Overview/SettingsViewController.swift b/Mastodon/Scene/Settings/Settings Overview/SettingsViewController.swift index 5c8727110..8dab17a63 100644 --- a/Mastodon/Scene/Settings/Settings Overview/SettingsViewController.swift +++ b/Mastodon/Scene/Settings/Settings Overview/SettingsViewController.swift @@ -16,11 +16,11 @@ class SettingsViewController: UIViewController { var tableViewDataSource: UITableViewDiffableDataSource? let tableView: UITableView - init(accountName: String) { + init(accountName: String, domain: String) { sections = [ .init(entries: [.general, .notifications]), - .init(entries: [.aboutMastodon]), + .init(entries: [.serverDetails(domain: domain), .aboutMastodon]), .init(entries: [.logout(accountName: accountName)]) ] diff --git a/Mastodon/Scene/Settings/SettingsCoordinator.swift b/Mastodon/Scene/Settings/SettingsCoordinator.swift index 93059ed91..2c4973a30 100644 --- a/Mastodon/Scene/Settings/SettingsCoordinator.swift +++ b/Mastodon/Scene/Settings/SettingsCoordinator.swift @@ -6,6 +6,7 @@ import MastodonCore import CoreDataStack import MastodonSDK import Combine +import MetaTextKit protocol SettingsCoordinatorDelegate: AnyObject { func logout(_ settingsCoordinator: SettingsCoordinator) @@ -26,15 +27,17 @@ class SettingsCoordinator: NSObject, Coordinator { let appContext: AppContext let authContext: AuthContext var disposeBag = Set() + let sceneCoordinator: SceneCoordinator - init(presentedOn: UIViewController, accountName: String, setting: Setting, appContext: AppContext, authContext: AuthContext) { + init(presentedOn: UIViewController, accountName: String, setting: Setting, appContext: AppContext, authContext: AuthContext, sceneCoordinator: SceneCoordinator) { self.presentedOn = presentedOn navigationController = UINavigationController() self.setting = setting self.appContext = appContext self.authContext = authContext + self.sceneCoordinator = sceneCoordinator - settingsViewController = SettingsViewController(accountName: accountName) + settingsViewController = SettingsViewController(accountName: accountName, domain: authContext.mastodonAuthenticationBox.domain) } func start() { @@ -65,8 +68,29 @@ extension SettingsCoordinator: SettingsViewControllerDelegate { let notificationViewController = NotificationSettingsViewController(currentSetting: currentSetting, notificationsEnabled: notificationsEnabled) notificationViewController.delegate = self - self.navigationController.pushViewController(notificationViewController, animated: true) + navigationController.pushViewController(notificationViewController, animated: true) + case .serverDetails(let domain): + let serverDetailsViewController = ServerDetailsViewController(domain: domain) + serverDetailsViewController.delegate = self + appContext.apiService.instanceV2(domain: domain) + .sink { _ in + + } receiveValue: { content in + serverDetailsViewController.update(with: content.value) + } + .store(in: &disposeBag) + + appContext.apiService.extendedDescription(domain: domain) + .sink { _ in + + } receiveValue: { content in + serverDetailsViewController.updateFooter(with: content.value) + } + .store(in: &disposeBag) + + + navigationController.pushViewController(serverDetailsViewController, animated: true) case .aboutMastodon: let aboutViewController = AboutViewController() aboutViewController.delegate = self @@ -135,6 +159,8 @@ extension SettingsCoordinator: NotificationSettingsViewControllerDelegate { guard viewModel.updated else { return } + //Show spinner? + let authenticationBox = authContext.mastodonAuthenticationBox guard let subscription = setting.activeSubscription, setting.domain == authenticationBox.domain, @@ -183,3 +209,58 @@ extension SettingsCoordinator: PolicySelectionViewControllerDelegate { try? self.appContext.managedObjectContext.save() } } + +//MARK: - ServerDetailsViewControllerDelegate +extension SettingsCoordinator: ServerDetailsViewControllerDelegate { + +} + +extension SettingsCoordinator: AboutInstanceViewControllerDelegate { + @MainActor func showAdminAccount(_ viewController: AboutInstanceViewController, account: Mastodon.Entity.Account) { + Task { + let user = try await appContext.apiService.fetchUser(username: account.username, domain: authContext.mastodonAuthenticationBox.domain, authenticationBox: authContext.mastodonAuthenticationBox) + + let profileViewModel = ProfileViewModel(context: appContext, authContext: authContext, optionalMastodonUser: user) + + _ = await MainActor.run { + sceneCoordinator.present(scene: .profile(viewModel: profileViewModel), transition: .show) + } + } + } + + func sendEmailToAdmin(_ viewController: AboutInstanceViewController, emailAddress: String) { + if let emailUrl = URL(string: "mailto:\(emailAddress)"), UIApplication.shared.canOpenURL(emailUrl) { + UIApplication.shared.open(emailUrl) + } + } +} + +extension SettingsCoordinator: InstanceRulesViewControllerDelegate { + +} + +extension SettingsCoordinator: MetaLabelDelegate { + @MainActor + func metaLabel(_ metaLabel: MetaLabel, didSelectMeta meta: Meta) { + switch meta { + case .url(_, _, let url, _): + guard let url = URL(string: url) else { return } + _ = sceneCoordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil)) + case .mention(_, _, let userInfo): + guard let href = userInfo?["href"] as? String, + let url = URL(string: href) else { return } + _ = sceneCoordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil)) + case .hashtag(_, let hashtag, _): + let hashtagTimelineViewModel = HashtagTimelineViewModel(context: appContext, authContext: authContext, hashtag: hashtag) + _ = sceneCoordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: nil, transition: .show) + case .email(let email, _): + if let emailUrl = URL(string: "mailto:\(email)"), UIApplication.shared.canOpenURL(emailUrl) { + UIApplication.shared.open(emailUrl) + } + case .emoji: + break + } + } + + +} diff --git a/MastodonSDK/Package.resolved b/MastodonSDK/Package.resolved deleted file mode 100644 index 39f40fa17..000000000 --- a/MastodonSDK/Package.resolved +++ /dev/null @@ -1,239 +0,0 @@ -{ - "pins" : [ - { - "identity" : "alamofire", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Alamofire/Alamofire.git", - "state" : { - "revision" : "8dd85aee02e39dd280c75eef88ffdb86eed4b07b", - "version" : "5.6.2" - } - }, - { - "identity" : "alamofireimage", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Alamofire/AlamofireImage.git", - "state" : { - "revision" : "98cbb00ce0ec5fc8e52a5b50a6bfc08d3e5aee10", - "version" : "4.2.0" - } - }, - { - "identity" : "commonoslog", - "kind" : "remoteSourceControl", - "location" : "https://github.com/MainasuK/CommonOSLog", - "state" : { - "revision" : "c121624a30698e9886efe38aebb36ff51c01b6c2", - "version" : "0.1.1" - } - }, - { - "identity" : "faviconfinder", - "kind" : "remoteSourceControl", - "location" : "https://github.com/will-lumley/FaviconFinder.git", - "state" : { - "revision" : "1f74844f77f79b95c0bb0130b3a87d4f340e6d3a", - "version" : "3.3.0" - } - }, - { - "identity" : "flanimatedimage", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Flipboard/FLAnimatedImage.git", - "state" : { - "revision" : "d4f07b6f164d53c1212c3e54d6460738b1981e9f", - "version" : "1.0.17" - } - }, - { - "identity" : "fpsindicator", - "kind" : "remoteSourceControl", - "location" : "https://github.com/MainasuK/FPSIndicator.git", - "state" : { - "revision" : "e4a5067ccd5293b024c767f09e51056afd4a4796", - "version" : "1.1.0" - } - }, - { - "identity" : "fuzi", - "kind" : "remoteSourceControl", - "location" : "https://github.com/cezheng/Fuzi.git", - "state" : { - "revision" : "f08c8323da21e985f3772610753bcfc652c2103f", - "version" : "3.1.3" - } - }, - { - "identity" : "keychainaccess", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kishikawakatsumi/KeychainAccess.git", - "state" : { - "revision" : "84e546727d66f1adc5439debad16270d0fdd04e7", - "version" : "4.2.2" - } - }, - { - "identity" : "metatextkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/TwidereProject/MetaTextKit.git", - "state" : { - "revision" : "dcd5255d6930c2fab408dc8562c577547e477624", - "version" : "2.2.5" - } - }, - { - "identity" : "nextlevelsessionexporter", - "kind" : "remoteSourceControl", - "location" : "https://github.com/NextLevel/NextLevelSessionExporter.git", - "state" : { - "revision" : "b6c0cce1aa37fe1547d694f958fac3c3524b74da", - "version" : "0.4.6" - } - }, - { - "identity" : "nuke", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kean/Nuke.git", - "state" : { - "revision" : "a002b7fd786f2df2ed4333fe73a9727499fd9d97", - "version" : "10.11.2" - } - }, - { - "identity" : "nuke-flanimatedimage-plugin", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git", - "state" : { - "revision" : "b59c346a7d536336db3b0f12c72c6e53ee709e16", - "version" : "8.0.0" - } - }, - { - "identity" : "pageboy", - "kind" : "remoteSourceControl", - "location" : "https://github.com/uias/Pageboy", - "state" : { - "revision" : "af8fa81788b893205e1ff42ddd88c5b0b315d7c5", - "version" : "3.7.0" - } - }, - { - "identity" : "panmodal", - "kind" : "remoteSourceControl", - "location" : "https://github.com/slackhq/PanModal.git", - "state" : { - "revision" : "b012aecb6b67a8e46369227f893c12544846613f", - "version" : "1.2.7" - } - }, - { - "identity" : "sdwebimage", - "kind" : "remoteSourceControl", - "location" : "https://github.com/SDWebImage/SDWebImage.git", - "state" : { - "revision" : "9248fe561a2a153916fb9597e3af4434784c6d32", - "version" : "5.13.4" - } - }, - { - "identity" : "stripes", - "kind" : "remoteSourceControl", - "location" : "https://github.com/eneko/Stripes.git", - "state" : { - "revision" : "d533fd44b8043a3abbf523e733599173d6f98c11", - "version" : "0.2.0" - } - }, - { - "identity" : "swift-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-collections.git", - "state" : { - "revision" : "f504716c27d2e5d4144fa4794b12129301d17729", - "version" : "1.0.3" - } - }, - { - "identity" : "swift-nio", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio.git", - "state" : { - "revision" : "546610d52b19be3e19935e0880bb06b9c03f5cef", - "version" : "1.14.4" - } - }, - { - "identity" : "swift-nio-zlib-support", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-zlib-support.git", - "state" : { - "revision" : "37760e9a52030bb9011972c5213c3350fa9d41fd", - "version" : "1.0.0" - } - }, - { - "identity" : "swiftsoup", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scinfu/SwiftSoup.git", - "state" : { - "revision" : "6778575285177365cbad3e5b8a72f2a20583cfec", - "version" : "2.4.3" - } - }, - { - "identity" : "swiftui-introspect", - "kind" : "remoteSourceControl", - "location" : "https://github.com/siteline/SwiftUI-Introspect.git", - "state" : { - "revision" : "f2616860a41f9d9932da412a8978fec79c06fe24", - "version" : "0.1.4" - } - }, - { - "identity" : "tabbarpager", - "kind" : "remoteSourceControl", - "location" : "https://github.com/TwidereProject/TabBarPager.git", - "state" : { - "revision" : "488aa66d157a648901b61721212c0dec23d27ee5", - "version" : "0.1.0" - } - }, - { - "identity" : "tabman", - "kind" : "remoteSourceControl", - "location" : "https://github.com/uias/Tabman", - "state" : { - "revision" : "4a4f7c755b875ffd4f9ef10d67a67883669d2465", - "version" : "2.13.0" - } - }, - { - "identity" : "tocropviewcontroller", - "kind" : "remoteSourceControl", - "location" : "https://github.com/TimOliver/TOCropViewController.git", - "state" : { - "revision" : "d0470491f56e734731bbf77991944c0dfdee3e0e", - "version" : "2.6.1" - } - }, - { - "identity" : "uihostingconfigurationbackport", - "kind" : "remoteSourceControl", - "location" : "https://github.com/woxtu/UIHostingConfigurationBackport.git", - "state" : { - "revision" : "6091f2d38faa4b24fc2ca0389c651e2f666624a3", - "version" : "0.1.0" - } - }, - { - "identity" : "uitextview-placeholder", - "kind" : "remoteSourceControl", - "location" : "https://github.com/MainasuK/UITextView-Placeholder.git", - "state" : { - "revision" : "20f513ded04a040cdf5467f0891849b1763ede3b", - "version" : "1.4.1" - } - } - ], - "version" : 2 -} diff --git a/MastodonSDK/Package.swift b/MastodonSDK/Package.swift index 655349ce1..a6ac785e7 100644 --- a/MastodonSDK/Package.swift +++ b/MastodonSDK/Package.swift @@ -42,7 +42,6 @@ let package = Package( .package(url: "https://github.com/apple/swift-collections.git", from: "1.0.3"), .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), .package(url: "https://github.com/Flipboard/FLAnimatedImage.git", from: "1.0.0"), - .package(url: "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git", from: "8.0.0"), .package(url: "https://github.com/kean/Nuke.git", from: "10.3.1"), .package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "4.2.2"), .package(url: "https://github.com/slackhq/PanModal.git", from: "1.2.7"), @@ -54,6 +53,7 @@ let package = Package( .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.12.0"), .package(url: "https://github.com/eneko/Stripes.git", from: "0.2.0"), .package(url: "https://github.com/NextLevel/NextLevelSessionExporter.git", from: "0.4.6"), + .package(url: "https://github.com/xmartlabs/XLPagerTabStrip.git", from: "9.1.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -126,6 +126,8 @@ let package = Package( .product(name: "PanModal", package: "PanModal"), .product(name: "Stripes", package: "Stripes"), .product(name: "NextLevelSessionExporter", package: "NextLevelSessionExporter"), + .product(name: "SDWebImage", package: "SDWebImage"), + .product(name: "XLPagerTabStrip", package: "XLPagerTabStrip"), ] ), .testTarget( diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/Contents.json similarity index 73% rename from MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/Contents.json rename to MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/Contents.json index 45265b0c2..95dc59919 100644 --- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/Contents.json +++ b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "dark.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "dark@2x.png", + "filename" : "about_instance_placeholder@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "dark@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/about_instance_placeholder@2x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/about_instance_placeholder@2x.png new file mode 100644 index 000000000..d40e27a12 Binary files /dev/null and b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/about_instance_placeholder@2x.png differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/Contents.json deleted file mode 100644 index 634b1b249..000000000 --- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "automatic.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "automatic@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "automatic@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic.png deleted file mode 100644 index 8a4997026..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@2x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@2x.png deleted file mode 100644 index 353f123a5..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@2x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@3x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@3x.png deleted file mode 100644 index 6a865417a..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@3x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark.png deleted file mode 100644 index 5bfe79abf..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@2x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@2x.png deleted file mode 100644 index 4bbee3622..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@2x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@3x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@3x.png deleted file mode 100644 index 4ea316fb2..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@3x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/Contents.json deleted file mode 100644 index c89e6245e..000000000 --- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "light@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light.png deleted file mode 100644 index 23efb384c..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@2x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@2x.png deleted file mode 100644 index 6bff099c8..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@2x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@3x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@3x.png deleted file mode 100644 index 94c68eb50..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@3x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift b/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift index d23979b95..be8f204e0 100644 --- a/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift +++ b/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift @@ -219,9 +219,7 @@ public enum Asset { } } public enum Settings { - public static let automatic = ImageAsset(name: "Settings/automatic") - public static let dark = ImageAsset(name: "Settings/dark") - public static let light = ImageAsset(name: "Settings/light") + public static let aboutInstancePlaceholder = ImageAsset(name: "Settings/about_instance_placeholder") } public enum Theme { public enum System { diff --git a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Instance.swift b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Instance.swift index b5a549903..03a63461e 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Instance.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Instance.swift @@ -24,4 +24,8 @@ extension APIService { ) -> AnyPublisher, Error> { return Mastodon.API.V2.Instance.instance(session: session, domain: domain) } + + public func extendedDescription(domain: String) -> AnyPublisher, Error> { + return Mastodon.API.Instance.extendedDescription(session: session, domain: domain) + } } diff --git a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift index 05ea58f05..af79705c1 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift +++ b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift @@ -1418,7 +1418,7 @@ public enum L10n { public enum Settings { public enum AboutMastodon { /// Clear Media Storage - public static let cleaerMediaStorage = L10n.tr("Localizable", "Scene.Settings.AboutMastodon.CleaerMediaStorage", fallback: "Clear Media Storage") + public static let clearMediaStorage = L10n.tr("Localizable", "Scene.Settings.AboutMastodon.ClearMediaStorage", fallback: "Clear Media Storage") /// Contribute to Mastodon public static let contributeToMastodon = L10n.tr("Localizable", "Scene.Settings.AboutMastodon.ContributeToMastodon", fallback: "Contribute to Mastodon") /// Even More Settings @@ -1509,6 +1509,8 @@ public enum L10n { } /// Notifications public static let notifications = L10n.tr("Localizable", "Scene.Settings.Overview.Notifications", fallback: "Notifications") + /// Server Details + public static let serverDetails = L10n.tr("Localizable", "Scene.Settings.Overview.ServerDetails", fallback: "Server Details") /// Support Mastodon public static let supportMastodon = L10n.tr("Localizable", "Scene.Settings.Overview.SupportMastodon", fallback: "Support Mastodon") /// Settings @@ -1592,6 +1594,20 @@ public enum L10n { public static let title = L10n.tr("Localizable", "Scene.Settings.Section.SpicyZone.Title", fallback: "The Spicy Zone") } } + public enum ServerDetails { + /// About + public static let about = L10n.tr("Localizable", "Scene.Settings.ServerDetails.About", fallback: "About") + /// Rules + public static let rules = L10n.tr("Localizable", "Scene.Settings.ServerDetails.Rules", fallback: "Rules") + public enum AboutInstance { + /// A legal notice + public static let legalNotice = L10n.tr("Localizable", "Scene.Settings.ServerDetails.AboutInstance.LegalNotice", fallback: "A legal notice") + /// Message Admin + public static let messageAdmin = L10n.tr("Localizable", "Scene.Settings.ServerDetails.AboutInstance.MessageAdmin", fallback: "Message Admin") + /// Administrator + public static let title = L10n.tr("Localizable", "Scene.Settings.ServerDetails.AboutInstance.Title", fallback: "Administrator") + } + } } public enum SuggestionAccount { /// Follow all diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings index ab6fef20f..509de54c4 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings @@ -527,6 +527,7 @@ uploaded to Mastodon."; "Scene.Settings.Overview.General" = "General"; "Scene.Settings.Overview.Notifications" = "Notifications"; "Scene.Settings.Overview.SupportMastodon" = "Support Mastodon"; +"Scene.Settings.Overview.ServerDetails" = "Server Details"; "Scene.Settings.Overview.AboutMastodon" = "About Mastodon"; "Scene.Settings.Overview.Logout" = "Logout %@"; @@ -534,7 +535,13 @@ uploaded to Mastodon."; "Scene.Settings.AboutMastodon.MoreSettings" = "Even More Settings"; "Scene.Settings.AboutMastodon.ContributeToMastodon" = "Contribute to Mastodon"; "Scene.Settings.AboutMastodon.PrivacyPolicy" = "Privacy Policy"; -"Scene.Settings.AboutMastodon.CleaerMediaStorage" = "Clear Media Storage"; +"Scene.Settings.AboutMastodon.ClearMediaStorage" = "Clear Media Storage"; + +"Scene.Settings.ServerDetails.About" = "About"; +"Scene.Settings.ServerDetails.Rules" = "Rules"; +"Scene.Settings.ServerDetails.AboutInstance.Title" = "Administrator"; +"Scene.Settings.ServerDetails.AboutInstance.MessageAdmin" = "Message Admin"; +"Scene.Settings.ServerDetails.AboutInstance.LegalNotice" = "A legal notice"; "Scene.Settings.General.Title" = "General"; "Scene.Settings.General.Appearance.SectionTitle" = "Appearance"; diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings index ab6fef20f..509de54c4 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings @@ -527,6 +527,7 @@ uploaded to Mastodon."; "Scene.Settings.Overview.General" = "General"; "Scene.Settings.Overview.Notifications" = "Notifications"; "Scene.Settings.Overview.SupportMastodon" = "Support Mastodon"; +"Scene.Settings.Overview.ServerDetails" = "Server Details"; "Scene.Settings.Overview.AboutMastodon" = "About Mastodon"; "Scene.Settings.Overview.Logout" = "Logout %@"; @@ -534,7 +535,13 @@ uploaded to Mastodon."; "Scene.Settings.AboutMastodon.MoreSettings" = "Even More Settings"; "Scene.Settings.AboutMastodon.ContributeToMastodon" = "Contribute to Mastodon"; "Scene.Settings.AboutMastodon.PrivacyPolicy" = "Privacy Policy"; -"Scene.Settings.AboutMastodon.CleaerMediaStorage" = "Clear Media Storage"; +"Scene.Settings.AboutMastodon.ClearMediaStorage" = "Clear Media Storage"; + +"Scene.Settings.ServerDetails.About" = "About"; +"Scene.Settings.ServerDetails.Rules" = "Rules"; +"Scene.Settings.ServerDetails.AboutInstance.Title" = "Administrator"; +"Scene.Settings.ServerDetails.AboutInstance.MessageAdmin" = "Message Admin"; +"Scene.Settings.ServerDetails.AboutInstance.LegalNotice" = "A legal notice"; "Scene.Settings.General.Title" = "General"; "Scene.Settings.General.Appearance.SectionTitle" = "Appearance"; diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Instance.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Instance.swift index e90ee27c1..871f43632 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Instance.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Instance.swift @@ -30,11 +30,7 @@ extension Mastodon.API.Instance { session: URLSession, domain: String ) -> AnyPublisher, Error> { - let request = Mastodon.API.get( - url: instanceEndpointURL(domain: domain), - query: nil, - authorization: nil - ) + let request = Mastodon.API.get(url: instanceEndpointURL(domain: domain)) return session.dataTaskPublisher(for: request) .tryMap { data, response in let value: Mastodon.Entity.Instance @@ -53,5 +49,27 @@ extension Mastodon.API.Instance { } .eraseToAnyPublisher() } - + + static func extendedDescriptionEndpointURL(domain: String) -> URL { + return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("instance").appendingPathComponent("extended_description") + } + + /// Extended description of the server + /// + /// - Returns: A ``MastodonSDK.Mastodon.Entity.ExtendedDescription`` + /// + /// ## Reference: + /// [Document](https://docs.joinmastodon.org/methods/instance/#extended_description) + public static func extendedDescription( + session: URLSession, + domain: String + ) -> AnyPublisher, Error> { + let request = Mastodon.API.get(url: extendedDescriptionEndpointURL(domain: domain)) + return session.dataTaskPublisher(for: request) + .tryMap { data, response in + let value = try Mastodon.API.decode(type: Mastodon.Entity.ExtendedDescription.self, from: data, response: response) + return Mastodon.Response.Content(value: value, response: response) + } + .eraseToAnyPublisher() + } } diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+ExtendedDescription.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+ExtendedDescription.swift new file mode 100644 index 000000000..c42fe6ab1 --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+ExtendedDescription.swift @@ -0,0 +1,19 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation + +extension Mastodon.Entity { + /// Extended description of the server + + /// ## Reference: + /// [Document](https://docs.joinmastodon.org/entities/ExtendedDescription/) + public struct ExtendedDescription: Codable { + public let updatedAt: Date + public let content: String + + enum CodingKeys: String, CodingKey { + case updatedAt = "updated_at" + case content + } + } +} diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+InstanceV2.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+InstanceV2.swift index d662c24c2..3cfb29699 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+InstanceV2.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+InstanceV2.swift @@ -10,12 +10,11 @@ extension Mastodon.Entity.V2 { /// # Reference /// [Document](https://docs.joinmastodon.org/entities/instance/) public struct Instance: Codable { - + public let domain: String? public let title: String public let description: String public let shortDescription: String? - public let email: String? public let version: String? public let languages: [String]? // (ISO 639 Part 1-5 language codes) public let registrations: Mastodon.Entity.V2.Instance.Registrations? @@ -25,7 +24,7 @@ extension Mastodon.Entity.V2 { public let statistics: Mastodon.Entity.Instance.Statistics? public let thumbnail: Thumbnail? - public let contactAccount: Mastodon.Entity.Account? + public let contact: Mastodon.Entity.V2.Instance.Contact? public let rules: [Mastodon.Entity.Instance.Rule]? // https://github.com/mastodon/mastodon/pull/16485 @@ -36,7 +35,7 @@ extension Mastodon.Entity.V2 { self.title = domain self.description = "" self.shortDescription = nil - self.email = "" + self.contact = nil self.version = nil self.languages = nil self.registrations = nil @@ -45,7 +44,6 @@ extension Mastodon.Entity.V2 { self.urls = nil self.statistics = nil self.thumbnail = nil - self.contactAccount = nil self.rules = nil self.configuration = nil } @@ -55,7 +53,6 @@ extension Mastodon.Entity.V2 { case title case description case shortDescription = "short_description" - case email case version case languages case registrations @@ -65,7 +62,7 @@ extension Mastodon.Entity.V2 { case statistics = "stats" case thumbnail - case contactAccount = "contact_account" + case contact case rules case configuration @@ -106,3 +103,10 @@ extension Mastodon.Entity.V2.Instance { public let url: String? } } + +extension Mastodon.Entity.V2.Instance { + public struct Contact: Codable { + public let email: String? + public let account: Mastodon.Entity.Account? + } +} diff --git a/MastodonSDK/Sources/MastodonUI/Extension/MetaLabel.swift b/MastodonSDK/Sources/MastodonUI/Extension/MetaLabel.swift index b352bdbc2..cee24e98b 100644 --- a/MastodonSDK/Sources/MastodonUI/Extension/MetaLabel.swift +++ b/MastodonSDK/Sources/MastodonUI/Extension/MetaLabel.swift @@ -30,6 +30,7 @@ extension MetaLabel { case accountListUsername case sidebarHeadline(isSelected: Bool) case sidebarSubheadline(isSelected: Bool) + case aboutInstance } public convenience init(style: Style) { @@ -129,6 +130,10 @@ extension MetaLabel { case .sidebarSubheadline(let isSelected): font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 13, weight: .regular), maximumPointSize: 18) textColor = isSelected ? .white : Asset.Colors.Label.secondary.color + case .aboutInstance: + font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) + textColor = .label + paragraphStyle.paragraphSpacing = 0 } self.font = font diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/CondensedUserView.swift b/MastodonSDK/Sources/MastodonUI/View/Content/CondensedUserView.swift index be766f5e6..48089be64 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/CondensedUserView.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/CondensedUserView.swift @@ -163,7 +163,7 @@ public class CondensedUserView: UIView { } } - public func configure(with account: Mastodon.Entity.Account) { + public func configure(with account: Mastodon.Entity.Account, showFollowers: Bool = true) { let displayNameMetaContent: MetaContent do { let content = MastodonContent(content: account.displayNameWithFallback, emojis: account.emojis?.asDictionary ?? [:]) @@ -174,10 +174,16 @@ public class CondensedUserView: UIView { displayNameLabel.configure(content: displayNameMetaContent) acctLabel.text = account.acct - followersLabel.attributedText = NSAttributedString( - format: NSAttributedString(string: L10n.Common.UserList.followersCount("%@"), attributes: [.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))]), - args: NSAttributedString(string: Self.metricFormatter.string(from: account.followersCount) ?? account.followersCount.formatted(), attributes: [.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .bold))]) - ) + + if showFollowers { + followersLabel.attributedText = NSAttributedString( + format: NSAttributedString(string: L10n.Common.UserList.followersCount("%@"), attributes: [.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))]), + args: NSAttributedString(string: Self.metricFormatter.string(from: account.followersCount) ?? account.followersCount.formatted(), attributes: [.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .bold))]) + ) + followersLabel.isHidden = false + } else { + followersLabel.isHidden = false + } avatarImageView.setImage(url: account.avatarImageURL()) diff --git a/Podfile b/Podfile index 7a6e9b9ff..e9538aa3d 100644 --- a/Podfile +++ b/Podfile @@ -7,11 +7,6 @@ target 'Mastodon' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! - # Pods for Mastodon - - # UI - pod 'XLPagerTabStrip', '~> 9.0.0' - # misc pod 'SwiftGen', '~> 6.6.2' pod 'Kanna', '~> 5.2.2' diff --git a/Podfile.lock b/Podfile.lock index ae66ca1b6..24522c82e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -4,27 +4,23 @@ PODS: - Sourcery/CLI-Only (= 1.9.2) - Sourcery/CLI-Only (1.9.2) - SwiftGen (6.6.2) - - XLPagerTabStrip (9.0.0) DEPENDENCIES: - Kanna (~> 5.2.2) - Sourcery (~> 1.9) - SwiftGen (~> 6.6.2) - - XLPagerTabStrip (~> 9.0.0) SPEC REPOS: trunk: - Kanna - Sourcery - SwiftGen - - XLPagerTabStrip SPEC CHECKSUMS: Kanna: 01cfbddc127f5ff0963692f285fcbc8a9d62d234 Sourcery: 179539341c2261068528cd15a31837b7238fd901 SwiftGen: 1366a7f71aeef49954ca5a63ba4bef6b0f24138c - XLPagerTabStrip: 61c57fd61f611ee5f01ff1495ad6fbee8bf496c5 -PODFILE CHECKSUM: 8c962b3cbb4c225f1e57fb2e4ca03d1f22c45e5e +PODFILE CHECKSUM: 597c21d7aa08efec996048577c3c4fbeffbb6305 COCOAPODS: 1.13.0