mirror of
https://github.com/mastodon/mastodon-ios.git
synced 2024-12-22 22:28:35 +01:00
feat: add violates server rules report path
This commit is contained in:
parent
e0f6940e28
commit
2ef6345d83
@ -87,7 +87,7 @@
|
||||
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; };
|
||||
2DF75BA725D10E1000694EC8 /* APIService+Favorite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF75BA625D10E1000694EC8 /* APIService+Favorite.swift */; };
|
||||
5B24BBDA262DB14800A9381B /* ReportViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B24BBD7262DB14800A9381B /* ReportViewModel.swift */; };
|
||||
5B24BBDB262DB14800A9381B /* ReportViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B24BBD8262DB14800A9381B /* ReportViewModel+Diffable.swift */; };
|
||||
5B24BBDB262DB14800A9381B /* ReportStatusViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B24BBD8262DB14800A9381B /* ReportStatusViewModel+Diffable.swift */; };
|
||||
5B24BBE2262DB19100A9381B /* APIService+Report.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B24BBE1262DB19100A9381B /* APIService+Report.swift */; };
|
||||
5B90C45E262599800002E742 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B90C456262599800002E742 /* SettingsViewModel.swift */; };
|
||||
5B90C45F262599800002E742 /* SettingsToggleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B90C459262599800002E742 /* SettingsToggleTableViewCell.swift */; };
|
||||
@ -440,7 +440,7 @@
|
||||
DB98337125C9443200AD9700 /* APIService+Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337025C9443200AD9700 /* APIService+Authentication.swift */; };
|
||||
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337E25C9452D00AD9700 /* APIService+APIError.swift */; };
|
||||
DB98339C25C96DE600AD9700 /* APIService+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98339B25C96DE600AD9700 /* APIService+Account.swift */; };
|
||||
DB98EB4727B0DFAA0082E365 /* ReportViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4627B0DFAA0082E365 /* ReportViewModel+State.swift */; };
|
||||
DB98EB4727B0DFAA0082E365 /* ReportStatusViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4627B0DFAA0082E365 /* ReportStatusViewModel+State.swift */; };
|
||||
DB98EB4927B0F0CD0082E365 /* ReportStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4827B0F0CD0082E365 /* ReportStatusTableViewCell.swift */; };
|
||||
DB98EB4C27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB4B27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift */; };
|
||||
DB98EB5327B0F9890082E365 /* ReportHeadlineTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98EB5227B0F9890082E365 /* ReportHeadlineTableViewCell.swift */; };
|
||||
@ -566,6 +566,14 @@
|
||||
DBE3CE07261D6A0E00430CC6 /* FavoriteViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE3CE06261D6A0E00430CC6 /* FavoriteViewModel+Diffable.swift */; };
|
||||
DBE54AC62636C89F004E7C0B /* NotificationPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE54AC52636C89F004E7C0B /* NotificationPreference.swift */; };
|
||||
DBE54ACC2636C8FD004E7C0B /* NotificationPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE54AC52636C89F004E7C0B /* NotificationPreference.swift */; };
|
||||
DBEFCD71282A12B200C0ABEA /* ReportReasonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEFCD70282A12B200C0ABEA /* ReportReasonViewController.swift */; };
|
||||
DBEFCD74282A130400C0ABEA /* ReportReasonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEFCD73282A130400C0ABEA /* ReportReasonViewModel.swift */; };
|
||||
DBEFCD76282A143F00C0ABEA /* ReportStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEFCD75282A143F00C0ABEA /* ReportStatusViewController.swift */; };
|
||||
DBEFCD79282A147000C0ABEA /* ReportStatusViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEFCD78282A147000C0ABEA /* ReportStatusViewModel.swift */; };
|
||||
DBEFCD7B282A162400C0ABEA /* ReportReasonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEFCD7A282A162400C0ABEA /* ReportReasonView.swift */; };
|
||||
DBEFCD7D282A2A3B00C0ABEA /* ReportServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEFCD7C282A2A3B00C0ABEA /* ReportServerRulesViewController.swift */; };
|
||||
DBEFCD80282A2AA900C0ABEA /* ReportServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEFCD7F282A2AA900C0ABEA /* ReportServerRulesViewModel.swift */; };
|
||||
DBEFCD82282A2AB100C0ABEA /* ReportServerRulesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEFCD81282A2AB100C0ABEA /* ReportServerRulesView.swift */; };
|
||||
DBF156DF2701B17600EC00B7 /* SidebarAddAccountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF156DE2701B17600EC00B7 /* SidebarAddAccountCollectionViewCell.swift */; };
|
||||
DBF156E22702DA6900EC00B7 /* UIStatusBarManager+HandleTapAction.m in Sources */ = {isa = PBXBuildFile; fileRef = DBF156E12702DA6900EC00B7 /* UIStatusBarManager+HandleTapAction.m */; };
|
||||
DBF156E42702DB3F00EC00B7 /* HandleTapAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF156E32702DB3F00EC00B7 /* HandleTapAction.swift */; };
|
||||
@ -789,7 +797,7 @@
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
5B24BBD7262DB14800A9381B /* ReportViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportViewModel.swift; sourceTree = "<group>"; };
|
||||
5B24BBD8262DB14800A9381B /* ReportViewModel+Diffable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ReportViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||
5B24BBD8262DB14800A9381B /* ReportStatusViewModel+Diffable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ReportStatusViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||
5B24BBE1262DB19100A9381B /* APIService+Report.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+Report.swift"; sourceTree = "<group>"; };
|
||||
5B90C456262599800002E742 /* SettingsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
|
||||
5B90C459262599800002E742 /* SettingsToggleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsToggleTableViewCell.swift; sourceTree = "<group>"; };
|
||||
@ -1199,7 +1207,7 @@
|
||||
DB98337025C9443200AD9700 /* APIService+Authentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Authentication.swift"; sourceTree = "<group>"; };
|
||||
DB98337E25C9452D00AD9700 /* APIService+APIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+APIError.swift"; sourceTree = "<group>"; };
|
||||
DB98339B25C96DE600AD9700 /* APIService+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Account.swift"; sourceTree = "<group>"; };
|
||||
DB98EB4627B0DFAA0082E365 /* ReportViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReportViewModel+State.swift"; sourceTree = "<group>"; };
|
||||
DB98EB4627B0DFAA0082E365 /* ReportStatusViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReportStatusViewModel+State.swift"; sourceTree = "<group>"; };
|
||||
DB98EB4827B0F0CD0082E365 /* ReportStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportStatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||
DB98EB4B27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReportStatusTableViewCell+ViewModel.swift"; sourceTree = "<group>"; };
|
||||
DB98EB5227B0F9890082E365 /* ReportHeadlineTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportHeadlineTableViewCell.swift; sourceTree = "<group>"; };
|
||||
@ -1329,6 +1337,14 @@
|
||||
DBEB19E927E4F37B00B0E80E /* ku */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ku; path = ku.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||
DBEB19EA27E4F37B00B0E80E /* ku */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ku; path = ku.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DBEB19EB27E4F37B00B0E80E /* ku */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ku; path = ku.lproj/Intents.stringsdict; sourceTree = "<group>"; };
|
||||
DBEFCD70282A12B200C0ABEA /* ReportReasonViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportReasonViewController.swift; sourceTree = "<group>"; };
|
||||
DBEFCD73282A130400C0ABEA /* ReportReasonViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportReasonViewModel.swift; sourceTree = "<group>"; };
|
||||
DBEFCD75282A143F00C0ABEA /* ReportStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportStatusViewController.swift; sourceTree = "<group>"; };
|
||||
DBEFCD78282A147000C0ABEA /* ReportStatusViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportStatusViewModel.swift; sourceTree = "<group>"; };
|
||||
DBEFCD7A282A162400C0ABEA /* ReportReasonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportReasonView.swift; sourceTree = "<group>"; };
|
||||
DBEFCD7C282A2A3B00C0ABEA /* ReportServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportServerRulesViewController.swift; sourceTree = "<group>"; };
|
||||
DBEFCD7F282A2AA900C0ABEA /* ReportServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportServerRulesViewModel.swift; sourceTree = "<group>"; };
|
||||
DBEFCD81282A2AB100C0ABEA /* ReportServerRulesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportServerRulesView.swift; sourceTree = "<group>"; };
|
||||
DBF156DE2701B17600EC00B7 /* SidebarAddAccountCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarAddAccountCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
DBF156E02702DA6800EC00B7 /* Mastodon-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Mastodon-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
DBF156E12702DA6900EC00B7 /* UIStatusBarManager+HandleTapAction.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIStatusBarManager+HandleTapAction.m"; sourceTree = "<group>"; };
|
||||
@ -1842,6 +1858,9 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB98EB5727B0FF1F0082E365 /* Share */,
|
||||
DBEFCD77282A144D00C0ABEA /* Report */,
|
||||
DBEFCD72282A12B900C0ABEA /* ReportReason */,
|
||||
DBEFCD7E282A2A3D00C0ABEA /* ReportServerRules */,
|
||||
DB98EB4F27B0F9300082E365 /* ReportStatus */,
|
||||
DB98EB5A27B109900082E365 /* ReportSupplementary */,
|
||||
DB98EB6327B216490082E365 /* ReportResult */,
|
||||
@ -2208,8 +2227,8 @@
|
||||
DB427DD425BAA00100D1B89D /* Mastodon */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB427DE325BAA00100D1B89D /* Info.plist */,
|
||||
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */,
|
||||
DB427DE325BAA00100D1B89D /* Info.plist */,
|
||||
2D76319C25C151DE00929FB9 /* Diffiable */,
|
||||
DB8AF52A25C13561002E6C99 /* State */,
|
||||
2D61335525C1886800CAE157 /* Service */,
|
||||
@ -2850,10 +2869,10 @@
|
||||
DB98EB4F27B0F9300082E365 /* ReportStatus */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5BB04FD4262E7AFF0043BFF6 /* ReportViewController.swift */,
|
||||
5B24BBD7262DB14800A9381B /* ReportViewModel.swift */,
|
||||
5B24BBD8262DB14800A9381B /* ReportViewModel+Diffable.swift */,
|
||||
DB98EB4627B0DFAA0082E365 /* ReportViewModel+State.swift */,
|
||||
DBEFCD75282A143F00C0ABEA /* ReportStatusViewController.swift */,
|
||||
DBEFCD78282A147000C0ABEA /* ReportStatusViewModel.swift */,
|
||||
5B24BBD8262DB14800A9381B /* ReportStatusViewModel+Diffable.swift */,
|
||||
DB98EB4627B0DFAA0082E365 /* ReportStatusViewModel+State.swift */,
|
||||
);
|
||||
path = ReportStatus;
|
||||
sourceTree = "<group>";
|
||||
@ -3177,6 +3196,35 @@
|
||||
path = Favorite;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DBEFCD72282A12B900C0ABEA /* ReportReason */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DBEFCD70282A12B200C0ABEA /* ReportReasonViewController.swift */,
|
||||
DBEFCD73282A130400C0ABEA /* ReportReasonViewModel.swift */,
|
||||
DBEFCD7A282A162400C0ABEA /* ReportReasonView.swift */,
|
||||
);
|
||||
path = ReportReason;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DBEFCD77282A144D00C0ABEA /* Report */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5BB04FD4262E7AFF0043BFF6 /* ReportViewController.swift */,
|
||||
5B24BBD7262DB14800A9381B /* ReportViewModel.swift */,
|
||||
);
|
||||
path = Report;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DBEFCD7E282A2A3D00C0ABEA /* ReportServerRules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DBEFCD7C282A2A3B00C0ABEA /* ReportServerRulesViewController.swift */,
|
||||
DBEFCD7F282A2AA900C0ABEA /* ReportServerRulesViewModel.swift */,
|
||||
DBEFCD81282A2AB100C0ABEA /* ReportServerRulesView.swift */,
|
||||
);
|
||||
path = ReportServerRules;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DBF1D24F269DAF6100C1C08A /* SearchDetail */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -3873,6 +3921,7 @@
|
||||
2D364F7225E66D7500204FDC /* MastodonResendEmailViewController.swift in Sources */,
|
||||
DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */,
|
||||
DB02CDAB26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift in Sources */,
|
||||
DBEFCD80282A2AA900C0ABEA /* ReportServerRulesViewModel.swift in Sources */,
|
||||
DB0617FF27855D6C0030EE79 /* MastodonServerRulesViewModel+Diffable.swift in Sources */,
|
||||
DBB5255E2611F07A002F1F29 /* ProfileViewModel.swift in Sources */,
|
||||
DB0FCB982797F6BF006C02E2 /* UserTableViewCell+ViewModel.swift in Sources */,
|
||||
@ -3900,6 +3949,7 @@
|
||||
DB63F76F279A7D1100455B82 /* NotificationTableViewCell.swift in Sources */,
|
||||
DB297B1B2679FAE200704C90 /* PlaceholderImageCacheService.swift in Sources */,
|
||||
DB0FCB8C2796BF8D006C02E2 /* SearchViewModel+Diffable.swift in Sources */,
|
||||
DBEFCD76282A143F00C0ABEA /* ReportStatusViewController.swift in Sources */,
|
||||
2D8FCA082637EABB00137F46 /* APIService+FollowRequest.swift in Sources */,
|
||||
DBDFF1952805561700557A48 /* DiscoveryPostsViewModel+Diffable.swift in Sources */,
|
||||
DB03A795272A981400EE37C5 /* ContentSplitViewController.swift in Sources */,
|
||||
@ -4030,7 +4080,7 @@
|
||||
DB63F75C279956D000455B82 /* Persistence+Tag.swift in Sources */,
|
||||
2D84350525FF858100EECE90 /* UIScrollView.swift in Sources */,
|
||||
DB49A61F25FF32AA00B98345 /* EmojiService+CustomEmojiViewModel.swift in Sources */,
|
||||
5B24BBDB262DB14800A9381B /* ReportViewModel+Diffable.swift in Sources */,
|
||||
5B24BBDB262DB14800A9381B /* ReportStatusViewModel+Diffable.swift in Sources */,
|
||||
DB4F0968269ED8AD00D62E92 /* SearchHistoryTableHeaderView.swift in Sources */,
|
||||
0FB3D2FE25E4CB6400AAD544 /* OnboardingHeadlineTableViewCell.swift in Sources */,
|
||||
5DA732CC2629CEF500A92342 /* UIView+Remove.swift in Sources */,
|
||||
@ -4042,6 +4092,7 @@
|
||||
DBBF1DC92652538500E5B703 /* AutoCompleteSection.swift in Sources */,
|
||||
DB3E6FE72806A7A200B035AE /* DiscoveryItem.swift in Sources */,
|
||||
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */,
|
||||
DBEFCD79282A147000C0ABEA /* ReportStatusViewModel.swift in Sources */,
|
||||
DB7F48452620241000796008 /* ProfileHeaderViewModel.swift in Sources */,
|
||||
DB647C5926F1EA2700F7F82C /* WizardPreference.swift in Sources */,
|
||||
DB0A322E280EE9FD001729D2 /* DiscoveryIntroBannerView.swift in Sources */,
|
||||
@ -4092,7 +4143,7 @@
|
||||
DBA94434265CBB5300C537E1 /* ProfileFieldSection.swift in Sources */,
|
||||
DB336F28278D6EC70031E64B /* MastodonFieldContainer.swift in Sources */,
|
||||
DBF156E42702DB3F00EC00B7 /* HandleTapAction.swift in Sources */,
|
||||
DB98EB4727B0DFAA0082E365 /* ReportViewModel+State.swift in Sources */,
|
||||
DB98EB4727B0DFAA0082E365 /* ReportStatusViewModel+State.swift in Sources */,
|
||||
2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */,
|
||||
DB6B74F6272FBCDB00C70B6E /* FollowerListViewModel+State.swift in Sources */,
|
||||
DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */,
|
||||
@ -4150,6 +4201,7 @@
|
||||
DB6D9F8426358EEC008423CD /* SettingsItem.swift in Sources */,
|
||||
2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */,
|
||||
DBA465932696B495002B41DB /* APIService+WebFinger.swift in Sources */,
|
||||
DBEFCD7B282A162400C0ABEA /* ReportReasonView.swift in Sources */,
|
||||
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
|
||||
DB63F77B279ACAE500455B82 /* DataSourceFacade+Favorite.swift in Sources */,
|
||||
DB9D6BF825E4F5690051B173 /* NotificationViewController.swift in Sources */,
|
||||
@ -4164,6 +4216,7 @@
|
||||
DB4F097526A037F500D62E92 /* SearchHistoryViewModel.swift in Sources */,
|
||||
DB3EA8E9281B7A3700598866 /* DiscoveryCommunityViewModel.swift in Sources */,
|
||||
DB6180F826391D660018D199 /* MediaPreviewingViewController.swift in Sources */,
|
||||
DBEFCD71282A12B200C0ABEA /* ReportReasonViewController.swift in Sources */,
|
||||
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */,
|
||||
DB98EB5627B0FF1B0082E365 /* ReportViewControllerAppearance.swift in Sources */,
|
||||
DB938F1526241FDF00E5B6C1 /* APIService+Thread.swift in Sources */,
|
||||
@ -4200,6 +4253,7 @@
|
||||
2D7867192625B77500211898 /* NotificationItem.swift in Sources */,
|
||||
DB45FAB625CA5485005A8AC7 /* UIAlertController.swift in Sources */,
|
||||
DBE0821525CD382600FD6BBD /* MastodonRegisterViewController.swift in Sources */,
|
||||
DBEFCD74282A130400C0ABEA /* ReportReasonViewModel.swift in Sources */,
|
||||
2D5A3D0325CF8742002347D6 /* ControlContainableScrollViews.swift in Sources */,
|
||||
DB36679D268AB91B0027D07F /* ComposeStatusAttachmentTableViewCell.swift in Sources */,
|
||||
DB98336B25C9420100AD9700 /* APIService+App.swift in Sources */,
|
||||
@ -4287,6 +4341,7 @@
|
||||
DB0617EB277EF3820030EE79 /* GradientBorderView.swift in Sources */,
|
||||
DB789A1225F9F2CC0071ACA0 /* ComposeViewModel.swift in Sources */,
|
||||
DB63F74B279914A000455B82 /* FollowingListViewController+DataSourceProvider.swift in Sources */,
|
||||
DBEFCD7D282A2A3B00C0ABEA /* ReportServerRulesViewController.swift in Sources */,
|
||||
DBB525362611ECEB002F1F29 /* UserTimelineViewController.swift in Sources */,
|
||||
DB938F3326243D6200E5B6C1 /* TimelineTopLoaderTableViewCell.swift in Sources */,
|
||||
DB98EB4927B0F0CD0082E365 /* ReportStatusTableViewCell.swift in Sources */,
|
||||
@ -4297,6 +4352,7 @@
|
||||
DB98EB6527B216500082E365 /* ReportResultViewModel.swift in Sources */,
|
||||
DB4F096A269EDAD200D62E92 /* SearchResultViewModel+State.swift in Sources */,
|
||||
5BB04FF5262F0E6D0043BFF6 /* ReportSection.swift in Sources */,
|
||||
DBEFCD82282A2AB100C0ABEA /* ReportServerRulesView.swift in Sources */,
|
||||
DBA94436265CBB7400C537E1 /* ProfileFieldItem.swift in Sources */,
|
||||
DB023D2A27A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift in Sources */,
|
||||
DB98EB6027B10E150082E365 /* ReportCommentTableViewCell.swift in Sources */,
|
||||
|
@ -109,7 +109,7 @@
|
||||
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>28</integer>
|
||||
<integer>20</integer>
|
||||
</dict>
|
||||
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
@ -124,12 +124,12 @@
|
||||
<key>NotificationService.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>27</integer>
|
||||
<integer>19</integer>
|
||||
</dict>
|
||||
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>26</integer>
|
||||
<integer>21</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
@ -158,7 +158,7 @@ extension SceneCoordinator {
|
||||
case mastodonServerRules(viewModel: MastodonServerRulesViewModel)
|
||||
case mastodonConfirmEmail(viewModel: MastodonConfirmEmailViewModel)
|
||||
case mastodonResendEmail(viewModel: MastodonResendEmailViewModel)
|
||||
case mastodonWebView(viewModel:WebViewModel)
|
||||
case mastodonWebView(viewModel: WebViewModel)
|
||||
|
||||
// search
|
||||
case searchDetail(viewModel: SearchDetailViewModel)
|
||||
@ -184,6 +184,8 @@ extension SceneCoordinator {
|
||||
|
||||
// report
|
||||
case report(viewModel: ReportViewModel)
|
||||
case reportServerRules(viewModel: ReportServerRulesViewModel)
|
||||
case reportStatus(viewModel: ReportStatusViewModel)
|
||||
case reportSupplementary(viewModel: ReportSupplementaryViewModel)
|
||||
case reportResult(viewModel: ReportResultViewModel)
|
||||
|
||||
@ -447,6 +449,14 @@ private extension SceneCoordinator {
|
||||
let _viewController = ReportViewController()
|
||||
_viewController.viewModel = viewModel
|
||||
viewController = _viewController
|
||||
case .reportServerRules(let viewModel):
|
||||
let _viewController = ReportServerRulesViewController()
|
||||
_viewController.viewModel = viewModel
|
||||
viewController = _viewController
|
||||
case .reportStatus(let viewModel):
|
||||
let _viewController = ReportStatusViewController()
|
||||
_viewController.viewModel = viewModel
|
||||
viewController = _viewController
|
||||
case .reportSupplementary(let viewModel):
|
||||
let _viewController = ReportSupplementaryViewController()
|
||||
_viewController.viewModel = viewModel
|
||||
|
@ -229,6 +229,11 @@ extension MastodonConfirmEmailViewController {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - PanPopableViewController
|
||||
extension MastodonConfirmEmailViewController: PanPopableViewController {
|
||||
var isPanPopable: Bool { false }
|
||||
}
|
||||
|
||||
// MARK: - OnboardingViewControllerAppearance
|
||||
extension MastodonConfirmEmailViewController: OnboardingViewControllerAppearance { }
|
||||
|
||||
|
@ -197,9 +197,6 @@ struct MastodonRegisterView: View {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
struct WidthKey: PreferenceKey {
|
||||
|
165
Mastodon/Scene/Report/Report/ReportViewController.swift
Normal file
165
Mastodon/Scene/Report/Report/ReportViewController.swift
Normal file
@ -0,0 +1,165 @@
|
||||
//
|
||||
// ReportViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by ihugo on 2021/4/20.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
import CoreDataStack
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
|
||||
class ReportViewController: UIViewController, NeedsDependency, ReportViewControllerAppearance {
|
||||
|
||||
let logger = Logger(subsystem: "ReportViewController", category: "ViewController")
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
private var observations = Set<NSKeyValueObservation>()
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
var viewModel: ReportViewModel!
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setupAppearance()
|
||||
defer { setupNavigationBarBackgroundView() }
|
||||
|
||||
viewModel.reportReasonViewModel.delegate = self
|
||||
viewModel.reportServerRulesViewModel.delegate = self
|
||||
viewModel.reportStatusViewModel.delegate = self
|
||||
viewModel.reportSupplementaryViewModel.delegate = self
|
||||
|
||||
let reportReasonViewController = ReportReasonViewController()
|
||||
reportReasonViewController.context = context
|
||||
reportReasonViewController.coordinator = coordinator
|
||||
reportReasonViewController.viewModel = viewModel.reportReasonViewModel
|
||||
|
||||
addChild(reportReasonViewController)
|
||||
reportReasonViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(reportReasonViewController.view)
|
||||
reportReasonViewController.didMove(toParent: self)
|
||||
NSLayoutConstraint.activate([
|
||||
reportReasonViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
reportReasonViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
reportReasonViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
reportReasonViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UIAdaptivePresentationControllerDelegate
|
||||
extension ReportViewController: UIAdaptivePresentationControllerDelegate {
|
||||
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
|
||||
return viewModel.isReportSuccess
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReportReasonViewControllerDelegate
|
||||
extension ReportViewController: ReportReasonViewControllerDelegate {
|
||||
func reportReasonViewController(_ viewController: ReportReasonViewController, nextButtonPressed button: UIButton) {
|
||||
guard let reason = viewController.viewModel.selectReason else { return }
|
||||
switch reason {
|
||||
case .violateRule:
|
||||
coordinator.present(
|
||||
scene: .reportServerRules(viewModel: viewModel.reportServerRulesViewModel),
|
||||
from: self,
|
||||
transition: .show
|
||||
)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReportServerRulesViewControllerDelegate
|
||||
extension ReportViewController: ReportServerRulesViewControllerDelegate {
|
||||
func reportServerRulesViewController(_ viewController: ReportServerRulesViewController, nextButtonPressed button: UIButton) {
|
||||
if viewController.viewModel.isDislike {
|
||||
|
||||
} else if viewController.viewModel.selectRule != nil {
|
||||
coordinator.present(
|
||||
scene: .reportStatus(viewModel: viewModel.reportStatusViewModel),
|
||||
from: self,
|
||||
transition: .show
|
||||
)
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReportStatusViewControllerDelegate
|
||||
extension ReportViewController: ReportStatusViewControllerDelegate {
|
||||
func reportStatusViewController(_ viewController: ReportStatusViewController, skipButtonDidPressed button: UIButton) {
|
||||
coordinateToReportSupplementary()
|
||||
}
|
||||
|
||||
func reportStatusViewController(_ viewController: ReportStatusViewController, nextButtonDidPressed button: UIButton) {
|
||||
coordinateToReportSupplementary()
|
||||
}
|
||||
|
||||
private func coordinateToReportSupplementary() {
|
||||
coordinator.present(
|
||||
scene: .reportSupplementary(viewModel: viewModel.reportSupplementaryViewModel),
|
||||
from: self,
|
||||
transition: .show
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReportSupplementaryViewControllerDelegate
|
||||
extension ReportViewController: ReportSupplementaryViewControllerDelegate {
|
||||
func reportSupplementaryViewController(_ viewController: ReportSupplementaryViewController, skipButtonDidPressed button: UIButton) {
|
||||
report()
|
||||
}
|
||||
|
||||
func reportSupplementaryViewController(_ viewController: ReportSupplementaryViewController, nextButtonDidPressed button: UIButton) {
|
||||
report()
|
||||
}
|
||||
|
||||
private func report() {
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let _ = try await viewModel.report()
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): report success")
|
||||
|
||||
let reportResultViewModel = ReportResultViewModel(
|
||||
context: context,
|
||||
user: viewModel.user
|
||||
)
|
||||
|
||||
coordinator.present(
|
||||
scene: .reportResult(viewModel: reportResultViewModel),
|
||||
from: self,
|
||||
transition: .show
|
||||
)
|
||||
|
||||
} catch {
|
||||
let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert)
|
||||
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
|
||||
alertController.addAction(okAction)
|
||||
self.coordinator.present(
|
||||
scene: .alertController(alertController: alertController),
|
||||
from: nil,
|
||||
transition: .alertController(animated: true, completion: nil)
|
||||
)
|
||||
}
|
||||
} // end Task
|
||||
}
|
||||
|
||||
}
|
176
Mastodon/Scene/Report/Report/ReportViewModel.swift
Normal file
176
Mastodon/Scene/Report/Report/ReportViewModel.swift
Normal file
@ -0,0 +1,176 @@
|
||||
//
|
||||
// ReportViewModel.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by ihugo on 2021/4/19.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import Foundation
|
||||
import GameplayKit
|
||||
import MastodonSDK
|
||||
import OrderedCollections
|
||||
import os.log
|
||||
import UIKit
|
||||
|
||||
class ReportViewModel {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
let reportReasonViewModel: ReportReasonViewModel
|
||||
let reportServerRulesViewModel: ReportServerRulesViewModel
|
||||
let reportStatusViewModel: ReportStatusViewModel
|
||||
let reportSupplementaryViewModel: ReportSupplementaryViewModel
|
||||
|
||||
// input
|
||||
let context: AppContext
|
||||
let user: ManagedObjectRecord<MastodonUser>
|
||||
let status: ManagedObjectRecord<Status>?
|
||||
|
||||
// output
|
||||
@Published var isReporting = false
|
||||
@Published var isReportSuccess = false
|
||||
|
||||
init(
|
||||
context: AppContext,
|
||||
user: ManagedObjectRecord<MastodonUser>,
|
||||
status: ManagedObjectRecord<Status>?
|
||||
) {
|
||||
self.context = context
|
||||
self.user = user
|
||||
self.status = status
|
||||
self.reportReasonViewModel = ReportReasonViewModel(context: context)
|
||||
self.reportServerRulesViewModel = ReportServerRulesViewModel(context: context)
|
||||
self.reportStatusViewModel = ReportStatusViewModel(context: context, user: user, status: status)
|
||||
self.reportSupplementaryViewModel = ReportSupplementaryViewModel(context: context, user: user)
|
||||
// end init
|
||||
|
||||
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||
return
|
||||
}
|
||||
|
||||
// setup reason viewModel
|
||||
if status != nil {
|
||||
// TODO: i18n
|
||||
reportReasonViewModel.headline = "What’s wrong with post?"
|
||||
} else {
|
||||
Task { @MainActor in
|
||||
let managedObjectContext = context.managedObjectContext
|
||||
let _username: String? = try? await managedObjectContext.perform {
|
||||
let user = user.object(in: managedObjectContext)
|
||||
return user?.acctWithDomain
|
||||
}
|
||||
if let username = _username {
|
||||
reportReasonViewModel.headline = "What’s wrong with @\(username)?"
|
||||
} else {
|
||||
reportReasonViewModel.headline = "What’s wrong with this account?"
|
||||
}
|
||||
} // end Task
|
||||
}
|
||||
|
||||
// bind server rules
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let response = try await context.apiService.instance(domain: authenticationBox.domain)
|
||||
.timeout(3, scheduler: DispatchQueue.main)
|
||||
.singleOutput()
|
||||
let rules = response.value.rules ?? []
|
||||
reportReasonViewModel.serverRules = rules
|
||||
reportServerRulesViewModel.serverRules = rules
|
||||
} catch {
|
||||
reportReasonViewModel.serverRules = []
|
||||
reportServerRulesViewModel.serverRules = []
|
||||
}
|
||||
} // end Task
|
||||
|
||||
$isReporting
|
||||
.assign(to: &reportSupplementaryViewModel.$isBusy)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportViewModel {
|
||||
@MainActor
|
||||
func report() async throws {
|
||||
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value,
|
||||
!isReporting
|
||||
else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
let managedObjectContext = context.managedObjectContext
|
||||
let _query: Mastodon.API.Reports.FileReportQuery? = try await managedObjectContext.perform {
|
||||
guard let user = self.user.object(in: managedObjectContext) else { return nil }
|
||||
let statusIDs: [Status.ID]? = {
|
||||
if self.reportStatusViewModel.isSkip {
|
||||
let _id: Status.ID? = self.reportStatusViewModel.status.flatMap { record -> Status.ID? in
|
||||
guard let status = record.object(in: managedObjectContext) else { return nil }
|
||||
return status.id
|
||||
}
|
||||
return _id.flatMap { [$0] }
|
||||
} else {
|
||||
return self.reportStatusViewModel.selectStatuses.compactMap { record -> Status.ID? in
|
||||
guard let status = record.object(in: managedObjectContext) else { return nil }
|
||||
return status.id
|
||||
}
|
||||
}
|
||||
}()
|
||||
let comment: String? = {
|
||||
var suffixes: [String] = []
|
||||
let content: String?
|
||||
|
||||
if let reason = self.reportReasonViewModel.selectReason {
|
||||
switch reason {
|
||||
case .spam:
|
||||
suffixes.append(reason.rawValue)
|
||||
case .violateRule:
|
||||
suffixes.append(reason.rawValue)
|
||||
if let rule = self.reportServerRulesViewModel.selectRule {
|
||||
suffixes.append(rule.text)
|
||||
}
|
||||
|
||||
case .dislike, .other:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
content = self.reportSupplementaryViewModel.isSkip ? nil : self.reportSupplementaryViewModel.commentContext.comment
|
||||
|
||||
let suffix: String? = {
|
||||
let text = suffixes.joined(separator: ". ")
|
||||
guard !text.isEmpty else { return nil }
|
||||
return "<" + text + ">"
|
||||
}()
|
||||
|
||||
let comment = [content, suffix]
|
||||
.compactMap { $0 }
|
||||
.joined(separator: " ")
|
||||
return comment.isEmpty ? nil : comment
|
||||
}()
|
||||
return Mastodon.API.Reports.FileReportQuery(
|
||||
accountID: user.id,
|
||||
statusIDs: statusIDs,
|
||||
comment: comment,
|
||||
forward: true
|
||||
)
|
||||
}
|
||||
|
||||
guard let query = _query else { return }
|
||||
|
||||
do {
|
||||
isReporting = true
|
||||
try await Task.sleep(nanoseconds: .second * 3)
|
||||
// let _ = try await context.apiService.report(
|
||||
// query: query,
|
||||
// authenticationBox: authenticationBox
|
||||
// )
|
||||
isReportSuccess = true
|
||||
} catch {
|
||||
isReporting = false
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
113
Mastodon/Scene/Report/ReportReason/ReportReasonView.swift
Normal file
113
Mastodon/Scene/Report/ReportReason/ReportReasonView.swift
Normal file
@ -0,0 +1,113 @@
|
||||
//
|
||||
// ReportReasonView.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK on 2022-5-10.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import MastodonLocalization
|
||||
import MastodonSDK
|
||||
import MastodonAsset
|
||||
|
||||
struct ReportReasonView: View {
|
||||
|
||||
@ObservedObject var viewModel: ReportReasonViewModel
|
||||
|
||||
// TODO: i18n
|
||||
var body: some View {
|
||||
ScrollView(.vertical) {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Step 1 of 4")
|
||||
.foregroundColor(Color(Asset.Colors.Label.secondary.color))
|
||||
.font(Font(UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) as CTFont))
|
||||
Text(viewModel.headline)
|
||||
.foregroundColor(Color(Asset.Colors.Label.primary.color))
|
||||
.font(Font(UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 28, weight: .bold)) as CTFont))
|
||||
Text("Select the best match")
|
||||
.foregroundColor(Color(Asset.Colors.Label.secondary.color))
|
||||
.font(Font(UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) as CTFont))
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
|
||||
VStack(spacing: 16) {
|
||||
if let serverRules = viewModel.serverRules {
|
||||
ForEach(ReportReasonViewModel.Reason.allCases, id: \.self) { reason in
|
||||
switch reason {
|
||||
case .violateRule where serverRules.isEmpty:
|
||||
EmptyView()
|
||||
default:
|
||||
ReportReasonRowView(reason: reason, isSelect: reason == viewModel.selectReason)
|
||||
.background(
|
||||
Color(viewModel.backgroundColor)
|
||||
)
|
||||
.onTapGesture {
|
||||
viewModel.selectReason = reason
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.transition(.opacity)
|
||||
.animation(.easeInOut)
|
||||
|
||||
Spacer()
|
||||
.frame(minHeight: viewModel.bottomPaddingHeight)
|
||||
}
|
||||
.background(
|
||||
Color(viewModel.backgroundColor)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct ReportReasonRowView: View {
|
||||
|
||||
var reason: ReportReasonViewModel.Reason
|
||||
var isSelect: Bool
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 14) {
|
||||
Image(systemName: isSelect ? "checkmark.circle.fill" : "circle")
|
||||
.resizable()
|
||||
.frame(width: 28, height: 28, alignment: .center)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(reason.title)
|
||||
.foregroundColor(Color(Asset.Colors.Label.primary.color))
|
||||
.font(.headline)
|
||||
Text(reason.subtitle)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color(Asset.Colors.Label.secondary.color))
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct ReportReasonView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NavigationView {
|
||||
ReportReasonView(viewModel: ReportReasonViewModel(context: .shared))
|
||||
.navigationBarTitle(Text(""))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
NavigationView {
|
||||
ReportReasonView(viewModel: ReportReasonViewModel(context: .shared))
|
||||
.navigationBarTitle(Text(""))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,116 @@
|
||||
//
|
||||
// ReportReasonViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK on 2022-5-10.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import MastodonUI
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
|
||||
protocol ReportReasonViewControllerDelegate: AnyObject {
|
||||
func reportReasonViewController(_ viewController: ReportReasonViewController, nextButtonPressed button: UIButton)
|
||||
}
|
||||
|
||||
final class ReportReasonViewController: UIViewController, NeedsDependency, ReportViewControllerAppearance {
|
||||
|
||||
let logger = Logger(subsystem: "ReportReasonViewController", category: "ViewController")
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
private var observations = Set<NSKeyValueObservation>()
|
||||
|
||||
var viewModel: ReportReasonViewModel!
|
||||
private(set) lazy var reportReasonView = ReportReasonView(viewModel: viewModel)
|
||||
|
||||
lazy var cancelBarButtonItem = UIBarButtonItem(
|
||||
barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(ReportReasonViewController.cancelBarButtonItemDidPressed(_:))
|
||||
)
|
||||
|
||||
let navigationActionView: NavigationActionView = {
|
||||
let navigationActionView = NavigationActionView()
|
||||
navigationActionView.backgroundColor = Asset.Scene.Onboarding.background.color
|
||||
navigationActionView.hidesBackButton = true
|
||||
return navigationActionView
|
||||
}()
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportReasonViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setupAppearance()
|
||||
defer { setupNavigationBarBackgroundView() }
|
||||
|
||||
navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
|
||||
let hostingViewController = UIHostingController(rootView: reportReasonView)
|
||||
hostingViewController.view.preservesSuperviewLayoutMargins = true
|
||||
addChild(hostingViewController)
|
||||
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(hostingViewController.view)
|
||||
NSLayoutConstraint.activate([
|
||||
hostingViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
hostingViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
hostingViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
hostingViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
|
||||
navigationActionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(navigationActionView)
|
||||
defer {
|
||||
view.bringSubviewToFront(navigationActionView)
|
||||
}
|
||||
NSLayoutConstraint.activate([
|
||||
navigationActionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
navigationActionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
view.bottomAnchor.constraint(equalTo: navigationActionView.bottomAnchor),
|
||||
])
|
||||
|
||||
navigationActionView
|
||||
.observe(\.bounds, options: [.initial, .new]) { [weak self] navigationActionView, _ in
|
||||
guard let self = self else { return }
|
||||
let inset = navigationActionView.frame.height
|
||||
self.viewModel.bottomPaddingHeight = inset
|
||||
}
|
||||
.store(in: &observations)
|
||||
|
||||
viewModel.$selectReason
|
||||
.map { $0 != nil }
|
||||
.assign(to: \.isEnabled, on: navigationActionView.nextButton)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
navigationActionView.nextButton.addTarget(self, action: #selector(ReportReasonViewController.nextButtonPressed(_:)), for: .touchUpInside)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportReasonViewController {
|
||||
|
||||
@objc private func cancelBarButtonItemDidPressed(_ sender: UIBarButtonItem) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func nextButtonPressed(_ sender: UIButton) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
|
||||
assert(viewModel.delegate != nil)
|
||||
viewModel.delegate?.reportReasonViewController(self, nextButtonPressed: sender)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
//
|
||||
// ReportReasonViewModel.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK on 2022-5-10.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import MastodonAsset
|
||||
import MastodonSDK
|
||||
|
||||
final class ReportReasonViewModel: ObservableObject {
|
||||
|
||||
weak var delegate: ReportReasonViewControllerDelegate?
|
||||
|
||||
// input
|
||||
let context: AppContext
|
||||
|
||||
@Published var headline = "What's wrong with this account?"
|
||||
@Published var serverRules: [Mastodon.Entity.Instance.Rule]?
|
||||
|
||||
@Published var bottomPaddingHeight: CGFloat = .zero
|
||||
@Published var backgroundColor: UIColor = Asset.Scene.Report.background.color
|
||||
|
||||
// output
|
||||
@Published var selectReason: Reason?
|
||||
|
||||
init(context: AppContext) {
|
||||
self.context = context
|
||||
// end init
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportReasonViewModel {
|
||||
enum Reason: Hashable, CaseIterable {
|
||||
case dislike
|
||||
case spam
|
||||
case violateRule
|
||||
case other
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .dislike:
|
||||
return "I don’t like it"
|
||||
case .spam:
|
||||
return "It’s spam"
|
||||
case .violateRule:
|
||||
return "It violates server rules"
|
||||
case .other:
|
||||
return "It’s something else"
|
||||
}
|
||||
}
|
||||
|
||||
var subtitle: String {
|
||||
switch self {
|
||||
case .dislike:
|
||||
return "It is not something you want to see"
|
||||
case .spam:
|
||||
return "Malicious links, fake engagement, or repetetive replies"
|
||||
case .violateRule:
|
||||
return "You are aware that it breaks specific rules"
|
||||
case .other:
|
||||
return "The issue does not fit into other categories"
|
||||
}
|
||||
}
|
||||
|
||||
// do not i18n this
|
||||
var rawValue: String {
|
||||
switch self {
|
||||
case .dislike:
|
||||
return "I don’t like it"
|
||||
case .spam:
|
||||
return "It’s spam"
|
||||
case .violateRule:
|
||||
return "It violates server rules"
|
||||
case .other:
|
||||
return "It’s something else"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,12 @@ final class ReportResultViewController: UIViewController, NeedsDependency, Repor
|
||||
|
||||
var viewModel: ReportResultViewModel!
|
||||
|
||||
lazy var doneBarButtonItem = UIBarButtonItem(
|
||||
barButtonSystemItem: .done,
|
||||
target: self,
|
||||
action: #selector(ReportResultViewController.doneBarButtonItemDidPressed(_:))
|
||||
)
|
||||
|
||||
let tableView: UITableView = {
|
||||
let tableView = ControlContainableTableView()
|
||||
tableView.backgroundColor = Asset.Scene.Report.background.color
|
||||
@ -60,6 +66,7 @@ extension ReportResultViewController {
|
||||
defer { setupNavigationBarBackgroundView() }
|
||||
|
||||
navigationItem.hidesBackButton = true
|
||||
navigationItem.rightBarButtonItem = doneBarButtonItem
|
||||
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(tableView)
|
||||
@ -102,6 +109,10 @@ extension ReportResultViewController {
|
||||
}
|
||||
|
||||
extension ReportResultViewController {
|
||||
|
||||
@objc func doneBarButtonItemDidPressed(_ sender: UIButton) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func nextButtonDidPressed(_ sender: UIButton) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
@ -111,3 +122,8 @@ extension ReportResultViewController {
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension ReportResultViewController: UITableViewDelegate { }
|
||||
|
||||
// MARK: - PanPopableViewController
|
||||
extension ReportResultViewController: PanPopableViewController {
|
||||
var isPanPopable: Bool { false }
|
||||
}
|
||||
|
@ -0,0 +1,116 @@
|
||||
//
|
||||
// ReportServerRulesView.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK on 2022-5-10.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import MastodonLocalization
|
||||
import MastodonSDK
|
||||
import MastodonAsset
|
||||
|
||||
struct ReportServerRulesView: View {
|
||||
|
||||
@ObservedObject var viewModel: ReportServerRulesViewModel
|
||||
|
||||
// TODO: i18n
|
||||
var body: some View {
|
||||
ScrollView(.vertical) {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Step 2 of 4")
|
||||
.foregroundColor(Color(Asset.Colors.Label.secondary.color))
|
||||
.font(Font(UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) as CTFont))
|
||||
Text(viewModel.headline)
|
||||
.foregroundColor(Color(Asset.Colors.Label.primary.color))
|
||||
.font(Font(UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 28, weight: .bold)) as CTFont))
|
||||
Text("Select all that apply")
|
||||
.foregroundColor(Color(Asset.Colors.Label.secondary.color))
|
||||
.font(Font(UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) as CTFont))
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
|
||||
VStack(spacing: 32) {
|
||||
ForEach(viewModel.serverRules, id: \.self) { rule in
|
||||
ReportServerRulesRowView(
|
||||
title: rule.text,
|
||||
isSelect: rule == viewModel.selectRule
|
||||
)
|
||||
.background(
|
||||
Color(viewModel.backgroundColor)
|
||||
)
|
||||
.onTapGesture {
|
||||
viewModel.selectRule = rule
|
||||
viewModel.isDislike = false
|
||||
}
|
||||
}
|
||||
ReportServerRulesRowView(
|
||||
title: "I just don’t like it",
|
||||
isSelect: viewModel.isDislike
|
||||
)
|
||||
.background(
|
||||
Color(viewModel.backgroundColor)
|
||||
)
|
||||
.onTapGesture {
|
||||
viewModel.selectRule = nil
|
||||
viewModel.isDislike = true
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.transition(.opacity)
|
||||
.animation(.easeInOut)
|
||||
|
||||
Spacer()
|
||||
.frame(minHeight: viewModel.bottomPaddingHeight)
|
||||
}
|
||||
.background(
|
||||
Color(viewModel.backgroundColor)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct ReportServerRulesRowView: View {
|
||||
|
||||
var title: String
|
||||
var isSelect: Bool
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 14) {
|
||||
Image(systemName: isSelect ? "checkmark.circle.fill" : "circle")
|
||||
.resizable()
|
||||
.frame(width: 28, height: 28, alignment: .center)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(title)
|
||||
.foregroundColor(Color(Asset.Colors.Label.primary.color))
|
||||
.font(.headline)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct ReportServerRulesView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NavigationView {
|
||||
ReportServerRulesView(viewModel: ReportServerRulesViewModel(context: .shared))
|
||||
.navigationBarTitle(Text(""))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
NavigationView {
|
||||
ReportServerRulesView(viewModel: ReportServerRulesViewModel(context: .shared))
|
||||
.navigationBarTitle(Text(""))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,117 @@
|
||||
//
|
||||
// ReportServerRulesViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK on 2022-5-10.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import MastodonUI
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
|
||||
protocol ReportServerRulesViewControllerDelegate: AnyObject {
|
||||
func reportServerRulesViewController(_ viewController: ReportServerRulesViewController, nextButtonPressed button: UIButton)
|
||||
}
|
||||
|
||||
final class ReportServerRulesViewController: UIViewController, NeedsDependency, ReportViewControllerAppearance {
|
||||
|
||||
let logger = Logger(subsystem: "ReportReasonViewController", category: "ViewController")
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
private var observations = Set<NSKeyValueObservation>()
|
||||
|
||||
var viewModel: ReportServerRulesViewModel!
|
||||
private(set) lazy var reportServerRulesView = ReportServerRulesView(viewModel: viewModel)
|
||||
|
||||
lazy var cancelBarButtonItem = UIBarButtonItem(
|
||||
barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(ReportServerRulesViewController.cancelBarButtonItemDidPressed(_:))
|
||||
)
|
||||
|
||||
let navigationActionView: NavigationActionView = {
|
||||
let navigationActionView = NavigationActionView()
|
||||
navigationActionView.backgroundColor = Asset.Scene.Onboarding.background.color
|
||||
navigationActionView.hidesBackButton = true
|
||||
return navigationActionView
|
||||
}()
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportServerRulesViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setupAppearance()
|
||||
defer { setupNavigationBarBackgroundView() }
|
||||
|
||||
let hostingViewController = UIHostingController(rootView: reportServerRulesView)
|
||||
hostingViewController.view.preservesSuperviewLayoutMargins = true
|
||||
addChild(hostingViewController)
|
||||
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(hostingViewController.view)
|
||||
NSLayoutConstraint.activate([
|
||||
hostingViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
hostingViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
hostingViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
hostingViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
|
||||
navigationActionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(navigationActionView)
|
||||
defer {
|
||||
view.bringSubviewToFront(navigationActionView)
|
||||
}
|
||||
NSLayoutConstraint.activate([
|
||||
navigationActionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
navigationActionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
view.bottomAnchor.constraint(equalTo: navigationActionView.bottomAnchor),
|
||||
])
|
||||
|
||||
navigationActionView
|
||||
.observe(\.bounds, options: [.initial, .new]) { [weak self] navigationActionView, _ in
|
||||
guard let self = self else { return }
|
||||
let inset = navigationActionView.frame.height
|
||||
self.viewModel.bottomPaddingHeight = inset
|
||||
}
|
||||
.store(in: &observations)
|
||||
|
||||
Publishers.CombineLatest(
|
||||
viewModel.$selectRule,
|
||||
viewModel.$isDislike
|
||||
)
|
||||
.map { $0 != nil || $1 }
|
||||
.assign(to: \.isEnabled, on: navigationActionView.nextButton)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
navigationActionView.nextButton.addTarget(self, action: #selector(ReportServerRulesViewController.nextButtonPressed(_:)), for: .touchUpInside)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportServerRulesViewController {
|
||||
|
||||
@objc private func cancelBarButtonItemDidPressed(_ sender: UIBarButtonItem) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func nextButtonPressed(_ sender: UIButton) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
|
||||
assert(viewModel.delegate != nil)
|
||||
viewModel.delegate?.reportServerRulesViewController(self, nextButtonPressed: sender)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
//
|
||||
// ReportServerRulesViewModel.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK on 2022-5-10.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import MastodonAsset
|
||||
import MastodonSDK
|
||||
|
||||
final class ReportServerRulesViewModel: ObservableObject {
|
||||
|
||||
weak var delegate: ReportServerRulesViewControllerDelegate?
|
||||
|
||||
// input
|
||||
let context: AppContext
|
||||
|
||||
@Published var headline = "Which rules are being violated?"
|
||||
@Published var serverRules: [Mastodon.Entity.Instance.Rule] = []
|
||||
|
||||
@Published var bottomPaddingHeight: CGFloat = .zero
|
||||
@Published var backgroundColor: UIColor = Asset.Scene.Report.background.color
|
||||
|
||||
// output
|
||||
@Published var selectRule: Mastodon.Entity.Instance.Rule?
|
||||
@Published var isDislike: Bool = false
|
||||
|
||||
init(context: AppContext) {
|
||||
self.context = context
|
||||
// end init
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
//
|
||||
// ReportViewController.swift
|
||||
// ReportStatusViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by ihugo on 2021/4/20.
|
||||
// Created by MainasuK on 2022-5-10.
|
||||
//
|
||||
|
||||
import os.log
|
||||
@ -12,21 +12,29 @@ import CoreDataStack
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
|
||||
class ReportViewController: UIViewController, NeedsDependency, ReportViewControllerAppearance {
|
||||
protocol ReportStatusViewControllerDelegate: AnyObject {
|
||||
func reportStatusViewController(_ viewController: ReportStatusViewController, skipButtonDidPressed button: UIButton)
|
||||
func reportStatusViewController(_ viewController: ReportStatusViewController, nextButtonDidPressed button: UIButton)
|
||||
}
|
||||
|
||||
class ReportStatusViewController: UIViewController, NeedsDependency, ReportViewControllerAppearance {
|
||||
|
||||
let logger = Logger(subsystem: "ReportStatusViewController", category: "ViewController")
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
private var observations = Set<NSKeyValueObservation>()
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
var viewModel: ReportViewModel!
|
||||
|
||||
var viewModel: ReportStatusViewModel!
|
||||
|
||||
// MAKK: - UI
|
||||
|
||||
lazy var cancelBarButtonItem = UIBarButtonItem(
|
||||
barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(ReportViewController.cancelBarButtonItemDidPressed(_:))
|
||||
action: #selector(ReportStatusViewController.cancelBarButtonItemDidPressed(_:))
|
||||
)
|
||||
|
||||
let tableView: UITableView = {
|
||||
@ -58,7 +66,7 @@ class ReportViewController: UIViewController, NeedsDependency, ReportViewControl
|
||||
|
||||
}
|
||||
|
||||
extension ReportViewController {
|
||||
extension ReportStatusViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
@ -67,7 +75,7 @@ extension ReportViewController {
|
||||
defer { setupNavigationBarBackgroundView() }
|
||||
|
||||
navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
|
||||
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(tableView)
|
||||
NSLayoutConstraint.activate([
|
||||
@ -109,7 +117,7 @@ extension ReportViewController {
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
guard self.view.window != nil else { return }
|
||||
self.viewModel.stateMachine.enter(ReportViewModel.State.Loading.self)
|
||||
self.viewModel.stateMachine.enter(ReportStatusViewModel.State.Loading.self)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
@ -118,56 +126,38 @@ extension ReportViewController {
|
||||
.assign(to: \.isEnabled, on: navigationActionView.nextButton)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
navigationActionView.backButton.addTarget(self, action: #selector(ReportViewController.skipButtonDidPressed(_:)), for: .touchUpInside)
|
||||
navigationActionView.nextButton.addTarget(self, action: #selector(ReportViewController.nextButtonDidPressed(_:)), for: .touchUpInside)
|
||||
navigationActionView.backButton.addTarget(self, action: #selector(ReportStatusViewController.skipButtonDidPressed(_:)), for: .touchUpInside)
|
||||
navigationActionView.nextButton.addTarget(self, action: #selector(ReportStatusViewController.nextButtonDidPressed(_:)), for: .touchUpInside)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportViewController {
|
||||
|
||||
extension ReportStatusViewController {
|
||||
|
||||
@objc private func cancelBarButtonItemDidPressed(_ sender: UIBarButtonItem) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func skipButtonDidPressed(_ sender: UIButton) {
|
||||
var selectStatuses: [ManagedObjectRecord<Status>] = []
|
||||
if let selectStatus = viewModel.status {
|
||||
selectStatuses.append(selectStatus)
|
||||
}
|
||||
@objc private func skipButtonDidPressed(_ sender: UIButton) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
|
||||
let reportSupplementaryViewModel = ReportSupplementaryViewModel(
|
||||
context: context,
|
||||
user: viewModel.user,
|
||||
selectStatuses: selectStatuses
|
||||
)
|
||||
coordinator.present(
|
||||
scene: .reportSupplementary(viewModel: reportSupplementaryViewModel),
|
||||
from: self,
|
||||
transition: .show
|
||||
)
|
||||
assert(viewModel.delegate != nil)
|
||||
viewModel.isSkip = true
|
||||
viewModel.delegate?.reportStatusViewController(self, skipButtonDidPressed: sender)
|
||||
}
|
||||
|
||||
@objc func nextButtonDidPressed(_ sender: UIButton) {
|
||||
let selectStatuses = Array(viewModel.selectStatuses)
|
||||
guard !selectStatuses.isEmpty else { return }
|
||||
@objc private func nextButtonDidPressed(_ sender: UIButton) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
|
||||
let reportSupplementaryViewModel = ReportSupplementaryViewModel(
|
||||
context: context,
|
||||
user: viewModel.user,
|
||||
selectStatuses: selectStatuses
|
||||
)
|
||||
coordinator.present(
|
||||
scene: .reportSupplementary(viewModel: reportSupplementaryViewModel),
|
||||
from: self,
|
||||
transition: .show
|
||||
)
|
||||
assert(viewModel.delegate != nil)
|
||||
viewModel.isSkip = false
|
||||
viewModel.delegate?.reportStatusViewController(self, nextButtonDidPressed: sender)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension ReportViewController: UITableViewDelegate {
|
||||
extension ReportStatusViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
||||
guard let item = viewModel.diffableDataSource?.itemIdentifier(for: indexPath),
|
||||
case .status = item
|
||||
@ -214,7 +204,7 @@ extension ReportViewController: UITableViewDelegate {
|
||||
}
|
||||
|
||||
// MARK: - UIAdaptivePresentationControllerDelegate
|
||||
extension ReportViewController: UIAdaptivePresentationControllerDelegate {
|
||||
extension ReportStatusViewController: UIAdaptivePresentationControllerDelegate {
|
||||
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
|
||||
return false
|
||||
}
|
@ -12,11 +12,11 @@ import CoreDataStack
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
|
||||
extension ReportViewModel {
|
||||
extension ReportStatusViewModel {
|
||||
|
||||
static let reportItemHeaderContext = ReportItem.HeaderContext(
|
||||
primaryLabelText: L10n.Scene.Report.content1,
|
||||
secondaryLabelText: L10n.Scene.Report.step1
|
||||
secondaryLabelText: "Step 3 of 4"
|
||||
)
|
||||
|
||||
func setupDiffableDataSource(
|
||||
@ -41,7 +41,7 @@ extension ReportViewModel {
|
||||
var snapshot = NSDiffableDataSourceSnapshot<ReportSection, ReportItem>()
|
||||
snapshot.appendSections([.main])
|
||||
|
||||
snapshot.appendItems([.header(context: ReportViewModel.reportItemHeaderContext)], toSection: .main)
|
||||
snapshot.appendItems([.header(context: ReportStatusViewModel.reportItemHeaderContext)], toSection: .main)
|
||||
|
||||
let items = records.map { ReportItem.status(record: $0) }
|
||||
snapshot.appendItems(items, toSection: .main)
|
@ -12,7 +12,7 @@ import CoreData
|
||||
import CoreDataStack
|
||||
import GameplayKit
|
||||
|
||||
extension ReportViewModel {
|
||||
extension ReportStatusViewModel {
|
||||
class State: GKState {
|
||||
|
||||
let logger = Logger(subsystem: "ReportViewModel.State", category: "StateMachine")
|
||||
@ -23,15 +23,15 @@ extension ReportViewModel {
|
||||
String(describing: Self.self)
|
||||
}
|
||||
|
||||
weak var viewModel: ReportViewModel?
|
||||
weak var viewModel: ReportStatusViewModel?
|
||||
|
||||
init(viewModel: ReportViewModel) {
|
||||
init(viewModel: ReportStatusViewModel) {
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
override func didEnter(from previousState: GKState?) {
|
||||
super.didEnter(from: previousState)
|
||||
let previousState = previousState as? ReportViewModel.State
|
||||
let previousState = previousState as? ReportStatusViewModel.State
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [\(self.id.uuidString)] enter \(self.name), previous: \(previousState?.name ?? "<nil>")")
|
||||
}
|
||||
|
||||
@ -46,8 +46,8 @@ extension ReportViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
extension ReportViewModel.State {
|
||||
class Initial: ReportViewModel.State {
|
||||
extension ReportStatusViewModel.State {
|
||||
class Initial: ReportStatusViewModel.State {
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
guard let _ = viewModel else { return false }
|
||||
switch stateClass {
|
||||
@ -59,7 +59,7 @@ extension ReportViewModel.State {
|
||||
}
|
||||
}
|
||||
|
||||
class Loading: ReportViewModel.State {
|
||||
class Loading: ReportStatusViewModel.State {
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
switch stateClass {
|
||||
case is Fail.Type:
|
||||
@ -128,7 +128,7 @@ extension ReportViewModel.State {
|
||||
}
|
||||
}
|
||||
|
||||
class Fail: ReportViewModel.State {
|
||||
class Fail: ReportStatusViewModel.State {
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
switch stateClass {
|
||||
case is Loading.Type:
|
||||
@ -139,7 +139,7 @@ extension ReportViewModel.State {
|
||||
}
|
||||
}
|
||||
|
||||
class Idle: ReportViewModel.State {
|
||||
class Idle: ReportStatusViewModel.State {
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
switch stateClass {
|
||||
case is Loading.Type:
|
||||
@ -150,7 +150,7 @@ extension ReportViewModel.State {
|
||||
}
|
||||
}
|
||||
|
||||
class NoMore: ReportViewModel.State {
|
||||
class NoMore: ReportStatusViewModel.State {
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
return false
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
//
|
||||
// ReportViewModel.swift
|
||||
// ReportStatusViewModel.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by ihugo on 2021/4/19.
|
||||
// Created by MainasuK on 2022-5-10.
|
||||
//
|
||||
|
||||
import Combine
|
||||
@ -15,10 +15,12 @@ import OrderedCollections
|
||||
import os.log
|
||||
import UIKit
|
||||
|
||||
class ReportViewModel {
|
||||
class ReportStatusViewModel {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
weak var delegate: ReportStatusViewControllerDelegate?
|
||||
|
||||
// input
|
||||
let context: AppContext
|
||||
let user: ManagedObjectRecord<MastodonUser>
|
||||
@ -26,6 +28,7 @@ class ReportViewModel {
|
||||
let statusFetchedResultsController: StatusFetchedResultsController
|
||||
let listBatchFetchViewModel = ListBatchFetchViewModel()
|
||||
|
||||
@Published var isSkip = false
|
||||
@Published var selectStatuses = OrderedSet<ManagedObjectRecord<Status>>()
|
||||
|
||||
// output
|
@ -11,20 +11,25 @@ import Combine
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
|
||||
protocol ReportSupplementaryViewControllerDelegate: AnyObject {
|
||||
func reportSupplementaryViewController(_ viewController: ReportSupplementaryViewController, skipButtonDidPressed button: UIButton)
|
||||
func reportSupplementaryViewController(_ viewController: ReportSupplementaryViewController, nextButtonDidPressed button: UIButton)
|
||||
}
|
||||
|
||||
final class ReportSupplementaryViewController: UIViewController, NeedsDependency, ReportViewControllerAppearance {
|
||||
|
||||
let logger = Logger(subsystem: "ReportSupplementaryViewController", category: "ViewController")
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
private var observations = Set<NSKeyValueObservation>()
|
||||
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
var viewModel: ReportSupplementaryViewModel! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
|
||||
// MAKK: - UI
|
||||
|
||||
lazy var cancelBarButtonItem = UIBarButtonItem(
|
||||
barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
@ -74,16 +79,14 @@ extension ReportSupplementaryViewController {
|
||||
setupAppearance()
|
||||
defer { setupNavigationBarBackgroundView() }
|
||||
|
||||
navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
|
||||
viewModel.$isReporting
|
||||
viewModel.$isBusy
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] isReporting in
|
||||
.sink { [weak self] isBusy in
|
||||
guard let self = self else { return }
|
||||
self.navigationActionView.isUserInteractionEnabled = !isReporting
|
||||
self.navigationItem.rightBarButtonItem = isBusy ? self.activityIndicatorBarButtonItem : self.cancelBarButtonItem
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(tableView)
|
||||
NSLayoutConstraint.activate([
|
||||
@ -130,49 +133,25 @@ extension ReportSupplementaryViewController {
|
||||
}
|
||||
|
||||
extension ReportSupplementaryViewController {
|
||||
private func report(withComment: Bool) {
|
||||
Task {
|
||||
do {
|
||||
let _ = try await viewModel.report(withComment: withComment)
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): report success")
|
||||
|
||||
let reportResultViewModel = ReportResultViewModel(
|
||||
context: context,
|
||||
user: viewModel.user
|
||||
)
|
||||
|
||||
coordinator.present(
|
||||
scene: .reportResult(viewModel: reportResultViewModel),
|
||||
from: self,
|
||||
transition: .show
|
||||
)
|
||||
|
||||
} catch {
|
||||
let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert)
|
||||
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
|
||||
alertController.addAction(okAction)
|
||||
self.coordinator.present(
|
||||
scene: .alertController(alertController: alertController),
|
||||
from: nil,
|
||||
transition: .alertController(animated: true, completion: nil)
|
||||
)
|
||||
}
|
||||
} // end Task
|
||||
}
|
||||
}
|
||||
|
||||
extension ReportSupplementaryViewController {
|
||||
|
||||
|
||||
@objc private func cancelBarButtonItemDidPressed(_ sender: UIBarButtonItem) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func skipButtonDidPressed(_ sender: UIButton) {
|
||||
report(withComment: false)
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
|
||||
assert(viewModel.delegate != nil)
|
||||
viewModel.isSkip = true
|
||||
viewModel.delegate?.reportSupplementaryViewController(self, skipButtonDidPressed: sender)
|
||||
}
|
||||
|
||||
@objc func nextButtonDidPressed(_ sender: UIButton) {
|
||||
report(withComment: true)
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
|
||||
assert(viewModel.delegate != nil)
|
||||
viewModel.isSkip = false
|
||||
viewModel.delegate?.reportSupplementaryViewController(self, nextButtonDidPressed: sender)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ import MastodonLocalization
|
||||
extension ReportSupplementaryViewModel {
|
||||
|
||||
static let reportItemHeaderContext = ReportItem.HeaderContext(
|
||||
primaryLabelText: L10n.Scene.Report.content2,
|
||||
secondaryLabelText: L10n.Scene.Report.step2
|
||||
primaryLabelText: "Is there anything else we should know?",
|
||||
secondaryLabelText: "Step 4 of 4"
|
||||
)
|
||||
|
||||
func setupDiffableDataSource(
|
||||
|
@ -12,26 +12,26 @@ import MastodonSDK
|
||||
|
||||
class ReportSupplementaryViewModel {
|
||||
|
||||
weak var delegate: ReportSupplementaryViewControllerDelegate?
|
||||
|
||||
// Input
|
||||
var context: AppContext
|
||||
let user: ManagedObjectRecord<MastodonUser>
|
||||
let selectStatuses: [ManagedObjectRecord<Status>]
|
||||
let commentContext = ReportItem.CommentContext()
|
||||
|
||||
@Published var isSkip = false
|
||||
@Published var isBusy = false
|
||||
|
||||
// output
|
||||
var diffableDataSource: UITableViewDiffableDataSource<ReportSection, ReportItem>?
|
||||
@Published var isNextButtonEnabled = false
|
||||
@Published var isReporting = false
|
||||
@Published var isReportSuccess = false
|
||||
|
||||
init(
|
||||
context: AppContext,
|
||||
user: ManagedObjectRecord<MastodonUser>,
|
||||
selectStatuses: [ManagedObjectRecord<Status>]
|
||||
user: ManagedObjectRecord<MastodonUser>
|
||||
) {
|
||||
self.context = context
|
||||
self.user = user
|
||||
self.selectStatuses = selectStatuses
|
||||
// end init
|
||||
|
||||
commentContext.$comment
|
||||
@ -42,41 +42,3 @@ class ReportSupplementaryViewModel {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ReportSupplementaryViewModel {
|
||||
func report(withComment: Bool) async throws {
|
||||
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
let managedObjectContext = context.managedObjectContext
|
||||
let _query: Mastodon.API.Reports.FileReportQuery? = try await managedObjectContext.perform {
|
||||
guard let user = self.user.object(in: managedObjectContext) else { return nil }
|
||||
let statusIDs = self.selectStatuses.compactMap { record -> Status.ID? in
|
||||
guard let status = record.object(in: managedObjectContext) else { return nil }
|
||||
return status.id
|
||||
}
|
||||
return Mastodon.API.Reports.FileReportQuery(
|
||||
accountID: user.id,
|
||||
statusIDs: statusIDs,
|
||||
comment: withComment ? self.commentContext.comment : nil,
|
||||
forward: nil
|
||||
)
|
||||
}
|
||||
|
||||
guard let query = _query else { return }
|
||||
|
||||
do {
|
||||
isReporting = true
|
||||
let _ = try await context.apiService.report(
|
||||
query: query,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
isReportSuccess = true
|
||||
} catch {
|
||||
isReporting = false
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
import UIKit
|
||||
import Combine
|
||||
import MastodonUI
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
import UITextView_Placeholder
|
||||
|
||||
@ -27,7 +28,8 @@ final class ReportCommentTableViewCell: UITableViewCell {
|
||||
textView.attributedPlaceholder = NSAttributedString(
|
||||
string: L10n.Scene.Report.textPlaceholder,
|
||||
attributes: [
|
||||
.font: font
|
||||
.font: font,
|
||||
.foregroundColor: Asset.Colors.Label.secondary.color
|
||||
]
|
||||
)
|
||||
textView.textContainerInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
|
||||
@ -80,4 +82,17 @@ extension ReportCommentTableViewCell {
|
||||
commentTextView.heightAnchor.constraint(greaterThanOrEqualToConstant: 100).priority(.defaultHigh),
|
||||
])
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
|
||||
commentTextView.attributedPlaceholder = NSAttributedString(
|
||||
string: L10n.Scene.Report.textPlaceholder,
|
||||
attributes: [
|
||||
.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)),
|
||||
.foregroundColor: Asset.Colors.Label.secondary.color
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ extension ReportViewControllerAppearance {
|
||||
|
||||
func setupAppearance() {
|
||||
|
||||
title = L10n.Scene.Report.titleReport
|
||||
// title = L10n.Scene.Report.titleReport
|
||||
view.backgroundColor = Asset.Scene.Report.background.color
|
||||
|
||||
setupNavigationBarAppearance()
|
||||
|
@ -23,6 +23,7 @@ extension AdaptiveStatusBarStyleNavigationController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setupFullWidthBackGesture()
|
||||
}
|
||||
|
||||
@ -45,6 +46,11 @@ extension AdaptiveStatusBarStyleNavigationController: UIGestureRecognizerDelegat
|
||||
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
let isSystemSwipeToBackEnabled = interactivePopGestureRecognizer?.isEnabled == true
|
||||
let isThereStackedViewControllers = viewControllers.count > 1
|
||||
return isSystemSwipeToBackEnabled && isThereStackedViewControllers
|
||||
let isPanPopable = (topViewController as? PanPopableViewController)?.isPanPopable ?? true
|
||||
return isSystemSwipeToBackEnabled && isThereStackedViewControllers && isPanPopable
|
||||
}
|
||||
}
|
||||
|
||||
protocol PanPopableViewController: UIViewController {
|
||||
var isPanPopable: Bool { get }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user