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:
@@ -102,9 +102,7 @@ struct ProfileView: View {
|
|||||||
colorScheme == .light ? DamusColors.white : DamusColors.black
|
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() -> Double {
|
||||||
func bannerBlurViewOpacity(topSafeAreaInset: CGFloat) -> Double {
|
|
||||||
let navbarHeight = navbarHeight(topSafeAreaInset: topSafeAreaInset)
|
|
||||||
let progress = -(yOffset + navbarHeight) / 100
|
let progress = -(yOffset + navbarHeight) / 100
|
||||||
return Double(-yOffset > navbarHeight ? progress : 0)
|
return Double(-yOffset > navbarHeight ? progress : 0)
|
||||||
}
|
}
|
||||||
@@ -116,9 +114,8 @@ struct ProfileView: View {
|
|||||||
return (displayName, "@\(userName)")
|
return (displayName, "@\(userName)")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the follow button should appear in the collapsed banner state.
|
func showFollowBtnInBlurrBanner() -> Bool {
|
||||||
func showFollowBtnInBlurrBanner(topSafeAreaInset: CGFloat) -> Bool {
|
damus_state.contacts.follow_state(profile.pubkey) == .unfollows && bannerBlurViewOpacity() > 1.0
|
||||||
damus_state.contacts.follow_state(profile.pubkey) == .unfollows && bannerBlurViewOpacity(topSafeAreaInset: topSafeAreaInset) > 1.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the profile timeline filter, while ensuring the user's own profile bypasses NSFW and hashtag-spam content filters.
|
/// 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
|
return ContentFilters(filters: filters).filter
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the stretching and collapsing banner while avoiding synchronous window safe-area queries on the main thread.
|
var bannerSection: some View {
|
||||||
func bannerSection(topSafeAreaInset: CGFloat) -> some View {
|
|
||||||
GeometryReader { proxy -> AnyView in
|
GeometryReader { proxy -> AnyView in
|
||||||
|
|
||||||
let minY = proxy.frame(in: .global).minY
|
let minY = proxy.frame(in: .global).minY
|
||||||
let navbarHeight = navbarHeight(topSafeAreaInset: topSafeAreaInset)
|
|
||||||
let blurOpacity = bannerBlurViewOpacity(topSafeAreaInset: topSafeAreaInset)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.yOffset = minY
|
self.yOffset = minY
|
||||||
@@ -155,10 +150,10 @@ struct ProfileView: View {
|
|||||||
.frame(width: proxy.size.width, height: minY > 0 ? bannerHeight + minY : bannerHeight)
|
.frame(width: proxy.size.width, height: minY > 0 ? bannerHeight + minY : bannerHeight)
|
||||||
.clipped()
|
.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)
|
.frame(height: minY > 0 ? bannerHeight + minY : nil)
|
||||||
.offset(y: minY > 0 ? -minY : -minY < navbarHeight ? 0 : -minY - navbarHeight)
|
.offset(y: minY > 0 ? -minY : -minY < navbarHeight ? 0 : -minY - navbarHeight)
|
||||||
@@ -169,9 +164,8 @@ struct ProfileView: View {
|
|||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the profile navigation height using the view's current safe-area context instead of querying the key window during SwiftUI updates.
|
var navbarHeight: CGFloat {
|
||||||
func navbarHeight(topSafeAreaInset: CGFloat) -> CGFloat {
|
return 100.0 - (Theme.safeAreaInsets?.top ?? 0)
|
||||||
100.0 - topSafeAreaInset
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func navImage(img: String) -> some View {
|
func navImage(img: String) -> some View {
|
||||||
@@ -305,30 +299,27 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the avatar's vertical offset during banner collapse.
|
func pfpOffset() -> CGFloat {
|
||||||
func pfpOffset(topSafeAreaInset: CGFloat) -> CGFloat {
|
let progress = -yOffset / navbarHeight
|
||||||
let progress = -yOffset / navbarHeight(topSafeAreaInset: topSafeAreaInset)
|
|
||||||
let offset = (pfp_size / 4.0) * (progress < 1.0 ? progress : 1)
|
let offset = (pfp_size / 4.0) * (progress < 1.0 ? progress : 1)
|
||||||
return offset > 0 ? offset : 0
|
return offset > 0 ? offset : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the avatar's scale during banner collapse.
|
func pfpScale() -> CGFloat {
|
||||||
func pfpScale(topSafeAreaInset: CGFloat) -> CGFloat {
|
let progress = -yOffset / navbarHeight
|
||||||
let progress = -yOffset / navbarHeight(topSafeAreaInset: topSafeAreaInset)
|
|
||||||
let scale = 1.0 - (0.5 * (progress < 1.0 ? progress : 1))
|
let scale = 1.0 - (0.5 * (progress < 1.0 ? progress : 1))
|
||||||
return scale < 1 ? scale : 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?) -> some View {
|
||||||
func nameSection(ndbprofile: Profile?, lnurl: String?, topSafeAreaInset: CGFloat) -> some View {
|
|
||||||
return Group {
|
return Group {
|
||||||
let follows_you = profile.pubkey != damus_state.pubkey && profile.follows(pubkey: damus_state.pubkey)
|
let follows_you = profile.pubkey != damus_state.pubkey && profile.follows(pubkey: damus_state.pubkey)
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
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)
|
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))
|
.padding(.top, -(pfp_size / 2.0))
|
||||||
.offset(y: pfpOffset(topSafeAreaInset: topSafeAreaInset))
|
.offset(y: pfpOffset())
|
||||||
.scaleEffect(pfpScale(topSafeAreaInset: topSafeAreaInset))
|
.scaleEffect(pfpScale())
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
is_zoomed.toggle()
|
is_zoomed.toggle()
|
||||||
}
|
}
|
||||||
@@ -366,13 +357,12 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the profile summary section using safe-area values from the surrounding geometry.
|
var aboutSection: some View {
|
||||||
func aboutSection(topSafeAreaInset: CGFloat) -> some View {
|
|
||||||
VStack(alignment: .leading, spacing: 8.0) {
|
VStack(alignment: .leading, spacing: 8.0) {
|
||||||
let lnurl = try? damus_state.profiles.lookup_lnurl(profile.pubkey)
|
let lnurl = try? damus_state.profiles.lookup_lnurl(profile.pubkey)
|
||||||
let ndbprofile = try? damus_state.profiles.lookup(id: 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 {
|
if let about = ndbprofile?.about {
|
||||||
AboutView(state: damus_state, about: about)
|
AboutView(state: damus_state, about: about)
|
||||||
@@ -461,20 +451,14 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some 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 {
|
ZStack {
|
||||||
ScrollView(.vertical) {
|
ScrollView(.vertical) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
bannerSection(topSafeAreaInset: topSafeAreaInset)
|
bannerSection
|
||||||
.zIndex(1)
|
.zIndex(1)
|
||||||
|
|
||||||
VStack {
|
VStack() {
|
||||||
aboutSection(topSafeAreaInset: topSafeAreaInset)
|
aboutSection
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
CustomPicker(tabs: tabs, selection: $filter_state)
|
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))
|
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)
|
.zIndex(-yOffset > navbarHeight ? 0 : 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom, tabHeight + safeAreaInsets.bottom)
|
.padding(.bottom, tabHeight + getSafeAreaBottom())
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarBackButtonHidden()
|
.navigationBarBackButtonHidden()
|
||||||
@@ -515,14 +499,14 @@ struct ProfileView: View {
|
|||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.white.opacity(0.8))
|
.foregroundColor(.white.opacity(0.8))
|
||||||
}
|
}
|
||||||
.opacity(blurOpacity)
|
.opacity(bannerBlurViewOpacity())
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(.top, max(5, 15 + (yOffset / 30)))
|
.padding(.top, max(5, 15 + (yOffset / 30)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.hideToolbarBackground()
|
.hideToolbarBackground()
|
||||||
|
|
||||||
if showFollowBtnInBlurrBanner(topSafeAreaInset: topSafeAreaInset) {
|
if showFollowBtnInBlurrBanner() {
|
||||||
ToolbarItem(placement: .topBarTrailing) {
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
FollowButtonView(
|
FollowButtonView(
|
||||||
target: profile.get_follow_target(),
|
target: profile.get_follow_target(),
|
||||||
@@ -571,7 +555,6 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProfileView_Previews: PreviewProvider {
|
struct ProfileView_Previews: PreviewProvider {
|
||||||
|
|||||||
Reference in New Issue
Block a user