Move profile update handling from notes to the background
During profiling, I found that some large hangs were being caused by a large number of `notify` calls (and their handling functions) keeping the main thread overly busy. We cannot move the `notify` mechanism to a background thread (It has to be done on the main actor or else runtime warnings/errors appear), so instead this commit removes a very large source of notify calls/handling around NoteContentView, and replaces it with a background task that streams for profile updates and only updates its view when a relevant profile is updated. Changelog-Changed: Improved performance around note content views to prevent hangs Closes: https://github.com/damus-io/damus/issues/3439 Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@@ -101,10 +101,17 @@ extension NostrNetworkManager {
|
|||||||
relevantStream.continuation.yield(profile)
|
relevantStream.continuation.yield(profile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Notify the rest of the app so views that rely on rendered text (like mention strings)
|
/// Manually trigger profile updates for a given pubkey
|
||||||
// can reload and pick up the freshly fetched profile metadata.
|
/// This is useful for local profile changes (e.g., nip05 validation, donation percentage updates)
|
||||||
notify(.profile_updated(.remote(pubkey: metadataEvent.pubkey)))
|
func notifyProfileUpdate(pubkey: Pubkey) {
|
||||||
|
if let relevantStreams = streams[pubkey] {
|
||||||
|
guard let profile = ndb.lookup_profile_and_copy(pubkey) else { return }
|
||||||
|
for relevantStream in relevantStreams.values {
|
||||||
|
relevantStream.continuation.yield(profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -121,6 +128,29 @@ extension NostrNetworkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func streamProfiles(pubkeys: Set<Pubkey>) -> AsyncStream<ProfileStreamItem> {
|
||||||
|
guard !pubkeys.isEmpty else {
|
||||||
|
return AsyncStream<ProfileStreamItem> { continuation in
|
||||||
|
continuation.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AsyncStream<ProfileStreamItem> { continuation in
|
||||||
|
let stream = ProfileStreamInfo(continuation: continuation)
|
||||||
|
for pubkey in pubkeys {
|
||||||
|
self.add(pubkey: pubkey, stream: stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
continuation.onTermination = { @Sendable _ in
|
||||||
|
Task {
|
||||||
|
for pubkey in pubkeys {
|
||||||
|
await self.removeStream(pubkey: pubkey, id: stream.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Stream management
|
// MARK: - Stream management
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ struct NoteContentView: View {
|
|||||||
let event: NostrEvent
|
let event: NostrEvent
|
||||||
@State var blur_images: Bool
|
@State var blur_images: Bool
|
||||||
@State var load_media: Bool = false
|
@State var load_media: Bool = false
|
||||||
@State private var requestedMentionProfiles: Set<Pubkey> = []
|
|
||||||
let size: EventViewKind
|
let size: EventViewKind
|
||||||
let preview_height: CGFloat?
|
let preview_height: CGFloat?
|
||||||
let options: EventViewOptions
|
let options: EventViewOptions
|
||||||
@@ -279,10 +278,12 @@ struct NoteContentView: View {
|
|||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureMentionProfilesAreFetchingIfNeeded() {
|
@concurrent
|
||||||
|
func streamProfiles() async throws {
|
||||||
var mentionPubkeys: Set<Pubkey> = []
|
var mentionPubkeys: Set<Pubkey> = []
|
||||||
try? NdbBlockGroup.borrowBlockGroup(event: event, using: damus_state.ndb, and: damus_state.keypair, borrow: { blockGroup in
|
let event = await self.event.clone()
|
||||||
let _: ()? = try? blockGroup.forEachBlock({ _, block in
|
try await NdbBlockGroup.borrowBlockGroup(event: event, using: damus_state.ndb, and: damus_state.keypair, borrow: { blockGroup in
|
||||||
|
blockGroup.forEachBlock({ _, block in
|
||||||
guard let pubkey = block.mentionPubkey(tags: event.tags) else {
|
guard let pubkey = block.mentionPubkey(tags: event.tags) else {
|
||||||
return .loopContinue
|
return .loopContinue
|
||||||
}
|
}
|
||||||
@@ -291,32 +292,12 @@ struct NoteContentView: View {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
guard !mentionPubkeys.isEmpty else { return }
|
if mentionPubkeys.isEmpty {
|
||||||
|
return
|
||||||
var toFetch: [Pubkey] = []
|
|
||||||
for pubkey in mentionPubkeys {
|
|
||||||
if requestedMentionProfiles.contains(pubkey) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
requestedMentionProfiles.insert(pubkey)
|
|
||||||
|
|
||||||
if damus_state.profiles.has_fresh_profile(id: pubkey) {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toFetch.append(pubkey)
|
for await profile in await damus_state.nostrNetwork.profilesManager.streamProfiles(pubkeys: mentionPubkeys) {
|
||||||
}
|
await load(force_artifacts: true)
|
||||||
|
|
||||||
guard !toFetch.isEmpty else { return }
|
|
||||||
|
|
||||||
// Kick off metadata fetches for any missing mention profiles so their names can render once loaded.
|
|
||||||
for pubkey in toFetch {
|
|
||||||
Task {
|
|
||||||
for await _ in await damus_state.nostrNetwork.profilesManager.streamProfile(pubkey: pubkey) {
|
|
||||||
// NO-OP, we will receive the update via `notify`
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,8 +306,6 @@ struct NoteContentView: View {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureMentionProfilesAreFetchingIfNeeded()
|
|
||||||
|
|
||||||
// always reload artifacts on load
|
// always reload artifacts on load
|
||||||
let plan = get_preload_plan(evcache: damus_state.events, ev: event, our_keypair: damus_state.keypair, settings: damus_state.settings)
|
let plan = get_preload_plan(evcache: damus_state.events, ev: event, our_keypair: damus_state.keypair, settings: damus_state.settings)
|
||||||
|
|
||||||
@@ -442,44 +421,13 @@ struct NoteContentView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ArtifactContent
|
ArtifactContent
|
||||||
.onReceive(handle_notify(.profile_updated)) { profile in
|
.task {
|
||||||
try? NdbBlockGroup.borrowBlockGroup(event: event, using: damus_state.ndb, and: damus_state.keypair, borrow: { blockGroup in
|
try? await streamProfiles()
|
||||||
let _: Int? = blockGroup.forEachBlock { index, block in
|
|
||||||
switch block {
|
|
||||||
case .mention(let m):
|
|
||||||
guard let typ = m.bech32_type else {
|
|
||||||
return .loopContinue
|
|
||||||
}
|
|
||||||
switch typ {
|
|
||||||
case .nprofile:
|
|
||||||
if m.bech32.nprofile.matches_pubkey(pk: profile.pubkey) {
|
|
||||||
load(force_artifacts: true)
|
|
||||||
}
|
|
||||||
case .npub:
|
|
||||||
if m.bech32.npub.matches_pubkey(pk: profile.pubkey) {
|
|
||||||
load(force_artifacts: true)
|
|
||||||
}
|
|
||||||
case .nevent: return .loopContinue
|
|
||||||
case .nrelay: return .loopContinue
|
|
||||||
case .nsec: return .loopContinue
|
|
||||||
case .note: return .loopContinue
|
|
||||||
case .naddr: return .loopContinue
|
|
||||||
}
|
|
||||||
case .text: return .loopContinue
|
|
||||||
case .hashtag: return .loopContinue
|
|
||||||
case .url: return .loopContinue
|
|
||||||
case .invoice: return .loopContinue
|
|
||||||
case .mention_index(_): return .loopContinue
|
|
||||||
}
|
|
||||||
return .loopContinue
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoteArtifactsParts {
|
class NoteArtifactsParts {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ struct NIP05DomainTimelineView: View {
|
|||||||
|
|
||||||
if let pubkeys = model.filter.authors {
|
if let pubkeys = model.filter.authors {
|
||||||
for pubkey in pubkeys {
|
for pubkey in pubkeys {
|
||||||
check_nip05_validity(pubkey: pubkey, profiles: damus_state.profiles)
|
check_nip05_validity(pubkey: pubkey, damus_state: damus_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ struct EventProfileName: View {
|
|||||||
@State var nip05: NIP05?
|
@State var nip05: NIP05?
|
||||||
@State var donation: Int?
|
@State var donation: Int?
|
||||||
@State var purple_account: DamusPurple.Account?
|
@State var purple_account: DamusPurple.Account?
|
||||||
@StateObject private var profileObserver: ProfileObserver
|
|
||||||
|
|
||||||
let size: EventViewKind
|
let size: EventViewKind
|
||||||
|
|
||||||
@@ -28,7 +27,6 @@ struct EventProfileName: View {
|
|||||||
let donation = damus.profiles.lookup(id: pubkey)?.damus_donation
|
let donation = damus.profiles.lookup(id: pubkey)?.damus_donation
|
||||||
self._donation = State(wrappedValue: donation)
|
self._donation = State(wrappedValue: donation)
|
||||||
self.purple_account = nil
|
self.purple_account = nil
|
||||||
self._profileObserver = StateObject.init(wrappedValue: ProfileObserver(pubkey: pubkey, damusState: damus))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var friend_type: FriendType? {
|
var friend_type: FriendType? {
|
||||||
@@ -102,13 +100,8 @@ struct EventProfileName: View {
|
|||||||
SupporterBadge(percent: self.supporter_percentage(), purple_account: self.purple_account, style: .compact)
|
SupporterBadge(percent: self.supporter_percentage(), purple_account: self.purple_account, style: .compact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.profile_updated)) { update in
|
.task {
|
||||||
if update.pubkey != pubkey {
|
for await profile in await damus_state.nostrNetwork.profilesManager.streamProfile(pubkey: pubkey) {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let profile = damus_state.profiles.lookup(id: update.pubkey) else { return }
|
|
||||||
|
|
||||||
let display_name = Profile.displayName(profile: profile, pubkey: pubkey)
|
let display_name = Profile.displayName(profile: profile, pubkey: pubkey)
|
||||||
if display_name != self.display_name {
|
if display_name != self.display_name {
|
||||||
self.display_name = display_name
|
self.display_name = display_name
|
||||||
@@ -124,6 +117,7 @@ struct EventProfileName: View {
|
|||||||
donation = profile.damus_donation
|
donation = profile.damus_donation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.task {
|
.task {
|
||||||
if damus_state.purple.enable_purple {
|
if damus_state.purple.enable_purple {
|
||||||
self.purple_account = try? await damus_state.purple.get_maybe_cached_account(pubkey: pubkey)
|
self.purple_account = try? await damus_state.purple.get_maybe_cached_account(pubkey: pubkey)
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ struct ProfileName: View {
|
|||||||
@State var donation: Int?
|
@State var donation: Int?
|
||||||
@State var purple_account: DamusPurple.Account?
|
@State var purple_account: DamusPurple.Account?
|
||||||
@State var nip05_domain_favicon: FaviconURL?
|
@State var nip05_domain_favicon: FaviconURL?
|
||||||
@StateObject var profileObserver: ProfileObserver
|
|
||||||
|
|
||||||
init(pubkey: Pubkey, prefix: String = "", damus: DamusState, show_nip5_domain: Bool = true, supporterBadgeStyle: SupporterBadge.Style = .compact) {
|
init(pubkey: Pubkey, prefix: String = "", damus: DamusState, show_nip5_domain: Bool = true, supporterBadgeStyle: SupporterBadge.Style = .compact) {
|
||||||
self.pubkey = pubkey
|
self.pubkey = pubkey
|
||||||
@@ -54,7 +53,6 @@ struct ProfileName: View {
|
|||||||
self.show_nip5_domain = show_nip5_domain
|
self.show_nip5_domain = show_nip5_domain
|
||||||
self.supporterBadgeStyle = supporterBadgeStyle
|
self.supporterBadgeStyle = supporterBadgeStyle
|
||||||
self.purple_account = nil
|
self.purple_account = nil
|
||||||
self._profileObserver = StateObject.init(wrappedValue: ProfileObserver(pubkey: pubkey, damusState: damus))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var friend_type: FriendType? {
|
var friend_type: FriendType? {
|
||||||
@@ -131,21 +129,10 @@ struct ProfileName: View {
|
|||||||
.largest()
|
.largest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.profile_updated)) { update in
|
.task {
|
||||||
if update.pubkey != pubkey {
|
for await profile in await damus_state.nostrNetwork.profilesManager.streamProfile(pubkey: pubkey) {
|
||||||
return
|
handle_profile_update(profile: profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch update {
|
|
||||||
case .remote(let pubkey):
|
|
||||||
guard let prof = damus_state.profiles.lookup(id: pubkey) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handle_profile_update(profile: prof)
|
|
||||||
case .manual(_, let prof):
|
|
||||||
handle_profile_update(profile: prof)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,8 +75,7 @@ struct ProfilePicView: View {
|
|||||||
let privacy_sensitive: Bool
|
let privacy_sensitive: Bool
|
||||||
|
|
||||||
@State var picture: String?
|
@State var picture: String?
|
||||||
@StateObject private var profileObserver: ProfileObserver
|
let damusState: DamusState
|
||||||
@EnvironmentObject var damusState: DamusState
|
|
||||||
|
|
||||||
init(pubkey: Pubkey, size: CGFloat, highlight: Highlight, profiles: Profiles, disable_animation: Bool, picture: String? = nil, show_zappability: Bool? = nil, privacy_sensitive: Bool = false, damusState: DamusState) {
|
init(pubkey: Pubkey, size: CGFloat, highlight: Highlight, profiles: Profiles, disable_animation: Bool, picture: String? = nil, show_zappability: Bool? = nil, privacy_sensitive: Bool = false, damusState: DamusState) {
|
||||||
self.pubkey = pubkey
|
self.pubkey = pubkey
|
||||||
@@ -87,7 +86,7 @@ struct ProfilePicView: View {
|
|||||||
self.disable_animation = disable_animation
|
self.disable_animation = disable_animation
|
||||||
self.zappability_indicator = show_zappability ?? false
|
self.zappability_indicator = show_zappability ?? false
|
||||||
self.privacy_sensitive = privacy_sensitive
|
self.privacy_sensitive = privacy_sensitive
|
||||||
self._profileObserver = StateObject.init(wrappedValue: ProfileObserver(pubkey: pubkey, damusState: damusState))
|
self.damusState = damusState
|
||||||
}
|
}
|
||||||
|
|
||||||
var privacy_sensitive_pubkey: Pubkey {
|
var privacy_sensitive_pubkey: Pubkey {
|
||||||
@@ -110,23 +109,6 @@ struct ProfilePicView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack (alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
|
ZStack (alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
|
||||||
InnerProfilePicView(url: get_profile_url(picture: picture, pubkey: privacy_sensitive_pubkey, profiles: profiles), fallbackUrl: URL(string: robohash(privacy_sensitive_pubkey)), size: size, highlight: highlight, disable_animation: disable_animation)
|
InnerProfilePicView(url: get_profile_url(picture: picture, pubkey: privacy_sensitive_pubkey, profiles: profiles), fallbackUrl: URL(string: robohash(privacy_sensitive_pubkey)), size: size, highlight: highlight, disable_animation: disable_animation)
|
||||||
.onReceive(handle_notify(.profile_updated)) { updated in
|
|
||||||
guard updated.pubkey == self.pubkey else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch updated {
|
|
||||||
case .manual(_, let profile):
|
|
||||||
if let pic = profile.picture {
|
|
||||||
self.picture = pic
|
|
||||||
}
|
|
||||||
case .remote(pubkey: let pk):
|
|
||||||
let profile = profiles.lookup(id: pk)
|
|
||||||
if let pic = profile?.picture {
|
|
||||||
self.picture = pic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.zappability_indicator, let lnurl = self.get_lnurl(), lnurl != "" {
|
if self.zappability_indicator, let lnurl = self.get_lnurl(), lnurl != "" {
|
||||||
Image("zap.fill")
|
Image("zap.fill")
|
||||||
@@ -141,6 +123,13 @@ struct ProfilePicView: View {
|
|||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.task {
|
||||||
|
for await profile in await damusState.nostrNetwork.profilesManager.streamProfile(pubkey: pubkey) {
|
||||||
|
if let pic = profile.picture {
|
||||||
|
self.picture = pic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ struct ProfileView: View {
|
|||||||
return AnyView(
|
return AnyView(
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
ZStack {
|
ZStack {
|
||||||
BannerImageView(pubkey: profile.pubkey, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
|
BannerImageView(pubkey: profile.pubkey, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation, damusState: damus_state)
|
||||||
.aspectRatio(contentMode: .fill)
|
.aspectRatio(contentMode: .fill)
|
||||||
.frame(width: proxy.size.width, height: minY > 0 ? bannerHeight + minY : bannerHeight)
|
.frame(width: proxy.size.width, height: minY > 0 ? bannerHeight + minY : bannerHeight)
|
||||||
.clipped()
|
.clipped()
|
||||||
@@ -525,7 +525,7 @@ struct ProfileView: View {
|
|||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
check_nip05_validity(pubkey: self.profile.pubkey, profiles: self.damus_state.profiles)
|
check_nip05_validity(pubkey: self.profile.pubkey, damus_state: self.damus_state)
|
||||||
profile.subscribe()
|
profile.subscribe()
|
||||||
//followers.subscribe()
|
//followers.subscribe()
|
||||||
}
|
}
|
||||||
@@ -568,7 +568,8 @@ extension View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
func check_nip05_validity(pubkey: Pubkey, profiles: Profiles) {
|
func check_nip05_validity(pubkey: Pubkey, damus_state: DamusState) {
|
||||||
|
let profiles = damus_state.profiles
|
||||||
let profile = profiles.lookup(id: pubkey)
|
let profile = profiles.lookup(id: pubkey)
|
||||||
|
|
||||||
guard let nip05 = profile?.nip05,
|
guard let nip05 = profile?.nip05,
|
||||||
@@ -586,7 +587,7 @@ func check_nip05_validity(pubkey: Pubkey, profiles: Profiles) {
|
|||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
profiles.set_validated(pubkey, nip05: validated)
|
profiles.set_validated(pubkey, nip05: validated)
|
||||||
profiles.nip05_pubkey[nip05] = pubkey
|
profiles.nip05_pubkey[nip05] = pubkey
|
||||||
notify(.profile_updated(.remote(pubkey: pubkey)))
|
await damus_state.nostrNetwork.profilesManager.notifyProfileUpdate(pubkey: pubkey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,7 +250,9 @@ struct NWCSettings: View {
|
|||||||
|
|
||||||
let prof = Profile(name: profile.name, display_name: profile.display_name, about: profile.about, picture: profile.picture, banner: profile.banner, website: profile.website, lud06: profile.lud06, lud16: profile.lud16, nip05: profile.nip05, damus_donation: p, reactions: profile.reactions)
|
let prof = Profile(name: profile.name, display_name: profile.display_name, about: profile.about, picture: profile.picture, banner: profile.banner, website: profile.website, lud06: profile.lud06, lud16: profile.lud16, nip05: profile.nip05, damus_donation: p, reactions: profile.reactions)
|
||||||
|
|
||||||
notify(.profile_updated(.manual(pubkey: self.damus_state.pubkey, profile: prof)))
|
Task {
|
||||||
|
await damus_state.nostrNetwork.profilesManager.notifyProfileUpdate(pubkey: self.damus_state.pubkey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
|
|
||||||
|
|||||||
@@ -90,30 +90,28 @@ struct BannerImageView: View {
|
|||||||
let disable_animation: Bool
|
let disable_animation: Bool
|
||||||
let pubkey: Pubkey
|
let pubkey: Pubkey
|
||||||
let profiles: Profiles
|
let profiles: Profiles
|
||||||
|
let damusState: DamusState
|
||||||
|
|
||||||
@State var banner: String?
|
@State var banner: String?
|
||||||
|
|
||||||
init(pubkey: Pubkey, profiles: Profiles, disable_animation: Bool, banner: String? = nil) {
|
init(pubkey: Pubkey, profiles: Profiles, disable_animation: Bool, banner: String? = nil, damusState: DamusState) {
|
||||||
self.pubkey = pubkey
|
self.pubkey = pubkey
|
||||||
self.profiles = profiles
|
self.profiles = profiles
|
||||||
self._banner = State(initialValue: banner)
|
self._banner = State(initialValue: banner)
|
||||||
self.disable_animation = disable_animation
|
self.disable_animation = disable_animation
|
||||||
|
self.damusState = damusState
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
InnerBannerImageView(disable_animation: disable_animation, url: get_banner_url(banner: banner, pubkey: pubkey, profiles: profiles))
|
InnerBannerImageView(disable_animation: disable_animation, url: get_banner_url(banner: banner, pubkey: pubkey, profiles: profiles))
|
||||||
.onReceive(handle_notify(.profile_updated)) { updated in
|
.task {
|
||||||
guard updated.pubkey == self.pubkey,
|
for await profile in await damusState.nostrNetwork.profilesManager.streamProfile(pubkey: pubkey) {
|
||||||
let profile = profiles.lookup(id: updated.pubkey)
|
|
||||||
else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if let bannerImage = profile.banner, bannerImage != self.banner {
|
if let bannerImage = profile.banner, bannerImage != self.banner {
|
||||||
self.banner = bannerImage
|
self.banner = bannerImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_banner_url(banner: String?, pubkey: Pubkey, profiles: Profiles) -> URL? {
|
func get_banner_url(banner: String?, pubkey: Pubkey, profiles: Profiles) -> URL? {
|
||||||
@@ -129,7 +127,8 @@ struct BannerImageView_Previews: PreviewProvider {
|
|||||||
BannerImageView(
|
BannerImageView(
|
||||||
pubkey: test_pubkey,
|
pubkey: test_pubkey,
|
||||||
profiles: make_preview_profiles(test_pubkey),
|
profiles: make_preview_profiles(test_pubkey),
|
||||||
disable_animation: false
|
disable_animation: false,
|
||||||
|
damusState: test_damus_state
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user