From 01bbfc31f2b72784deb3b6eb0b7b5709d60eb0a5 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Wed, 8 Nov 2023 21:49:21 -0500 Subject: [PATCH] visionOS: Improve suggested profile card appearance --- Tusker.xcodeproj/project.pbxproj | 4 + .../Explore/SuggestedProfileCardView.swift | 78 +++++++++++++++++++ .../Explore/TrendsViewController.swift | 12 +++ 3 files changed, 94 insertions(+) create mode 100644 Tusker/Screens/Explore/SuggestedProfileCardView.swift diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index a63ffa47..bef1ba2f 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -255,6 +255,7 @@ D6B9366F2828452F00237D0E /* SavedHashtag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B9366E2828452F00237D0E /* SavedHashtag.swift */; }; D6B936712829F72900237D0E /* NSManagedObjectContext+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B936702829F72900237D0E /* NSManagedObjectContext+Helpers.swift */; }; D6BC74842AFC3DF9000DD603 /* TrendingLinkCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BC74832AFC3DF9000DD603 /* TrendingLinkCardView.swift */; }; + D6BC74862AFC4772000DD603 /* SuggestedProfileCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BC74852AFC4772000DD603 /* SuggestedProfileCardView.swift */; }; D6BC9DB1232C61BC002CA326 /* NotificationsPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BC9DB0232C61BC002CA326 /* NotificationsPageViewController.swift */; }; D6BC9DB3232D4C07002CA326 /* WellnessPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BC9DB2232D4C07002CA326 /* WellnessPrefsView.swift */; }; D6BC9DD7232D7811002CA326 /* TimelinesPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BC9DD6232D7811002CA326 /* TimelinesPageViewController.swift */; }; @@ -655,6 +656,7 @@ D6B9366E2828452F00237D0E /* SavedHashtag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedHashtag.swift; sourceTree = ""; }; D6B936702829F72900237D0E /* NSManagedObjectContext+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Helpers.swift"; sourceTree = ""; }; D6BC74832AFC3DF9000DD603 /* TrendingLinkCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingLinkCardView.swift; sourceTree = ""; }; + D6BC74852AFC4772000DD603 /* SuggestedProfileCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestedProfileCardView.swift; sourceTree = ""; }; D6BC9DB0232C61BC002CA326 /* NotificationsPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsPageViewController.swift; sourceTree = ""; }; D6BC9DB2232D4C07002CA326 /* WellnessPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WellnessPrefsView.swift; sourceTree = ""; }; D6BC9DD6232D7811002CA326 /* TimelinesPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinesPageViewController.swift; sourceTree = ""; }; @@ -907,6 +909,7 @@ D6BC74832AFC3DF9000DD603 /* TrendingLinkCardView.swift */, D601FA81297EEC3F00A8E8B5 /* SuggestedProfileCardCollectionViewCell.swift */, D601FA82297EEC3F00A8E8B5 /* SuggestedProfileCardCollectionViewCell.xib */, + D6BC74852AFC4772000DD603 /* SuggestedProfileCardView.swift */, D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */, D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */, D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */, @@ -1981,6 +1984,7 @@ D61F759B29384F9C00C0B37F /* FilterMO.swift in Sources */, D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */, D64AAE9126C80DC600FC57FB /* ToastView.swift in Sources */, + D6BC74862AFC4772000DD603 /* SuggestedProfileCardView.swift in Sources */, 0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */, D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */, D61F75942936F0DA00C0B37F /* FollowedHashtag.swift in Sources */, diff --git a/Tusker/Screens/Explore/SuggestedProfileCardView.swift b/Tusker/Screens/Explore/SuggestedProfileCardView.swift new file mode 100644 index 00000000..b6296403 --- /dev/null +++ b/Tusker/Screens/Explore/SuggestedProfileCardView.swift @@ -0,0 +1,78 @@ +// +// SuggestedProfileCardView.swift +// Tusker +// +// Created by Shadowfacts on 11/8/23. +// Copyright © 2023 Shadowfacts. All rights reserved. +// + +#if os(visionOS) +import SwiftUI + +struct SuggestedProfileCardView: View { + let account: AccountMO + + var body: some View { + VStack { + HeaderLayout { + AsyncImage(url: account.header) { image in + image + .resizable() + } placeholder: { + Rectangle().fill(.tertiary) + } + AsyncImage(url: account.avatar) { image in + image + .resizable() + .clipShape(RoundedRectangle(cornerRadius: 5)) + } placeholder: { + Rectangle().fill(.tertiary) + } + VStack(alignment: .leading) { + AccountDisplayNameView(account: account, textStyle: .title, emojiSize: 24) + Text(verbatim: "@\(account.acct)") + } + } + + NoteTextView(note: account.note) + } + .glassBackgroundEffect(in: RoundedRectangle(cornerRadius: 12.5)) + } +} + +private struct HeaderLayout: Layout { + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + let acceptedWidth = proposal.width ?? 200 + let avatarSize = subviews[1].sizeThatFits(ProposedViewSize(width: 86, height: 86)) + let accountInfoSize = subviews[2].sizeThatFits(ProposedViewSize(width: acceptedWidth - 8 - avatarSize.width, height: 43)) + return CGSize(width: proposal.width ?? 200, height: 100 + 4 + accountInfoSize.height) + } + + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + let headerSize = subviews[0].sizeThatFits(ProposedViewSize(width: bounds.width, height: 100)) + subviews[0].place(at: .zero, proposal: ProposedViewSize(headerSize)) + let avatarSize = subviews[1].sizeThatFits(ProposedViewSize(width: 86, height: 86)) + subviews[1].place(at: CGPoint(x: 8, y: headerSize.height), anchor: .leading, proposal: ProposedViewSize(avatarSize)) + subviews[2].place(at: CGPoint(x: 8 + avatarSize.width + 8, y: headerSize.height + 4), proposal: ProposedViewSize(width: bounds.width - 8 - avatarSize.width - 8, height: 43)) + } +} + +private struct NoteTextView: UIViewRepresentable { + typealias UIViewType = StatusContentTextView + let note: String + func makeUIView(context: Context) -> StatusContentTextView { + let view = StatusContentTextView() + view.isUserInteractionEnabled = false + view.isScrollEnabled = true + view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + return view + } + func updateUIView(_ uiView: StatusContentTextView, context: Context) { + uiView.setTextFromHtml(note) + } +} + +//#Preview { +// SuggestedProfileCardView() +//} +#endif diff --git a/Tusker/Screens/Explore/TrendsViewController.swift b/Tusker/Screens/Explore/TrendsViewController.swift index dfd75022..1f94172e 100644 --- a/Tusker/Screens/Explore/TrendsViewController.swift +++ b/Tusker/Screens/Explore/TrendsViewController.swift @@ -163,10 +163,22 @@ class TrendsViewController: UIViewController, CollectionViewController { // TODO: filter trends cell.updateUI(statusID: item.0, state: item.1, filterResult: .allow, precomputedContent: nil) } + #if os(visionOS) + let accountCell = UICollectionView.CellRegistration { [unowned self] cell, indexPath, item in + if let account = self.mastodonController.persistentContainer.account(for: item.0) { + cell.contentConfiguration = UIHostingConfiguration(content: { + SuggestedProfileCardView(account: account) + }) + } else { + cell.contentConfiguration = nil + } + } + #else let accountCell = UICollectionView.CellRegistration(cellNib: UINib(nibName: "SuggestedProfileCardCollectionViewCell", bundle: .main)) { [unowned self] cell, indexPath, item in cell.delegate = self cell.updateUI(accountID: item.0, source: item.1) } + #endif let confirmLoadMoreCell = UICollectionView.CellRegistration { [unowned self] cell, indexPath, isLoading in cell.confirmLoadMore = self.confirmLoadMoreStatuses cell.isLoading = isLoading