Merge branch 'develop' into remove_status
This commit is contained in:
commit
e3f3e2c662
@ -137,6 +137,11 @@
|
|||||||
D81A22752AB4643200905D71 /* SearchResultsOverviewTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */; };
|
D81A22752AB4643200905D71 /* SearchResultsOverviewTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */; };
|
||||||
D81A22782AB4782400905D71 /* SearchResultOverviewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22772AB4782400905D71 /* SearchResultOverviewSection.swift */; };
|
D81A22782AB4782400905D71 /* SearchResultOverviewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22772AB4782400905D71 /* SearchResultOverviewSection.swift */; };
|
||||||
D81A227B2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A227A2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift */; };
|
D81A227B2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A227A2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift */; };
|
||||||
|
D81A94122B07A1BE0067A19D /* ProfileCardTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A94112B07A1BE0067A19D /* ProfileCardTableViewCell.swift */; };
|
||||||
|
D81A94132B07A1BE0067A19D /* ProfileCardTableViewCell+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A94102B07A1BE0067A19D /* ProfileCardTableViewCell+Configuration.swift */; };
|
||||||
|
D81A94172B07A1D30067A19D /* ProfileCardView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A94162B07A1D30067A19D /* ProfileCardView+Configuration.swift */; };
|
||||||
|
D81A94182B07A1D30067A19D /* ProfileCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A94142B07A1D30067A19D /* ProfileCardView.swift */; };
|
||||||
|
D81A94192B07A1D30067A19D /* ProfileCardView+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A94152B07A1D30067A19D /* ProfileCardView+ViewModel.swift */; };
|
||||||
D81D12462A4E1861005009D4 /* PolicySelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81D12452A4E1861005009D4 /* PolicySelectionViewController.swift */; };
|
D81D12462A4E1861005009D4 /* PolicySelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81D12452A4E1861005009D4 /* PolicySelectionViewController.swift */; };
|
||||||
D81D124B2A4E1914005009D4 /* ToggleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81D124A2A4E1914005009D4 /* ToggleTableViewCell.swift */; };
|
D81D124B2A4E1914005009D4 /* ToggleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81D124A2A4E1914005009D4 /* ToggleTableViewCell.swift */; };
|
||||||
D82BD7532ABC44C2009A374A /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F9170E2A4B47EF008A5370 /* Coordinator.swift */; };
|
D82BD7532ABC44C2009A374A /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F9170E2A4B47EF008A5370 /* Coordinator.swift */; };
|
||||||
@ -451,7 +456,6 @@
|
|||||||
DBCBCBF4267CB070000F5B51 /* Decode85.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBCBF3267CB070000F5B51 /* Decode85.swift */; };
|
DBCBCBF4267CB070000F5B51 /* Decode85.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBCBF3267CB070000F5B51 /* Decode85.swift */; };
|
||||||
DBCBED1726132DB500B49291 /* UserTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBED1626132DB500B49291 /* UserTimelineViewModel+Diffable.swift */; };
|
DBCBED1726132DB500B49291 /* UserTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBED1626132DB500B49291 /* UserTimelineViewModel+Diffable.swift */; };
|
||||||
DBCC3B30261440A50045B23D /* UITabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCC3B2F261440A50045B23D /* UITabBarController.swift */; };
|
DBCC3B30261440A50045B23D /* UITabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCC3B2F261440A50045B23D /* UITabBarController.swift */; };
|
||||||
DBCC3B8F26148F7B0045B23D /* CachedProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCC3B8E26148F7B0045B23D /* CachedProfileViewModel.swift */; };
|
|
||||||
DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD376B1269302A4007FEC24 /* UITableViewCell.swift */; };
|
DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD376B1269302A4007FEC24 /* UITableViewCell.swift */; };
|
||||||
DBD5B1F827BCFD9D00BD6B38 /* DataSourceProvider+TableViewControllerNavigateable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD5B1F727BCFD9D00BD6B38 /* DataSourceProvider+TableViewControllerNavigateable.swift */; };
|
DBD5B1F827BCFD9D00BD6B38 /* DataSourceProvider+TableViewControllerNavigateable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD5B1F727BCFD9D00BD6B38 /* DataSourceProvider+TableViewControllerNavigateable.swift */; };
|
||||||
DBD5B1FA27BD013700BD6B38 /* DataSourceProvider+StatusTableViewControllerNavigateable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD5B1F927BD013700BD6B38 /* DataSourceProvider+StatusTableViewControllerNavigateable.swift */; };
|
DBD5B1FA27BD013700BD6B38 /* DataSourceProvider+StatusTableViewControllerNavigateable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD5B1F927BD013700BD6B38 /* DataSourceProvider+StatusTableViewControllerNavigateable.swift */; };
|
||||||
@ -794,6 +798,11 @@
|
|||||||
D81A940D2B04E7AC0067A19D /* hy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hy; path = hy.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
|
D81A940D2B04E7AC0067A19D /* hy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hy; path = hy.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
|
||||||
D81A940E2B04E7AD0067A19D /* hy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hy; path = hy.lproj/MainInterface.strings; sourceTree = "<group>"; };
|
D81A940E2B04E7AD0067A19D /* hy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hy; path = hy.lproj/MainInterface.strings; sourceTree = "<group>"; };
|
||||||
D81A940F2B04E7AD0067A19D /* hy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hy; path = hy.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
D81A940F2B04E7AD0067A19D /* hy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hy; path = hy.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
D81A94102B07A1BE0067A19D /* ProfileCardTableViewCell+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileCardTableViewCell+Configuration.swift"; sourceTree = "<group>"; };
|
||||||
|
D81A94112B07A1BE0067A19D /* ProfileCardTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCardTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
D81A94142B07A1D30067A19D /* ProfileCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCardView.swift; sourceTree = "<group>"; };
|
||||||
|
D81A94152B07A1D30067A19D /* ProfileCardView+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileCardView+ViewModel.swift"; sourceTree = "<group>"; };
|
||||||
|
D81A94162B07A1D30067A19D /* ProfileCardView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileCardView+Configuration.swift"; sourceTree = "<group>"; };
|
||||||
D81D12452A4E1861005009D4 /* PolicySelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PolicySelectionViewController.swift; sourceTree = "<group>"; };
|
D81D12452A4E1861005009D4 /* PolicySelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PolicySelectionViewController.swift; sourceTree = "<group>"; };
|
||||||
D81D124A2A4E1914005009D4 /* ToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleTableViewCell.swift; sourceTree = "<group>"; };
|
D81D124A2A4E1914005009D4 /* ToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D82463522A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Intents.strings; sourceTree = "<group>"; };
|
D82463522A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||||
@ -1194,7 +1203,6 @@
|
|||||||
DBCBCBF3267CB070000F5B51 /* Decode85.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decode85.swift; sourceTree = "<group>"; };
|
DBCBCBF3267CB070000F5B51 /* Decode85.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decode85.swift; sourceTree = "<group>"; };
|
||||||
DBCBED1626132DB500B49291 /* UserTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
DBCBED1626132DB500B49291 /* UserTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||||
DBCC3B2F261440A50045B23D /* UITabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITabBarController.swift; sourceTree = "<group>"; };
|
DBCC3B2F261440A50045B23D /* UITabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITabBarController.swift; sourceTree = "<group>"; };
|
||||||
DBCC3B8E26148F7B0045B23D /* CachedProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedProfileViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
DBD376B1269302A4007FEC24 /* UITableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = "<group>"; };
|
DBD376B1269302A4007FEC24 /* UITableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = "<group>"; };
|
||||||
DBD5B1F727BCFD9D00BD6B38 /* DataSourceProvider+TableViewControllerNavigateable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceProvider+TableViewControllerNavigateable.swift"; sourceTree = "<group>"; };
|
DBD5B1F727BCFD9D00BD6B38 /* DataSourceProvider+TableViewControllerNavigateable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceProvider+TableViewControllerNavigateable.swift"; sourceTree = "<group>"; };
|
||||||
DBD5B1F927BD013700BD6B38 /* DataSourceProvider+StatusTableViewControllerNavigateable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceProvider+StatusTableViewControllerNavigateable.swift"; sourceTree = "<group>"; };
|
DBD5B1F927BD013700BD6B38 /* DataSourceProvider+StatusTableViewControllerNavigateable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceProvider+StatusTableViewControllerNavigateable.swift"; sourceTree = "<group>"; };
|
||||||
@ -1650,7 +1658,6 @@
|
|||||||
DB65C63527A2AF52008BAC2E /* Report */,
|
DB65C63527A2AF52008BAC2E /* Report */,
|
||||||
DB0617F727855B010030EE79 /* Notification */,
|
DB0617F727855B010030EE79 /* Notification */,
|
||||||
DB4F097726A039A200D62E92 /* Search */,
|
DB4F097726A039A200D62E92 /* Search */,
|
||||||
DB3E6FE52806A5BA00B035AE /* Discovery */,
|
|
||||||
);
|
);
|
||||||
path = Diffable;
|
path = Diffable;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2091,15 +2098,6 @@
|
|||||||
path = Hashtags;
|
path = Hashtags;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
DB3E6FE52806A5BA00B035AE /* Discovery */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
DB3E6FE32806A5B800B035AE /* DiscoverySection.swift */,
|
|
||||||
DB3E6FE62806A7A200B035AE /* DiscoveryItem.swift */,
|
|
||||||
);
|
|
||||||
path = Discovery;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
DB3E6FED2806D7FC00B035AE /* News */ = {
|
DB3E6FED2806D7FC00B035AE /* News */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -2114,6 +2112,11 @@
|
|||||||
DB3E6FF62807C40500B035AE /* ForYou */ = {
|
DB3E6FF62807C40500B035AE /* ForYou */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D81A94142B07A1D30067A19D /* ProfileCardView.swift */,
|
||||||
|
D81A94162B07A1D30067A19D /* ProfileCardView+Configuration.swift */,
|
||||||
|
D81A94152B07A1D30067A19D /* ProfileCardView+ViewModel.swift */,
|
||||||
|
D81A94112B07A1BE0067A19D /* ProfileCardTableViewCell.swift */,
|
||||||
|
D81A94102B07A1BE0067A19D /* ProfileCardTableViewCell+Configuration.swift */,
|
||||||
DB3E6FF42807C40300B035AE /* DiscoveryForYouViewController.swift */,
|
DB3E6FF42807C40300B035AE /* DiscoveryForYouViewController.swift */,
|
||||||
DB3E6FF72807C45300B035AE /* DiscoveryForYouViewModel.swift */,
|
DB3E6FF72807C45300B035AE /* DiscoveryForYouViewModel.swift */,
|
||||||
DB3E6FF92807C47900B035AE /* DiscoveryForYouViewModel+Diffable.swift */,
|
DB3E6FF92807C47900B035AE /* DiscoveryForYouViewModel+Diffable.swift */,
|
||||||
@ -2763,7 +2766,6 @@
|
|||||||
DBFEEC97279BDC6A004F81DD /* About */,
|
DBFEEC97279BDC6A004F81DD /* About */,
|
||||||
DB9D6BFE25E4F5940051B173 /* ProfileViewController.swift */,
|
DB9D6BFE25E4F5940051B173 /* ProfileViewController.swift */,
|
||||||
DBB5255D2611F07A002F1F29 /* ProfileViewModel.swift */,
|
DBB5255D2611F07A002F1F29 /* ProfileViewModel.swift */,
|
||||||
DBCC3B8E26148F7B0045B23D /* CachedProfileViewModel.swift */,
|
|
||||||
DBAE3FAE26172FC0004B8251 /* RemoteProfileViewModel.swift */,
|
DBAE3FAE26172FC0004B8251 /* RemoteProfileViewModel.swift */,
|
||||||
DBB525632612C988002F1F29 /* MeProfileViewModel.swift */,
|
DBB525632612C988002F1F29 /* MeProfileViewModel.swift */,
|
||||||
);
|
);
|
||||||
@ -2926,6 +2928,8 @@
|
|||||||
DB3E6FED2806D7FC00B035AE /* News */,
|
DB3E6FED2806D7FC00B035AE /* News */,
|
||||||
DB3EA8E7281B79E500598866 /* Community */,
|
DB3EA8E7281B79E500598866 /* Community */,
|
||||||
DB3E6FF62807C40500B035AE /* ForYou */,
|
DB3E6FF62807C40500B035AE /* ForYou */,
|
||||||
|
DB3E6FE32806A5B800B035AE /* DiscoverySection.swift */,
|
||||||
|
DB3E6FE62806A7A200B035AE /* DiscoveryItem.swift */,
|
||||||
DBDFF19928055A1400557A48 /* DiscoveryViewController.swift */,
|
DBDFF19928055A1400557A48 /* DiscoveryViewController.swift */,
|
||||||
DBDFF19B28055BD600557A48 /* DiscoveryViewModel.swift */,
|
DBDFF19B28055BD600557A48 /* DiscoveryViewModel.swift */,
|
||||||
);
|
);
|
||||||
@ -3727,6 +3731,7 @@
|
|||||||
2D38F1EB25CD477000561493 /* HomeTimelineViewModel+LoadLatestState.swift in Sources */,
|
2D38F1EB25CD477000561493 /* HomeTimelineViewModel+LoadLatestState.swift in Sources */,
|
||||||
DB5B7295273112B100081888 /* FollowingListViewController.swift in Sources */,
|
DB5B7295273112B100081888 /* FollowingListViewController.swift in Sources */,
|
||||||
0F202201261326E6000C64BF /* HashtagTimelineViewModel.swift in Sources */,
|
0F202201261326E6000C64BF /* HashtagTimelineViewModel.swift in Sources */,
|
||||||
|
D81A94172B07A1D30067A19D /* ProfileCardView+Configuration.swift in Sources */,
|
||||||
DB63F7452799056400455B82 /* HashtagTableViewCell.swift in Sources */,
|
DB63F7452799056400455B82 /* HashtagTableViewCell.swift in Sources */,
|
||||||
DBAE3FAF26172FC0004B8251 /* RemoteProfileViewModel.swift in Sources */,
|
DBAE3FAF26172FC0004B8251 /* RemoteProfileViewModel.swift in Sources */,
|
||||||
D82BD7552ABC73AF009A374A /* NotificationPolicyTableViewCell.swift in Sources */,
|
D82BD7552ABC73AF009A374A /* NotificationPolicyTableViewCell.swift in Sources */,
|
||||||
@ -3772,6 +3777,7 @@
|
|||||||
2A506CF4292CD85800059C37 /* FollowedTagsViewController.swift in Sources */,
|
2A506CF4292CD85800059C37 /* FollowedTagsViewController.swift in Sources */,
|
||||||
DB1D843026566512000346B3 /* KeyboardPreference.swift in Sources */,
|
DB1D843026566512000346B3 /* KeyboardPreference.swift in Sources */,
|
||||||
DB852D1926FAEB6B00FC9D81 /* SidebarViewController.swift in Sources */,
|
DB852D1926FAEB6B00FC9D81 /* SidebarViewController.swift in Sources */,
|
||||||
|
D81A94132B07A1BE0067A19D /* ProfileCardTableViewCell+Configuration.swift in Sources */,
|
||||||
2D206B9225F60EA700143C56 /* UIControl.swift in Sources */,
|
2D206B9225F60EA700143C56 /* UIControl.swift in Sources */,
|
||||||
85904C02293BC0EB0011C817 /* ImageProvider.swift in Sources */,
|
85904C02293BC0EB0011C817 /* ImageProvider.swift in Sources */,
|
||||||
DBDFF1932805554900557A48 /* DiscoveryPostsViewModel.swift in Sources */,
|
DBDFF1932805554900557A48 /* DiscoveryPostsViewModel.swift in Sources */,
|
||||||
@ -3799,6 +3805,7 @@
|
|||||||
DB63F769279A5EBB00455B82 /* NotificationTimelineViewModel+Diffable.swift in Sources */,
|
DB63F769279A5EBB00455B82 /* NotificationTimelineViewModel+Diffable.swift in Sources */,
|
||||||
DBFEEC9B279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift in Sources */,
|
DBFEEC9B279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift in Sources */,
|
||||||
DBB525562611EDCA002F1F29 /* UserTimelineViewModel.swift in Sources */,
|
DBB525562611EDCA002F1F29 /* UserTimelineViewModel.swift in Sources */,
|
||||||
|
D81A94192B07A1D30067A19D /* ProfileCardView+ViewModel.swift in Sources */,
|
||||||
D8916DC029211BE500124085 /* ContentSizedTableView.swift in Sources */,
|
D8916DC029211BE500124085 /* ContentSizedTableView.swift in Sources */,
|
||||||
DB0618012785732C0030EE79 /* ServerRulesTableViewCell.swift in Sources */,
|
DB0618012785732C0030EE79 /* ServerRulesTableViewCell.swift in Sources */,
|
||||||
DB98EB5C27B10A730082E365 /* ReportSupplementaryViewModel.swift in Sources */,
|
DB98EB5C27B10A730082E365 /* ReportSupplementaryViewModel.swift in Sources */,
|
||||||
@ -3875,7 +3882,6 @@
|
|||||||
2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */,
|
2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */,
|
||||||
D8F9170D2A4B3C6F008A5370 /* AboutMastodonTableViewCell.swift in Sources */,
|
D8F9170D2A4B3C6F008A5370 /* AboutMastodonTableViewCell.swift in Sources */,
|
||||||
DB697DE1278F5296004EF2F7 /* DataSourceFacade+Model.swift in Sources */,
|
DB697DE1278F5296004EF2F7 /* DataSourceFacade+Model.swift in Sources */,
|
||||||
DBCC3B8F26148F7B0045B23D /* CachedProfileViewModel.swift in Sources */,
|
|
||||||
DB4F097526A037F500D62E92 /* SearchHistoryViewModel.swift in Sources */,
|
DB4F097526A037F500D62E92 /* SearchHistoryViewModel.swift in Sources */,
|
||||||
DB3EA8E9281B7A3700598866 /* DiscoveryCommunityViewModel.swift in Sources */,
|
DB3EA8E9281B7A3700598866 /* DiscoveryCommunityViewModel.swift in Sources */,
|
||||||
D87BFC8B291D5C6B00FEE264 /* MastodonLoginView.swift in Sources */,
|
D87BFC8B291D5C6B00FEE264 /* MastodonLoginView.swift in Sources */,
|
||||||
@ -3974,6 +3980,7 @@
|
|||||||
DBEFCD7D282A2A3B00C0ABEA /* ReportServerRulesViewController.swift in Sources */,
|
DBEFCD7D282A2A3B00C0ABEA /* ReportServerRulesViewController.swift in Sources */,
|
||||||
DBB525362611ECEB002F1F29 /* UserTimelineViewController.swift in Sources */,
|
DBB525362611ECEB002F1F29 /* UserTimelineViewController.swift in Sources */,
|
||||||
D8F917122A4C6B67008A5370 /* GeneralSettingsViewController.swift in Sources */,
|
D8F917122A4C6B67008A5370 /* GeneralSettingsViewController.swift in Sources */,
|
||||||
|
D81A94122B07A1BE0067A19D /* ProfileCardTableViewCell.swift in Sources */,
|
||||||
DB98EB4927B0F0CD0082E365 /* ReportStatusTableViewCell.swift in Sources */,
|
DB98EB4927B0F0CD0082E365 /* ReportStatusTableViewCell.swift in Sources */,
|
||||||
855149CA29606D6400943D96 /* PortraitAlertController.swift in Sources */,
|
855149CA29606D6400943D96 /* PortraitAlertController.swift in Sources */,
|
||||||
DBF3B7412733EB9400E21627 /* MastodonLocalCode.swift in Sources */,
|
DBF3B7412733EB9400E21627 /* MastodonLocalCode.swift in Sources */,
|
||||||
@ -3981,6 +3988,7 @@
|
|||||||
DB4F096A269EDAD200D62E92 /* SearchResultViewModel+State.swift in Sources */,
|
DB4F096A269EDAD200D62E92 /* SearchResultViewModel+State.swift in Sources */,
|
||||||
D8ECC8102AC31EA400AE0818 /* NotificationSettingsDisabledTableViewCell.swift in Sources */,
|
D8ECC8102AC31EA400AE0818 /* NotificationSettingsDisabledTableViewCell.swift in Sources */,
|
||||||
5BB04FF5262F0E6D0043BFF6 /* ReportSection.swift in Sources */,
|
5BB04FF5262F0E6D0043BFF6 /* ReportSection.swift in Sources */,
|
||||||
|
D81A94182B07A1D30067A19D /* ProfileCardView.swift in Sources */,
|
||||||
DBEFCD82282A2AB100C0ABEA /* ReportServerRulesView.swift in Sources */,
|
DBEFCD82282A2AB100C0ABEA /* ReportServerRulesView.swift in Sources */,
|
||||||
DBA94436265CBB7400C537E1 /* ProfileFieldItem.swift in Sources */,
|
DBA94436265CBB7400C537E1 /* ProfileFieldItem.swift in Sources */,
|
||||||
2A3F6FE5292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift in Sources */,
|
2A3F6FE5292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift in Sources */,
|
||||||
|
@ -30,15 +30,18 @@ extension DataSourceFacade {
|
|||||||
static func responseToUserFollowAction(
|
static func responseToUserFollowAction(
|
||||||
dependency: NeedsDependency & AuthContextProvider,
|
dependency: NeedsDependency & AuthContextProvider,
|
||||||
user: Mastodon.Entity.Account
|
user: Mastodon.Entity.Account
|
||||||
) async throws {
|
) async throws -> Mastodon.Entity.Relationship {
|
||||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||||
await selectionFeedbackGenerator.selectionChanged()
|
await selectionFeedbackGenerator.selectionChanged()
|
||||||
|
|
||||||
_ = try await dependency.context.apiService.toggleFollow(
|
let response = try await dependency.context.apiService.toggleFollow(
|
||||||
user: user,
|
user: user,
|
||||||
authenticationBox: dependency.authContext.mastodonAuthenticationBox
|
authenticationBox: dependency.authContext.mastodonAuthenticationBox
|
||||||
)
|
).value
|
||||||
|
|
||||||
dependency.context.authenticationService.fetchFollowingAndBlockedAsync()
|
dependency.context.authenticationService.fetchFollowingAndBlockedAsync()
|
||||||
|
|
||||||
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,10 @@ extension DataSourceFacade {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let profileViewModel = CachedProfileViewModel(
|
let profileViewModel = ProfileViewModel(
|
||||||
context: provider.context,
|
context: provider.context,
|
||||||
authContext: provider.authContext,
|
authContext: provider.authContext,
|
||||||
mastodonUser: user
|
optionalMastodonUser: user
|
||||||
)
|
)
|
||||||
|
|
||||||
_ = provider.coordinator.present(
|
_ = provider.coordinator.present(
|
||||||
@ -126,7 +126,7 @@ extension DataSourceFacade {
|
|||||||
let _user = provider.context.managedObjectContext.safeFetch(request).first
|
let _user = provider.context.managedObjectContext.safeFetch(request).first
|
||||||
|
|
||||||
if let user = _user {
|
if let user = _user {
|
||||||
return CachedProfileViewModel(context: provider.context, authContext: provider.authContext, mastodonUser: user)
|
return ProfileViewModel(context: provider.context, authContext: provider.authContext, optionalMastodonUser: user)
|
||||||
} else {
|
} else {
|
||||||
return RemoteProfileViewModel(context: provider.context, authContext: provider.authContext, userID: userID)
|
return RemoteProfileViewModel(context: provider.context, authContext: provider.authContext, userID: userID)
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,10 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import CoreDataStack
|
|
||||||
|
|
||||||
enum DiscoveryItem: Hashable {
|
enum DiscoveryItem: Hashable {
|
||||||
case hashtag(Mastodon.Entity.Tag)
|
case hashtag(Mastodon.Entity.Tag)
|
||||||
case link(Mastodon.Entity.Link)
|
case link(Mastodon.Entity.Link)
|
||||||
case user(ManagedObjectRecord<MastodonUser>)
|
case account(Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?)
|
||||||
case bottomLoader
|
case bottomLoader
|
||||||
}
|
}
|
@ -40,12 +40,16 @@ extension DiscoverySection {
|
|||||||
context: AppContext,
|
context: AppContext,
|
||||||
configuration: Configuration
|
configuration: Configuration
|
||||||
) -> UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem> {
|
) -> UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem> {
|
||||||
|
|
||||||
tableView.register(TrendTableViewCell.self, forCellReuseIdentifier: String(describing: TrendTableViewCell.self))
|
tableView.register(TrendTableViewCell.self, forCellReuseIdentifier: String(describing: TrendTableViewCell.self))
|
||||||
tableView.register(NewsTableViewCell.self, forCellReuseIdentifier: String(describing: NewsTableViewCell.self))
|
tableView.register(NewsTableViewCell.self, forCellReuseIdentifier: String(describing: NewsTableViewCell.self))
|
||||||
tableView.register(ProfileCardTableViewCell.self, forCellReuseIdentifier: String(describing: ProfileCardTableViewCell.self))
|
tableView.register(ProfileCardTableViewCell.self, forCellReuseIdentifier: String(describing: ProfileCardTableViewCell.self))
|
||||||
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
||||||
|
|
||||||
return UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item in
|
return UITableViewDiffableDataSource(tableView: tableView) {
|
||||||
|
tableView,
|
||||||
|
indexPath,
|
||||||
|
item in
|
||||||
switch item {
|
switch item {
|
||||||
case .hashtag(let tag):
|
case .hashtag(let tag):
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TrendTableViewCell.self), for: indexPath) as! TrendTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TrendTableViewCell.self), for: indexPath) as! TrendTableViewCell
|
||||||
@ -55,27 +59,26 @@ extension DiscoverySection {
|
|||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NewsTableViewCell.self), for: indexPath) as! NewsTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NewsTableViewCell.self), for: indexPath) as! NewsTableViewCell
|
||||||
cell.newsView.configure(link: link)
|
cell.newsView.configure(link: link)
|
||||||
return cell
|
return cell
|
||||||
case .user(let record):
|
case .account(let account, relationship: let relationship):
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ProfileCardTableViewCell.self), for: indexPath) as! ProfileCardTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ProfileCardTableViewCell.self), for: indexPath) as! ProfileCardTableViewCell
|
||||||
context.managedObjectContext.performAndWait {
|
|
||||||
guard let user = record.object(in: context.managedObjectContext) else { return }
|
|
||||||
cell.configure(
|
cell.configure(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
user: user,
|
account: account,
|
||||||
|
relationship: relationship,
|
||||||
profileCardTableViewCellDelegate: configuration.profileCardTableViewCellDelegate
|
profileCardTableViewCellDelegate: configuration.profileCardTableViewCellDelegate
|
||||||
)
|
)
|
||||||
|
|
||||||
// bind familiarFollowers
|
// bind familiarFollowers
|
||||||
if let familiarFollowers = configuration.familiarFollowers {
|
if let familiarFollowers = configuration.familiarFollowers {
|
||||||
familiarFollowers
|
familiarFollowers
|
||||||
.map { array in array.first(where: { $0.id == user.id }) }
|
.map { array in array.first(where: { $0.id == account.id }) }
|
||||||
.assign(to: \.familiarFollowers, on: cell.profileCardView.viewModel)
|
.assign(to: \.familiarFollowers, on: cell.profileCardView.viewModel)
|
||||||
.store(in: &cell.disposeBag)
|
.store(in: &cell.disposeBag)
|
||||||
} else {
|
} else {
|
||||||
cell.profileCardView.viewModel.familiarFollowers = nil
|
cell.profileCardView.viewModel.familiarFollowers = nil
|
||||||
}
|
}
|
||||||
// bind me
|
|
||||||
cell.profileCardView.viewModel.relationshipViewModel.me = configuration.authContext.mastodonAuthenticationBox.authentication.user(in: context.managedObjectContext)
|
|
||||||
}
|
|
||||||
return cell
|
return cell
|
||||||
case .bottomLoader:
|
case .bottomLoader:
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell
|
@ -96,18 +96,11 @@ extension DiscoveryForYouViewController: AuthContextProvider {
|
|||||||
extension DiscoveryForYouViewController: UITableViewDelegate {
|
extension DiscoveryForYouViewController: UITableViewDelegate {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
guard case let .user(record) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
|
guard case let .account(account, _) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
|
||||||
guard let user = record.object(in: context.managedObjectContext) else { return }
|
|
||||||
let profileViewModel = CachedProfileViewModel(
|
Task {
|
||||||
context: context,
|
await DataSourceFacade.coordinateToProfileScene(provider: self, account: account)
|
||||||
authContext: viewModel.authContext,
|
}
|
||||||
mastodonUser: user
|
|
||||||
)
|
|
||||||
_ = coordinator.present(
|
|
||||||
scene: .profile(viewModel: profileViewModel),
|
|
||||||
from: self,
|
|
||||||
transition: .show
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -117,17 +110,22 @@ extension DiscoveryForYouViewController: ProfileCardTableViewCellDelegate {
|
|||||||
func profileCardTableViewCell(
|
func profileCardTableViewCell(
|
||||||
_ cell: ProfileCardTableViewCell,
|
_ cell: ProfileCardTableViewCell,
|
||||||
profileCardView: ProfileCardView,
|
profileCardView: ProfileCardView,
|
||||||
relationshipButtonDidPressed button: ProfileRelationshipActionButton
|
relationshipButtonDidPressed button: UIButton
|
||||||
) {
|
) {
|
||||||
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
||||||
guard case let .user(record) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
|
guard case let .account(account, _) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
|
||||||
|
|
||||||
|
cell.profileCardView.setButtonState(.loading)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
try await DataSourceFacade.responseToUserFollowAction(
|
let newRelationship = try await DataSourceFacade.responseToUserFollowAction(dependency: self, user: account)
|
||||||
dependency: self,
|
|
||||||
user: record
|
let isMe = (account.id == authContext.mastodonAuthenticationBox.userID)
|
||||||
)
|
|
||||||
} // end Task
|
await MainActor.run {
|
||||||
|
cell.profileCardView.updateButtonState(with: newRelationship, isMe: isMe)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func profileCardTableViewCell(
|
func profileCardTableViewCell(
|
||||||
@ -136,10 +134,9 @@ extension DiscoveryForYouViewController: ProfileCardTableViewCellDelegate {
|
|||||||
familiarFollowersDashboardViewDidPressed view: FamiliarFollowersDashboardView
|
familiarFollowersDashboardViewDidPressed view: FamiliarFollowersDashboardView
|
||||||
) {
|
) {
|
||||||
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
||||||
guard case let .user(record) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
|
guard case let .account(account, _) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
|
||||||
guard let user = record.object(in: context.managedObjectContext) else { return }
|
|
||||||
|
|
||||||
let userID = user.id
|
let userID = account.id
|
||||||
let _familiarFollowers = viewModel.familiarFollowers.first(where: { $0.id == userID })
|
let _familiarFollowers = viewModel.familiarFollowers.first(where: { $0.id == userID })
|
||||||
guard let familiarFollowers = _familiarFollowers else {
|
guard let familiarFollowers = _familiarFollowers else {
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
|
@ -28,22 +28,5 @@ extension DiscoveryForYouViewModel {
|
|||||||
Task {
|
Task {
|
||||||
try await fetch()
|
try await fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
userFetchedResultsController.$records
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] records in
|
|
||||||
guard let self = self else { return }
|
|
||||||
guard let diffableDataSource = self.diffableDataSource else { return }
|
|
||||||
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<DiscoverySection, DiscoveryItem>()
|
|
||||||
snapshot.appendSections([.forYou])
|
|
||||||
|
|
||||||
let items = records.map { DiscoveryItem.user($0) }
|
|
||||||
snapshot.appendItems(items, toSection: .forYou)
|
|
||||||
|
|
||||||
diffableDataSource.apply(snapshot, animatingDifferences: false)
|
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
import GameplayKit
|
import GameplayKit
|
||||||
import CoreData
|
|
||||||
import CoreDataStack
|
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
|
|
||||||
@ -20,11 +18,12 @@ final class DiscoveryForYouViewModel {
|
|||||||
// input
|
// input
|
||||||
let context: AppContext
|
let context: AppContext
|
||||||
let authContext: AuthContext
|
let authContext: AuthContext
|
||||||
let userFetchedResultsController: UserFetchedResultsController
|
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@Published var familiarFollowers: [Mastodon.Entity.FamiliarFollowers] = []
|
@Published var familiarFollowers: [Mastodon.Entity.FamiliarFollowers] = []
|
||||||
@Published var isFetching = false
|
@Published var isFetching = false
|
||||||
|
@Published var accounts: [Mastodon.Entity.Account]
|
||||||
|
var relationships: [Mastodon.Entity.Relationship?]
|
||||||
|
|
||||||
// output
|
// output
|
||||||
var diffableDataSource: UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem>?
|
var diffableDataSource: UITableViewDiffableDataSource<DiscoverySection, DiscoveryItem>?
|
||||||
@ -33,12 +32,8 @@ final class DiscoveryForYouViewModel {
|
|||||||
init(context: AppContext, authContext: AuthContext) {
|
init(context: AppContext, authContext: AuthContext) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.authContext = authContext
|
self.authContext = authContext
|
||||||
self.userFetchedResultsController = UserFetchedResultsController(
|
self.accounts = []
|
||||||
managedObjectContext: context.managedObjectContext,
|
self.relationships = []
|
||||||
domain: authContext.mastodonAuthenticationBox.domain,
|
|
||||||
additionalPredicate: nil
|
|
||||||
)
|
|
||||||
// end init
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,40 +41,63 @@ extension DiscoveryForYouViewModel {
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
func fetch() async throws {
|
func fetch() async throws {
|
||||||
guard !isFetching else { return }
|
guard isFetching == false else { return }
|
||||||
isFetching = true
|
isFetching = true
|
||||||
defer { isFetching = false }
|
defer { isFetching = false }
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let userIDs = try await fetchSuggestionAccounts()
|
let suggestedAccounts = try await fetchSuggestionAccounts()
|
||||||
|
|
||||||
let _familiarFollowersResponse = try? await context.apiService.familiarFollowers(
|
let familiarFollowersResponse = try? await context.apiService.familiarFollowers(
|
||||||
query: .init(ids: userIDs),
|
query: .init(ids: suggestedAccounts.compactMap { $0.id }),
|
||||||
authenticationBox: authContext.mastodonAuthenticationBox
|
authenticationBox: authContext.mastodonAuthenticationBox
|
||||||
)
|
).value
|
||||||
familiarFollowers = _familiarFollowersResponse?.value ?? []
|
|
||||||
userFetchedResultsController.userIDs = userIDs
|
let relationships = try? await context.apiService.relationship(
|
||||||
|
forAccounts: suggestedAccounts,
|
||||||
|
authenticationBox: authContext.mastodonAuthenticationBox
|
||||||
|
).value
|
||||||
|
|
||||||
|
familiarFollowers = familiarFollowersResponse ?? []
|
||||||
|
accounts = suggestedAccounts
|
||||||
|
self.relationships = relationships ?? []
|
||||||
} catch {
|
} catch {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await MainActor.run {
|
||||||
|
guard let diffableDataSource = self.diffableDataSource else { return }
|
||||||
|
|
||||||
|
var snapshot = NSDiffableDataSourceSnapshot<DiscoverySection, DiscoveryItem>()
|
||||||
|
snapshot.appendSections([.forYou])
|
||||||
|
|
||||||
|
let items = self.accounts.map { account in
|
||||||
|
let relationship = relationships.first { $0?.id == account.id } ?? nil
|
||||||
|
|
||||||
|
return DiscoveryItem.account(account, relationship: relationship)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchSuggestionAccounts() async throws -> [Mastodon.Entity.Account.ID] {
|
snapshot.appendItems(items, toSection: .forYou)
|
||||||
|
|
||||||
|
diffableDataSource.apply(snapshot, animatingDifferences: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func fetchSuggestionAccounts() async throws -> [Mastodon.Entity.Account] {
|
||||||
do {
|
do {
|
||||||
let response = try await context.apiService.suggestionAccountV2(
|
let response = try await context.apiService.suggestionAccountV2(
|
||||||
query: nil,
|
query: nil,
|
||||||
authenticationBox: authContext.mastodonAuthenticationBox
|
authenticationBox: authContext.mastodonAuthenticationBox
|
||||||
)
|
).value
|
||||||
let userIDs = response.value.map { $0.account.id }
|
return response.compactMap { $0.account }
|
||||||
return userIDs
|
|
||||||
} catch {
|
} catch {
|
||||||
// fallback V1
|
// fallback V1
|
||||||
let response = try await context.apiService.suggestionAccount(
|
let response = try await context.apiService.suggestionAccount(
|
||||||
query: nil,
|
query: nil,
|
||||||
authenticationBox: authContext.mastodonAuthenticationBox
|
authenticationBox: authContext.mastodonAuthenticationBox
|
||||||
)
|
).value
|
||||||
let userIDs = response.value.map { $0.id }
|
|
||||||
return userIDs
|
return response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import CoreDataStack
|
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
extension ProfileCardTableViewCell {
|
extension ProfileCardTableViewCell {
|
||||||
|
|
||||||
public func configure(
|
public func configure(
|
||||||
tableView: UITableView,
|
tableView: UITableView,
|
||||||
user: MastodonUser,
|
account: Mastodon.Entity.Account,
|
||||||
|
relationship: Mastodon.Entity.Relationship?,
|
||||||
profileCardTableViewCellDelegate: ProfileCardTableViewCellDelegate?
|
profileCardTableViewCellDelegate: ProfileCardTableViewCellDelegate?
|
||||||
) {
|
) {
|
||||||
if profileCardView.frame == .zero {
|
if profileCardView.frame == .zero {
|
||||||
@ -23,7 +23,7 @@ extension ProfileCardTableViewCell {
|
|||||||
profileCardView.setupLayoutFrame(layoutMarginsGuide.layoutFrame)
|
profileCardView.setupLayoutFrame(layoutMarginsGuide.layoutFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
profileCardView.configure(user: user)
|
profileCardView.configure(account: account, relationship: relationship)
|
||||||
delegate = profileCardTableViewCellDelegate
|
delegate = profileCardTableViewCellDelegate
|
||||||
}
|
}
|
||||||
|
|
@ -7,9 +7,10 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
import MastodonUI
|
||||||
|
|
||||||
public protocol ProfileCardTableViewCellDelegate: AnyObject {
|
public protocol ProfileCardTableViewCellDelegate: AnyObject {
|
||||||
func profileCardTableViewCell(_ cell: ProfileCardTableViewCell, profileCardView: ProfileCardView, relationshipButtonDidPressed button: ProfileRelationshipActionButton)
|
func profileCardTableViewCell(_ cell: ProfileCardTableViewCell, profileCardView: ProfileCardView, relationshipButtonDidPressed button: UIButton)
|
||||||
func profileCardTableViewCell(_ cell: ProfileCardTableViewCell, profileCardView: ProfileCardView, familiarFollowersDashboardViewDidPressed view: FamiliarFollowersDashboardView)
|
func profileCardTableViewCell(_ cell: ProfileCardTableViewCell, profileCardView: ProfileCardView, familiarFollowersDashboardViewDidPressed view: FamiliarFollowersDashboardView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ extension ProfileCardTableViewCell {
|
|||||||
profileCardView.isAccessibilityElement = true
|
profileCardView.isAccessibilityElement = true
|
||||||
accessibilityElements = [
|
accessibilityElements = [
|
||||||
profileCardView,
|
profileCardView,
|
||||||
profileCardView.relationshipActionButton
|
profileCardView.followButton
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +81,7 @@ extension ProfileCardTableViewCell {
|
|||||||
// MARK: - ProfileCardViewDelegate
|
// MARK: - ProfileCardViewDelegate
|
||||||
extension ProfileCardTableViewCell: ProfileCardViewDelegate {
|
extension ProfileCardTableViewCell: ProfileCardViewDelegate {
|
||||||
|
|
||||||
public func profileCardView(_ profileCardView: ProfileCardView, relationshipButtonDidPressed button: ProfileRelationshipActionButton) {
|
public func profileCardView(_ profileCardView: ProfileCardView, relationshipButtonDidPressed button: UIButton) {
|
||||||
delegate?.profileCardTableViewCell(self, profileCardView: profileCardView, relationshipButtonDidPressed: button)
|
delegate?.profileCardTableViewCell(self, profileCardView: profileCardView, relationshipButtonDidPressed: button)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// ProfileCardView+Configuration.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by MainasuK on 2022-4-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import CoreDataStack
|
||||||
|
import Meta
|
||||||
|
import MastodonCore
|
||||||
|
import MastodonMeta
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
extension ProfileCardView {
|
||||||
|
|
||||||
|
public func configure(account: Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?) {
|
||||||
|
viewModel.authorBannerImageURL = URL(string: account.header)
|
||||||
|
viewModel.statusesCount = account.statusesCount
|
||||||
|
viewModel.followingCount = account.followingCount
|
||||||
|
viewModel.followersCount = account.followersCount
|
||||||
|
viewModel.authorAvatarImageURL = account.avatarImageURL()
|
||||||
|
|
||||||
|
let emojis = account.emojis?.asDictionary ?? [:]
|
||||||
|
|
||||||
|
do {
|
||||||
|
let content = MastodonContent(content: account.displayNameWithFallback, emojis: emojis)
|
||||||
|
let metaContent = try MastodonMetaContent.convert(document: content)
|
||||||
|
viewModel.authorName = metaContent
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
let metaContent = PlaintextMetaContent(string: account.displayNameWithFallback)
|
||||||
|
viewModel.authorName = metaContent
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.authorUsername = account.acct
|
||||||
|
|
||||||
|
do {
|
||||||
|
let content = MastodonContent(content: account.note, emojis: emojis)
|
||||||
|
let metaContent = try MastodonMetaContent.convert(document: content)
|
||||||
|
viewModel.bioContent = metaContent
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
let metaContent = PlaintextMetaContent(string: account.note)
|
||||||
|
viewModel.bioContent = metaContent
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButtonState(with: relationship, isMe: false)
|
||||||
|
}
|
||||||
|
}
|
@ -14,16 +14,12 @@ import MastodonLocalization
|
|||||||
import MastodonAsset
|
import MastodonAsset
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
|
import MastodonUI
|
||||||
|
|
||||||
extension ProfileCardView {
|
extension ProfileCardView {
|
||||||
public class ViewModel: ObservableObject {
|
public class ViewModel: ObservableObject {
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
public let relationshipViewModel = RelationshipViewModel()
|
|
||||||
|
|
||||||
@Published public var userInterfaceStyle: UIUserInterfaceStyle?
|
|
||||||
@Published public var backgroundColor: UIColor?
|
|
||||||
|
|
||||||
// Author
|
// Author
|
||||||
@Published public var authorBannerImageURL: URL?
|
@Published public var authorBannerImageURL: URL?
|
||||||
@Published public var authorAvatarImageURL: URL?
|
@Published public var authorAvatarImageURL: URL?
|
||||||
@ -45,53 +41,19 @@ extension ProfileCardView {
|
|||||||
@Published public var groupedAccessibilityLabel = ""
|
@Published public var groupedAccessibilityLabel = ""
|
||||||
|
|
||||||
@Published public var familiarFollowers: Mastodon.Entity.FamiliarFollowers?
|
@Published public var familiarFollowers: Mastodon.Entity.FamiliarFollowers?
|
||||||
|
|
||||||
init() {
|
|
||||||
backgroundColor = .systemBackground
|
|
||||||
$userInterfaceStyle
|
|
||||||
.sink { [weak self] userInterfaceStyle in
|
|
||||||
guard let self = self else { return }
|
|
||||||
guard let userInterfaceStyle = userInterfaceStyle else { return }
|
|
||||||
switch userInterfaceStyle {
|
|
||||||
case .dark:
|
|
||||||
self.backgroundColor = .secondarySystemBackground
|
|
||||||
case .light, .unspecified:
|
|
||||||
self.backgroundColor = Asset.Scene.Discovery.profileCardBackground.color
|
|
||||||
@unknown default:
|
|
||||||
self.backgroundColor = Asset.Scene.Discovery.profileCardBackground.color
|
|
||||||
assertionFailure()
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProfileCardView.ViewModel {
|
extension ProfileCardView.ViewModel {
|
||||||
func bind(view: ProfileCardView) {
|
func bind(view: ProfileCardView) {
|
||||||
bindAppearacne(view: view)
|
|
||||||
bindHeader(view: view)
|
bindHeader(view: view)
|
||||||
bindUser(view: view)
|
bindUser(view: view)
|
||||||
bindBio(view: view)
|
bindBio(view: view)
|
||||||
bindRelationship(view: view)
|
|
||||||
bindDashboard(view: view)
|
bindDashboard(view: view)
|
||||||
bindFamiliarFollowers(view: view)
|
bindFamiliarFollowers(view: view)
|
||||||
bindAccessibility(view: view)
|
bindAccessibility(view: view)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func bindAppearacne(view: ProfileCardView) {
|
|
||||||
userInterfaceStyle = view.traitCollection.userInterfaceStyle
|
|
||||||
|
|
||||||
$backgroundColor
|
|
||||||
.assign(to: \.backgroundColor, on: view.container)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
$backgroundColor
|
|
||||||
.assign(to: \.backgroundColor, on: view.avatarButtonBackgroundView)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private func bindHeader(view: ProfileCardView) {
|
private func bindHeader(view: ProfileCardView) {
|
||||||
$authorBannerImageURL
|
$authorBannerImageURL
|
||||||
.sink { url in
|
.sink { url in
|
||||||
@ -152,30 +114,7 @@ extension ProfileCardView.ViewModel {
|
|||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func bindRelationship(view: ProfileCardView) {
|
|
||||||
relationshipViewModel.$optionSet
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { relationshipActionSet in
|
|
||||||
let relationshipActionSet = relationshipActionSet ?? .follow
|
|
||||||
view.relationshipActionButton.configure(actionOptionSet: relationshipActionSet)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func bindDashboard(view: ProfileCardView) {
|
private func bindDashboard(view: ProfileCardView) {
|
||||||
relationshipViewModel.$isMyself
|
|
||||||
.sink { isMyself in
|
|
||||||
if isMyself {
|
|
||||||
view.statusDashboardView.postDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.myPosts
|
|
||||||
view.statusDashboardView.followingDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.myFollowing
|
|
||||||
view.statusDashboardView.followersDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.myFollowers
|
|
||||||
} else {
|
|
||||||
view.statusDashboardView.postDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.otherPosts
|
|
||||||
view.statusDashboardView.followingDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.otherFollowing
|
|
||||||
view.statusDashboardView.followersDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.otherFollowers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
$statusesCount
|
$statusesCount
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { count in
|
.sink { count in
|
||||||
@ -212,11 +151,7 @@ extension ProfileCardView.ViewModel {
|
|||||||
view.familiarFollowersDashboardView.configure(familiarFollowers: familiarFollowers)
|
view.familiarFollowersDashboardView.configure(familiarFollowers: familiarFollowers)
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
$backgroundColor
|
|
||||||
.assign(to: \.backgroundColor, on: view.familiarFollowersDashboardView.viewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func bindAccessibility(view: ProfileCardView) {
|
private func bindAccessibility(view: ProfileCardView) {
|
||||||
let authorAccessibilityLabel = Publishers.CombineLatest(
|
let authorAccessibilityLabel = Publishers.CombineLatest(
|
||||||
$authorName,
|
$authorName,
|
||||||
@ -244,7 +179,7 @@ extension ProfileCardView.ViewModel {
|
|||||||
.map {
|
.map {
|
||||||
AXCustomContent(
|
AXCustomContent(
|
||||||
label: L10n.Scene.Profile.Dashboard.otherPosts,
|
label: L10n.Scene.Profile.Dashboard.otherPosts,
|
||||||
value: $0
|
value: String(describing: $0)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
let followingContent = $followingCount
|
let followingContent = $followingCount
|
||||||
@ -252,7 +187,7 @@ extension ProfileCardView.ViewModel {
|
|||||||
.map {
|
.map {
|
||||||
AXCustomContent(
|
AXCustomContent(
|
||||||
label: L10n.Scene.Profile.Dashboard.otherFollowing,
|
label: L10n.Scene.Profile.Dashboard.otherFollowing,
|
||||||
value: $0
|
value: String(describing: $0)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
let followersContent = $followersCount
|
let followersContent = $followersCount
|
||||||
@ -260,11 +195,11 @@ extension ProfileCardView.ViewModel {
|
|||||||
.map {
|
.map {
|
||||||
AXCustomContent(
|
AXCustomContent(
|
||||||
label: L10n.Scene.Profile.Dashboard.otherFollowers,
|
label: L10n.Scene.Profile.Dashboard.otherFollowers,
|
||||||
value: $0
|
value: String(describing: $0)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
let familiarContent = view.familiarFollowersDashboardView.viewModel.$label
|
let familiarContent = view.familiarFollowersDashboardView.viewModel.$label
|
||||||
.map { $0?.accessibilityLabel }
|
.map { $0?.accessibilityLabel ?? ""}
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.map {
|
.map {
|
||||||
AXCustomContent(
|
AXCustomContent(
|
@ -9,9 +9,12 @@ import UIKit
|
|||||||
import Combine
|
import Combine
|
||||||
import MetaTextKit
|
import MetaTextKit
|
||||||
import MastodonAsset
|
import MastodonAsset
|
||||||
|
import MastodonUI
|
||||||
|
import MastodonLocalization
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
public protocol ProfileCardViewDelegate: AnyObject {
|
public protocol ProfileCardViewDelegate: AnyObject {
|
||||||
func profileCardView(_ profileCardView: ProfileCardView, relationshipButtonDidPressed button: ProfileRelationshipActionButton)
|
func profileCardView(_ profileCardView: ProfileCardView, relationshipButtonDidPressed button: UIButton)
|
||||||
func profileCardView(_ profileCardView: ProfileCardView, familiarFollowersDashboardViewDidPressed view: FamiliarFollowersDashboardView)
|
func profileCardView(_ profileCardView: ProfileCardView, familiarFollowersDashboardViewDidPressed view: FamiliarFollowersDashboardView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,12 +90,22 @@ public final class ProfileCardView: UIView, AXCustomContentProvider {
|
|||||||
|
|
||||||
let statusDashboardView = ProfileStatusDashboardView()
|
let statusDashboardView = ProfileStatusDashboardView()
|
||||||
|
|
||||||
let relationshipActionButtonShadowContainer = ShadowBackgroundContainer()
|
public let followButtonWrapper = UIView()
|
||||||
let relationshipActionButton: ProfileRelationshipActionButton = {
|
public let followButton: UIButton = {
|
||||||
let button = ProfileRelationshipActionButton()
|
var buttonConfiguration = UIButton.Configuration.filled()
|
||||||
button.titleLabel?.font = .systemFont(ofSize: 17, weight: .semibold)
|
buttonConfiguration.background.cornerRadius = 10
|
||||||
button.titleLabel?.adjustsFontSizeToFitWidth = true
|
|
||||||
button.titleLabel?.minimumScaleFactor = 0.5
|
let button = UIButton(configuration: buttonConfiguration)
|
||||||
|
button.isHidden = true
|
||||||
|
button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
button.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
|
button.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
button.widthAnchor.constraint(equalToConstant: 96),
|
||||||
|
button.heightAnchor.constraint(equalToConstant: 36)
|
||||||
|
])
|
||||||
|
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -135,6 +148,7 @@ extension ProfileCardView {
|
|||||||
container.axis = .vertical
|
container.axis = .vertical
|
||||||
container.spacing = 8
|
container.spacing = 8
|
||||||
container.translatesAutoresizingMaskIntoConstraints = false
|
container.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
container.backgroundColor = Asset.Scene.Discovery.profileCardBackground.color
|
||||||
addSubview(container)
|
addSubview(container)
|
||||||
container.pinToParent()
|
container.pinToParent()
|
||||||
|
|
||||||
@ -183,6 +197,7 @@ extension ProfileCardView {
|
|||||||
avatarButton.heightAnchor.constraint(equalToConstant: ProfileCardView.avatarSize.height).priority(.required - 1),
|
avatarButton.heightAnchor.constraint(equalToConstant: ProfileCardView.avatarSize.height).priority(.required - 1),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
avatarButtonBackgroundView.backgroundColor = Asset.Scene.Discovery.profileCardBackground.color
|
||||||
avatarButtonBackgroundView.layer.masksToBounds = true
|
avatarButtonBackgroundView.layer.masksToBounds = true
|
||||||
avatarButtonBackgroundView.layer.cornerCurve = .continuous
|
avatarButtonBackgroundView.layer.cornerCurve = .continuous
|
||||||
avatarButtonBackgroundView.layer.cornerRadius = 12 + 1
|
avatarButtonBackgroundView.layer.cornerRadius = 12 + 1
|
||||||
@ -225,16 +240,15 @@ extension ProfileCardView {
|
|||||||
infoContainer.addArrangedSubview(UIView())
|
infoContainer.addArrangedSubview(UIView())
|
||||||
infoContainerSpacer.setContentHuggingPriority(.defaultLow - 100, for: .vertical)
|
infoContainerSpacer.setContentHuggingPriority(.defaultLow - 100, for: .vertical)
|
||||||
infoContainerSpacer.setContentHuggingPriority(.defaultLow - 100, for: .horizontal)
|
infoContainerSpacer.setContentHuggingPriority(.defaultLow - 100, for: .horizontal)
|
||||||
let relationshipActionButtonShadowContainer = ShadowBackgroundContainer()
|
infoContainer.addArrangedSubview(followButtonWrapper)
|
||||||
relationshipActionButtonShadowContainer.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
infoContainer.addArrangedSubview(relationshipActionButtonShadowContainer)
|
|
||||||
|
|
||||||
relationshipActionButton.translatesAutoresizingMaskIntoConstraints = false
|
followButtonWrapper.translatesAutoresizingMaskIntoConstraints = false
|
||||||
relationshipActionButtonShadowContainer.addSubview(relationshipActionButton)
|
followButtonWrapper.addSubview(followButton)
|
||||||
relationshipActionButton.pinToParent()
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
relationshipActionButtonShadowContainer.widthAnchor.constraint(greaterThanOrEqualToConstant: ProfileCardView.friendshipActionButtonSize.width).priority(.required - 1),
|
followButton.topAnchor.constraint(lessThanOrEqualTo: followButtonWrapper.topAnchor),
|
||||||
relationshipActionButtonShadowContainer.heightAnchor.constraint(equalToConstant: ProfileCardView.friendshipActionButtonSize.height).priority(.required - 1),
|
followButton.leadingAnchor.constraint(equalTo: followButtonWrapper.leadingAnchor),
|
||||||
|
followButtonWrapper.trailingAnchor.constraint(equalTo: followButton.trailingAnchor),
|
||||||
|
followButtonWrapper.bottomAnchor.constraint(greaterThanOrEqualTo: followButton.bottomAnchor),
|
||||||
])
|
])
|
||||||
|
|
||||||
familiarFollowersDashboardViewAdaptiveMarginContainerView.contentView = familiarFollowersDashboardView
|
familiarFollowersDashboardViewAdaptiveMarginContainerView.contentView = familiarFollowersDashboardView
|
||||||
@ -248,17 +262,15 @@ extension ProfileCardView {
|
|||||||
bottomPadding.heightAnchor.constraint(equalToConstant: 8).priority(.required - 10),
|
bottomPadding.heightAnchor.constraint(equalToConstant: 8).priority(.required - 10),
|
||||||
])
|
])
|
||||||
|
|
||||||
relationshipActionButton.addTarget(self, action: #selector(ProfileCardView.relationshipActionButtonDidPressed(_:)), for: .touchUpInside)
|
followButton.addTarget(self, action: #selector(ProfileCardView.relationshipActionButtonDidPressed(_:)), for: .touchUpInside)
|
||||||
|
|
||||||
let familiarFollowersDashboardViewTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
|
let familiarFollowersDashboardViewTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
|
||||||
familiarFollowersDashboardViewTapGestureRecognizer.addTarget(self, action: #selector(ProfileCardView.familiarFollowersDashboardViewDidPressed(_:)))
|
familiarFollowersDashboardViewTapGestureRecognizer.addTarget(self, action: #selector(ProfileCardView.familiarFollowersDashboardViewDidPressed(_:)))
|
||||||
familiarFollowersDashboardView.addGestureRecognizer(familiarFollowersDashboardViewTapGestureRecognizer)
|
familiarFollowersDashboardView.addGestureRecognizer(familiarFollowersDashboardViewTapGestureRecognizer)
|
||||||
}
|
|
||||||
|
|
||||||
public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
statusDashboardView.postDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.otherPosts
|
||||||
super.traitCollectionDidChange(previousTraitCollection)
|
statusDashboardView.followingDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.otherFollowing
|
||||||
|
statusDashboardView.followersDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.otherFollowers
|
||||||
viewModel.userInterfaceStyle = traitCollection.userInterfaceStyle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func layoutSubviews() {
|
public override func layoutSubviews() {
|
||||||
@ -285,8 +297,8 @@ extension ProfileCardView {
|
|||||||
|
|
||||||
extension ProfileCardView {
|
extension ProfileCardView {
|
||||||
@objc private func relationshipActionButtonDidPressed(_ sender: UIButton) {
|
@objc private func relationshipActionButtonDidPressed(_ sender: UIButton) {
|
||||||
assert(sender === relationshipActionButton)
|
assert(sender === followButton)
|
||||||
delegate?.profileCardView(self, relationshipButtonDidPressed: relationshipActionButton)
|
delegate?.profileCardView(self, relationshipButtonDidPressed: followButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func familiarFollowersDashboardViewDidPressed(_ sender: UITapGestureRecognizer) {
|
@objc private func familiarFollowersDashboardViewDidPressed(_ sender: UITapGestureRecognizer) {
|
||||||
@ -294,3 +306,99 @@ extension ProfileCardView {
|
|||||||
delegate?.profileCardView(self, familiarFollowersDashboardViewDidPressed: familiarFollowersDashboardView)
|
delegate?.profileCardView(self, familiarFollowersDashboardViewDidPressed: familiarFollowersDashboardView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ProfileCardView {
|
||||||
|
func updateButtonState(with relationship: Mastodon.Entity.Relationship?, isMe: Bool) {
|
||||||
|
let buttonState: UserView.ButtonState
|
||||||
|
|
||||||
|
if let relationship {
|
||||||
|
if isMe {
|
||||||
|
buttonState = .none
|
||||||
|
} else if relationship.following {
|
||||||
|
buttonState = .unfollow
|
||||||
|
} else if relationship.blocking || (relationship.domainBlocking ?? false) {
|
||||||
|
buttonState = .blocked
|
||||||
|
} else if relationship.requested ?? false {
|
||||||
|
buttonState = .pending
|
||||||
|
} else {
|
||||||
|
buttonState = .follow
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buttonState = .none
|
||||||
|
}
|
||||||
|
|
||||||
|
setButtonState(buttonState)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func setButtonState(_ state: UserView.ButtonState) {
|
||||||
|
// currentButtonState = state
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
|
||||||
|
case .loading:
|
||||||
|
followButtonWrapper.isHidden = false
|
||||||
|
followButton.isHidden = false
|
||||||
|
followButton.configuration?.title = nil
|
||||||
|
followButton.configuration?.showsActivityIndicator = true
|
||||||
|
followButton.configuration?.background.backgroundColor = Asset.Colors.Button.userFollowing.color
|
||||||
|
followButton.configuration?.baseForegroundColor = Asset.Colors.Brand.blurple.color
|
||||||
|
followButton.isEnabled = false
|
||||||
|
|
||||||
|
case .follow:
|
||||||
|
followButtonWrapper.isHidden = false
|
||||||
|
followButton.isHidden = false
|
||||||
|
followButton.configuration?.title = L10n.Common.Controls.Friendship.follow
|
||||||
|
followButton.configuration?.showsActivityIndicator = false
|
||||||
|
followButton.configuration?.background.backgroundColor = Asset.Colors.Button.userFollow.color
|
||||||
|
followButton.configuration?.baseForegroundColor = .white
|
||||||
|
followButton.isEnabled = true
|
||||||
|
|
||||||
|
case .request:
|
||||||
|
followButtonWrapper.isHidden = false
|
||||||
|
followButton.isHidden = false
|
||||||
|
followButton.configuration?.title = L10n.Common.Controls.Friendship.request
|
||||||
|
followButton.configuration?.showsActivityIndicator = false
|
||||||
|
followButton.configuration?.background.backgroundColor = Asset.Colors.Button.userFollow.color
|
||||||
|
followButton.configuration?.baseForegroundColor = .white
|
||||||
|
followButton.isEnabled = true
|
||||||
|
|
||||||
|
case .pending:
|
||||||
|
followButtonWrapper.isHidden = false
|
||||||
|
followButton.isHidden = false
|
||||||
|
followButton.configuration?.title = L10n.Common.Controls.Friendship.pending
|
||||||
|
followButton.configuration?.baseForegroundColor = Asset.Colors.Button.userFollowingTitle.color
|
||||||
|
followButton.configuration?.showsActivityIndicator = false
|
||||||
|
followButton.configuration?.background.backgroundColor = Asset.Colors.Button.userFollowing.color
|
||||||
|
followButton.isEnabled = true
|
||||||
|
|
||||||
|
case .unfollow:
|
||||||
|
followButtonWrapper.isHidden = false
|
||||||
|
followButton.isHidden = false
|
||||||
|
followButton.configuration?.title = L10n.Common.Controls.Friendship.following
|
||||||
|
followButton.configuration?.showsActivityIndicator = false
|
||||||
|
followButton.configuration?.background.backgroundColor = Asset.Colors.Button.userFollowing.color
|
||||||
|
followButton.configuration?.baseForegroundColor = Asset.Colors.Button.userFollowingTitle.color
|
||||||
|
followButton.isEnabled = true
|
||||||
|
|
||||||
|
case .blocked:
|
||||||
|
followButtonWrapper.isHidden = false
|
||||||
|
followButton.isHidden = false
|
||||||
|
followButton.configuration?.title = L10n.Common.Controls.Friendship.blocked
|
||||||
|
followButton.configuration?.showsActivityIndicator = false
|
||||||
|
followButton.configuration?.background.backgroundColor = Asset.Colors.Button.userBlocked.color
|
||||||
|
followButton.configuration?.baseForegroundColor = .systemRed
|
||||||
|
followButton.isEnabled = true
|
||||||
|
|
||||||
|
case .none:
|
||||||
|
followButtonWrapper.isHidden = true
|
||||||
|
followButton.isHidden = true
|
||||||
|
followButton.configuration?.title = nil
|
||||||
|
followButton.configuration?.showsActivityIndicator = false
|
||||||
|
followButton.configuration?.background.backgroundColor = .clear
|
||||||
|
followButton.isEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
followButton.titleLabel?.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .boldSystemFont(ofSize: 15))
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// CachedProfileViewModel.swift
|
|
||||||
// Mastodon
|
|
||||||
//
|
|
||||||
// Created by MainasuK Cirno on 2021-3-31.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import CoreDataStack
|
|
||||||
import MastodonCore
|
|
||||||
|
|
||||||
final class CachedProfileViewModel: ProfileViewModel {
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
init(context: AppContext, authContext: AuthContext, mastodonUser: MastodonUser) {
|
|
||||||
super.init(context: context, authContext: authContext, optionalMastodonUser: mastodonUser)
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,6 +12,7 @@ import MastodonCore
|
|||||||
|
|
||||||
protocol ContentSplitViewControllerDelegate: AnyObject {
|
protocol ContentSplitViewControllerDelegate: AnyObject {
|
||||||
func contentSplitViewController(_ contentSplitViewController: ContentSplitViewController, sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab)
|
func contentSplitViewController(_ contentSplitViewController: ContentSplitViewController, sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab)
|
||||||
|
func contentSplitViewController(_ contentSplitViewController: ContentSplitViewController, sidebarViewController: SidebarViewController, didDoubleTapTab tab: MainTabBarController.Tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ContentSplitViewController: UIViewController, NeedsDependency {
|
final class ContentSplitViewController: UIViewController, NeedsDependency {
|
||||||
@ -121,16 +122,7 @@ extension ContentSplitViewController: SidebarViewControllerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sidebarViewController(_ sidebarViewController: SidebarViewController, didDoubleTapItem item: SidebarViewModel.Item, sourceView: UIView) {
|
func sidebarViewController(_ sidebarViewController: SidebarViewController, didDoubleTapItem item: SidebarViewModel.Item, sourceView: UIView) {
|
||||||
guard case let .tab(tab) = item, tab == .me else { return }
|
guard case let .tab(tab) = item else { return }
|
||||||
guard let authContext = authContext else { return }
|
delegate?.contentSplitViewController(self, sidebarViewController: sidebarViewController, didDoubleTapTab: tab)
|
||||||
assert(Thread.isMainThread)
|
|
||||||
|
|
||||||
guard let nextAccount = context.nextAccount(in: authContext) else { return }
|
|
||||||
|
|
||||||
Task { @MainActor in
|
|
||||||
let isActive = try await context.authenticationService.activeMastodonUser(domain: nextAccount.domain, userID: nextAccount.userID)
|
|
||||||
guard isActive else { return }
|
|
||||||
self.coordinator.setup()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,12 +298,11 @@ extension MainTabBarController {
|
|||||||
tabBarLongPressGestureRecognizer.delegate = self
|
tabBarLongPressGestureRecognizer.delegate = self
|
||||||
tabBar.addGestureRecognizer(tabBarLongPressGestureRecognizer)
|
tabBar.addGestureRecognizer(tabBarLongPressGestureRecognizer)
|
||||||
|
|
||||||
// todo: reconsider the "double tap to change account" feature -> https://github.com/mastodon/mastodon-ios/issues/628
|
let tabBarDoubleTapGestureRecognizer = UITapGestureRecognizer()
|
||||||
// let tabBarDoubleTapGestureRecognizer = UITapGestureRecognizer()
|
tabBarDoubleTapGestureRecognizer.numberOfTapsRequired = 2
|
||||||
// tabBarDoubleTapGestureRecognizer.numberOfTapsRequired = 2
|
tabBarDoubleTapGestureRecognizer.addTarget(self, action: #selector(MainTabBarController.tabBarDoubleTapGestureRecognizerHandler(_:)))
|
||||||
// tabBarDoubleTapGestureRecognizer.addTarget(self, action: #selector(MainTabBarController.tabBarDoubleTapGestureRecognizerHandler(_:)))
|
tabBarDoubleTapGestureRecognizer.delaysTouchesEnded = false
|
||||||
// tabBarDoubleTapGestureRecognizer.delaysTouchesEnded = false
|
tabBar.addGestureRecognizer(tabBarDoubleTapGestureRecognizer)
|
||||||
// tabBar.addGestureRecognizer(tabBarDoubleTapGestureRecognizer)
|
|
||||||
|
|
||||||
self.isReadyForWizardAvatarButton = authContext != nil
|
self.isReadyForWizardAvatarButton = authContext != nil
|
||||||
|
|
||||||
@ -365,17 +364,10 @@ extension MainTabBarController {
|
|||||||
guard let tab = touchedTab(by: sender) else { return }
|
guard let tab = touchedTab(by: sender) else { return }
|
||||||
|
|
||||||
switch tab {
|
switch tab {
|
||||||
case .me:
|
case .search:
|
||||||
guard let authContext = authContext else { return }
|
|
||||||
assert(Thread.isMainThread)
|
assert(Thread.isMainThread)
|
||||||
|
// double tapping search tab opens the search bar without additional taps
|
||||||
guard let nextAccount = context.nextAccount(in: authContext) else { return }
|
searchViewController?.searchBar.becomeFirstResponder()
|
||||||
|
|
||||||
Task { @MainActor in
|
|
||||||
let isActive = try await context.authenticationService.activeMastodonUser(domain: nextAccount.domain, userID: nextAccount.userID)
|
|
||||||
guard isActive else { return }
|
|
||||||
self.coordinator.setup()
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -504,7 +496,7 @@ extension MainTabBarController: UITabBarControllerDelegate {
|
|||||||
|
|
||||||
// Assert index is as same as the tab rawValue. This check needs to be done `shouldSelect`
|
// Assert index is as same as the tab rawValue. This check needs to be done `shouldSelect`
|
||||||
// because the nav controller has already popped in `didSelect`.
|
// because the nav controller has already popped in `didSelect`.
|
||||||
if currentTab.rawValue == tabBarController.selectedIndex,
|
if currentTab.rawValue == viewController.tabBarItem.tag,
|
||||||
let navigationController = viewController as? UINavigationController,
|
let navigationController = viewController as? UINavigationController,
|
||||||
navigationController.viewControllers.count == 1,
|
navigationController.viewControllers.count == 1,
|
||||||
let scrollViewContainer = navigationController.topViewController as? ScrollViewContainer {
|
let scrollViewContainer = navigationController.topViewController as? ScrollViewContainer {
|
||||||
|
@ -157,6 +157,24 @@ extension RootSplitViewController: ContentSplitViewControllerDelegate {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func contentSplitViewController(_ contentSplitViewController: ContentSplitViewController, sidebarViewController: SidebarViewController, didDoubleTapTab tab: MainTabBarController.Tab) {
|
||||||
|
guard let _ = MainTabBarController.Tab.allCases.firstIndex(of: tab) else {
|
||||||
|
assertionFailure()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tab {
|
||||||
|
case .search:
|
||||||
|
// allow double tap to focus search bar only when is not primary display (iPad potrait)
|
||||||
|
guard !isPrimaryDisplay else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
contentSplitViewController.mainTabBarController.searchViewController?.searchBar.becomeFirstResponder()
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UISplitViewControllerDelegate
|
// MARK: - UISplitViewControllerDelegate
|
||||||
|
@ -128,13 +128,12 @@ extension SidebarViewController {
|
|||||||
sidebarLongPressGestureRecognizer.addTarget(self, action: #selector(SidebarViewController.sidebarLongPressGestureRecognizerHandler(_:)))
|
sidebarLongPressGestureRecognizer.addTarget(self, action: #selector(SidebarViewController.sidebarLongPressGestureRecognizerHandler(_:)))
|
||||||
collectionView.addGestureRecognizer(sidebarLongPressGestureRecognizer)
|
collectionView.addGestureRecognizer(sidebarLongPressGestureRecognizer)
|
||||||
|
|
||||||
// todo: reconsider the "double tap to change account" feature -> https://github.com/mastodon/mastodon-ios/issues/628
|
let sidebarDoubleTapGestureRecognizer = UITapGestureRecognizer()
|
||||||
// let sidebarDoubleTapGestureRecognizer = UITapGestureRecognizer()
|
sidebarDoubleTapGestureRecognizer.numberOfTapsRequired = 2
|
||||||
// sidebarDoubleTapGestureRecognizer.numberOfTapsRequired = 2
|
sidebarDoubleTapGestureRecognizer.addTarget(self, action: #selector(SidebarViewController.sidebarDoubleTapGestureRecognizerHandler(_:)))
|
||||||
// sidebarDoubleTapGestureRecognizer.addTarget(self, action: #selector(SidebarViewController.sidebarDoubleTapGestureRecognizerHandler(_:)))
|
sidebarDoubleTapGestureRecognizer.delaysTouchesEnded = false
|
||||||
// sidebarDoubleTapGestureRecognizer.delaysTouchesEnded = false
|
sidebarDoubleTapGestureRecognizer.cancelsTouchesInView = true
|
||||||
// sidebarDoubleTapGestureRecognizer.cancelsTouchesInView = true
|
collectionView.addGestureRecognizer(sidebarDoubleTapGestureRecognizer)
|
||||||
// collectionView.addGestureRecognizer(sidebarDoubleTapGestureRecognizer)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,6 +233,7 @@ extension SearchDetailViewController {
|
|||||||
navigationItem.setHidesBackButton(true, animated: false)
|
navigationItem.setHidesBackButton(true, animated: false)
|
||||||
navigationItem.titleView = nil
|
navigationItem.titleView = nil
|
||||||
navigationItem.searchController = searchController
|
navigationItem.searchController = searchController
|
||||||
|
navigationItem.preferredSearchBarPlacement = .stacked
|
||||||
searchController.searchBar.sizeToFit()
|
searchController.searchBar.sizeToFit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
"color-space" : "srgb",
|
"color-space" : "srgb",
|
||||||
"components" : {
|
"components" : {
|
||||||
"alpha" : "1.000",
|
"alpha" : "1.000",
|
||||||
"blue" : "4",
|
"blue" : "0x1E",
|
||||||
"green" : "5",
|
"green" : "0x1C",
|
||||||
"red" : "6"
|
"red" : "0x1C"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
|
@ -22,7 +22,7 @@ extension FamiliarFollowersDashboardView {
|
|||||||
@Published var emojis: MastodonContent.Emojis = [:]
|
@Published var emojis: MastodonContent.Emojis = [:]
|
||||||
@Published var backgroundColor: UIColor?
|
@Published var backgroundColor: UIColor?
|
||||||
|
|
||||||
@Published var label: MetaContent?
|
@Published public var label: MetaContent?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,15 +63,3 @@ extension FamiliarFollowersDashboardView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
import SwiftUI
|
|
||||||
struct FamiliarFollowersDashboardView_Preview: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
UIViewPreview {
|
|
||||||
FamiliarFollowersDashboardView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
//
|
|
||||||
// ProfileCardView+Configuration.swift
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by MainasuK on 2022-4-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Combine
|
|
||||||
import CoreDataStack
|
|
||||||
import Meta
|
|
||||||
import MastodonCore
|
|
||||||
import MastodonMeta
|
|
||||||
import MastodonSDK
|
|
||||||
|
|
||||||
extension ProfileCardView {
|
|
||||||
|
|
||||||
public func configure(user: MastodonUser) {
|
|
||||||
// banner
|
|
||||||
user.publisher(for: \.header)
|
|
||||||
.map { URL(string: $0) }
|
|
||||||
.assign(to: \.authorBannerImageURL, on: viewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
// author avatar
|
|
||||||
Publishers.CombineLatest3(
|
|
||||||
user.publisher(for: \.avatar),
|
|
||||||
user.publisher(for: \.avatarStatic),
|
|
||||||
UserDefaults.shared.publisher(for: \.preferredStaticAvatar)
|
|
||||||
)
|
|
||||||
.map { _ in user.avatarImageURL() }
|
|
||||||
.assign(to: \.authorAvatarImageURL, on: viewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
// name
|
|
||||||
Publishers.CombineLatest(
|
|
||||||
user.publisher(for: \.displayName),
|
|
||||||
user.publisher(for: \.emojis)
|
|
||||||
)
|
|
||||||
.map { _, emojis in
|
|
||||||
do {
|
|
||||||
let content = MastodonContent(content: user.displayNameWithFallback, emojis: emojis.asDictionary)
|
|
||||||
let metaContent = try MastodonMetaContent.convert(document: content)
|
|
||||||
return metaContent
|
|
||||||
} catch {
|
|
||||||
assertionFailure(error.localizedDescription)
|
|
||||||
return PlaintextMetaContent(string: user.displayNameWithFallback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.assign(to: \.authorName, on: viewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
// username
|
|
||||||
user.publisher(for: \.acct)
|
|
||||||
.map { $0 as String? }
|
|
||||||
.assign(to: \.authorUsername, on: viewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
// bio
|
|
||||||
Publishers.CombineLatest(
|
|
||||||
user.publisher(for: \.note),
|
|
||||||
user.publisher(for: \.emojis)
|
|
||||||
)
|
|
||||||
.map { note, emojis in
|
|
||||||
guard let note = note else { return nil }
|
|
||||||
do {
|
|
||||||
let content = MastodonContent(content: note, emojis: emojis.asDictionary)
|
|
||||||
let metaContent = try MastodonMetaContent.convert(document: content)
|
|
||||||
return metaContent
|
|
||||||
} catch {
|
|
||||||
assertionFailure(error.localizedDescription)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.assign(to: \.bioContent, on: viewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
// relationship
|
|
||||||
viewModel.relationshipViewModel.user = user
|
|
||||||
// dashboard
|
|
||||||
user.publisher(for: \.statusesCount)
|
|
||||||
.map { Int($0) }
|
|
||||||
.assign(to: \.statusesCount, on: viewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
user.publisher(for: \.followingCount)
|
|
||||||
.map { Int($0) }
|
|
||||||
.assign(to: \.followingCount, on: viewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
user.publisher(for: \.followersCount)
|
|
||||||
.map { Int($0) }
|
|
||||||
.assign(to: \.followersCount, on: viewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user