Revert "Fix AttributeGraph cycle on Profile View"

This reverts commit cba554b6ce.

This commit was reverted because although it fixed one AttributeGraph
cycle, it caused other aesthetic issues.

The fix should be revisited to avoid AttributeGraph cycles.
This commit is contained in:
Daniel D’Aquino
2026-06-08 09:08:46 -07:00
parent bcd79652d8
commit e462dbd53c
+25 -42
View File
@@ -102,9 +102,7 @@ struct ProfileView: View {
colorScheme == .light ? DamusColors.white : DamusColors.black
}
/// Returns the blur opacity for the collapsing banner based on the current scroll offset and top safe-area inset.
func bannerBlurViewOpacity(topSafeAreaInset: CGFloat) -> Double {
let navbarHeight = navbarHeight(topSafeAreaInset: topSafeAreaInset)
func bannerBlurViewOpacity() -> Double {
let progress = -(yOffset + navbarHeight) / 100
return Double(-yOffset > navbarHeight ? progress : 0)
}
@@ -116,9 +114,8 @@ struct ProfileView: View {
return (displayName, "@\(userName)")
}
/// Determines whether the follow button should appear in the collapsed banner state.
func showFollowBtnInBlurrBanner(topSafeAreaInset: CGFloat) -> Bool {
damus_state.contacts.follow_state(profile.pubkey) == .unfollows && bannerBlurViewOpacity(topSafeAreaInset: topSafeAreaInset) > 1.0
func showFollowBtnInBlurrBanner() -> Bool {
damus_state.contacts.follow_state(profile.pubkey) == .unfollows && bannerBlurViewOpacity() > 1.0
}
/// Builds the profile timeline filter, while ensuring the user's own profile bypasses NSFW and hashtag-spam content filters.
@@ -136,12 +133,10 @@ struct ProfileView: View {
return ContentFilters(filters: filters).filter
}
/// Builds the stretching and collapsing banner while avoiding synchronous window safe-area queries on the main thread.
func bannerSection(topSafeAreaInset: CGFloat) -> some View {
var bannerSection: some View {
GeometryReader { proxy -> AnyView in
let minY = proxy.frame(in: .global).minY
let navbarHeight = navbarHeight(topSafeAreaInset: topSafeAreaInset)
let blurOpacity = bannerBlurViewOpacity(topSafeAreaInset: topSafeAreaInset)
DispatchQueue.main.async {
self.yOffset = minY
@@ -155,10 +150,10 @@ struct ProfileView: View {
.frame(width: proxy.size.width, height: minY > 0 ? bannerHeight + minY : bannerHeight)
.clipped()
VisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterial)).opacity(blurOpacity)
VisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterial)).opacity(bannerBlurViewOpacity())
}
Divider().opacity(blurOpacity)
Divider().opacity(bannerBlurViewOpacity())
}
.frame(height: minY > 0 ? bannerHeight + minY : nil)
.offset(y: minY > 0 ? -minY : -minY < navbarHeight ? 0 : -minY - navbarHeight)
@@ -169,9 +164,8 @@ struct ProfileView: View {
.allowsHitTesting(false)
}
/// Computes the profile navigation height using the view's current safe-area context instead of querying the key window during SwiftUI updates.
func navbarHeight(topSafeAreaInset: CGFloat) -> CGFloat {
100.0 - topSafeAreaInset
var navbarHeight: CGFloat {
return 100.0 - (Theme.safeAreaInsets?.top ?? 0)
}
func navImage(img: String) -> some View {
@@ -305,30 +299,27 @@ struct ProfileView: View {
}
}
/// Returns the avatar's vertical offset during banner collapse.
func pfpOffset(topSafeAreaInset: CGFloat) -> CGFloat {
let progress = -yOffset / navbarHeight(topSafeAreaInset: topSafeAreaInset)
func pfpOffset() -> CGFloat {
let progress = -yOffset / navbarHeight
let offset = (pfp_size / 4.0) * (progress < 1.0 ? progress : 1)
return offset > 0 ? offset : 0
}
/// Returns the avatar's scale during banner collapse.
func pfpScale(topSafeAreaInset: CGFloat) -> CGFloat {
let progress = -yOffset / navbarHeight(topSafeAreaInset: topSafeAreaInset)
func pfpScale() -> CGFloat {
let progress = -yOffset / navbarHeight
let scale = 1.0 - (0.5 * (progress < 1.0 ? progress : 1))
return scale < 1 ? scale : 1
}
/// Builds the name and avatar section using geometry-provided safe-area values to avoid AttributeGraph cycles.
func nameSection(ndbprofile: Profile?, lnurl: String?, topSafeAreaInset: CGFloat) -> some View {
func nameSection(ndbprofile: Profile?, lnurl: String?) -> some View {
return Group {
let follows_you = profile.pubkey != damus_state.pubkey && profile.follows(pubkey: damus_state.pubkey)
HStack(alignment: .center) {
ProfilePicView(pubkey: profile.pubkey, size: pfp_size, highlight: .custom(imageBorderColor(), 4.0), profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation, damusState: damus_state)
.padding(.top, -(pfp_size / 2.0))
.offset(y: pfpOffset(topSafeAreaInset: topSafeAreaInset))
.scaleEffect(pfpScale(topSafeAreaInset: topSafeAreaInset))
.offset(y: pfpOffset())
.scaleEffect(pfpScale())
.onTapGesture {
is_zoomed.toggle()
}
@@ -366,13 +357,12 @@ struct ProfileView: View {
}
}
/// Builds the profile summary section using safe-area values from the surrounding geometry.
func aboutSection(topSafeAreaInset: CGFloat) -> some View {
var aboutSection: some View {
VStack(alignment: .leading, spacing: 8.0) {
let lnurl = try? damus_state.profiles.lookup_lnurl(profile.pubkey)
let ndbprofile = try? damus_state.profiles.lookup(id: profile.pubkey)
nameSection(ndbprofile: ndbprofile, lnurl: lnurl, topSafeAreaInset: topSafeAreaInset)
nameSection(ndbprofile: ndbprofile, lnurl: lnurl)
if let about = ndbprofile?.about {
AboutView(state: damus_state, about: about)
@@ -461,20 +451,14 @@ struct ProfileView: View {
}
var body: some View {
GeometryReader { geometry in
let safeAreaInsets = geometry.safeAreaInsets
let topSafeAreaInset = safeAreaInsets.top
let navbarHeight = navbarHeight(topSafeAreaInset: topSafeAreaInset)
let blurOpacity = bannerBlurViewOpacity(topSafeAreaInset: topSafeAreaInset)
ZStack {
ScrollView(.vertical) {
VStack(spacing: 0) {
bannerSection(topSafeAreaInset: topSafeAreaInset)
bannerSection
.zIndex(1)
VStack {
aboutSection(topSafeAreaInset: topSafeAreaInset)
VStack() {
aboutSection
VStack(spacing: 0) {
CustomPicker(tabs: tabs, selection: $filter_state)
@@ -493,11 +477,11 @@ struct ProfileView: View {
InnerTimelineView(events: profile.events, damus: damus_state, filter: content_filter(FilterState.conversations))
}
}
.padding(.horizontal, safeAreaInsets.leading)
.padding(.horizontal, Theme.safeAreaInsets?.left)
.zIndex(-yOffset > navbarHeight ? 0 : 1)
}
}
.padding(.bottom, tabHeight + safeAreaInsets.bottom)
.padding(.bottom, tabHeight + getSafeAreaBottom())
.ignoresSafeArea()
.navigationTitle("")
.navigationBarBackButtonHidden()
@@ -515,14 +499,14 @@ struct ProfileView: View {
.font(.subheadline)
.foregroundColor(.white.opacity(0.8))
}
.opacity(blurOpacity)
.opacity(bannerBlurViewOpacity())
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.top, max(5, 15 + (yOffset / 30)))
}
}
.hideToolbarBackground()
if showFollowBtnInBlurrBanner(topSafeAreaInset: topSafeAreaInset) {
if showFollowBtnInBlurrBanner() {
ToolbarItem(placement: .topBarTrailing) {
FollowButtonView(
target: profile.get_follow_target(),
@@ -572,7 +556,6 @@ struct ProfileView: View {
}
}
}
}
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {