Eliminate popping when scrolling
This commit makes a few changes: - Link preview views are no longer cached, only the metadata. This fixes a memory leak when preview videos. It will keep playing the video forever eventually leading to a crash. This is fixed! - Cache the intrinsic height of previews, when loading notes it looks for the cached height so that things don't pop-in after the fact - Note artifacts and previews are set in the constructor instead of onAppear, this prevents the size from changing and popping after it has been loaded into the lazyvstack Changelog-Fixed: Fix memory leak with inline videos Changelog-Fixed: Eliminate popping when scrolling
This commit is contained in:
@@ -1649,7 +1649,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 3;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1691,7 +1691,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 3;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
||||
@@ -10,10 +10,11 @@ import LinkPresentation
|
||||
|
||||
class CustomLinkView: LPLinkView {
|
||||
override var intrinsicContentSize: CGSize { CGSize(width: 0, height: super.intrinsicContentSize.height) }
|
||||
|
||||
}
|
||||
|
||||
enum Metadata {
|
||||
case linkmeta(LPLinkMetadata)
|
||||
case linkmeta(CachedMetadata)
|
||||
case url(URL)
|
||||
}
|
||||
|
||||
@@ -26,12 +27,19 @@ struct LinkViewRepresentable: UIViewRepresentable {
|
||||
func makeUIView(context: Context) -> CustomLinkView {
|
||||
switch meta {
|
||||
case .linkmeta(let linkmeta):
|
||||
return CustomLinkView(metadata: linkmeta)
|
||||
return CustomLinkView(metadata: linkmeta.meta)
|
||||
case .url(let url):
|
||||
return CustomLinkView(url: url)
|
||||
}
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: CustomLinkView, context: Context) {
|
||||
switch meta {
|
||||
case .linkmeta(let cached):
|
||||
cached.intrinsic_height = uiView.intrinsicContentSize.height
|
||||
case .url:
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,18 @@
|
||||
import Foundation
|
||||
import LinkPresentation
|
||||
|
||||
class CachedMetadata {
|
||||
let meta: LPLinkMetadata
|
||||
var intrinsic_height: CGFloat?
|
||||
|
||||
init(meta: LPLinkMetadata) {
|
||||
self.meta = meta
|
||||
self.intrinsic_height = nil
|
||||
}
|
||||
}
|
||||
|
||||
enum Preview {
|
||||
case value(LinkViewRepresentable)
|
||||
case value(CachedMetadata)
|
||||
case failed
|
||||
}
|
||||
|
||||
@@ -20,12 +30,12 @@ class PreviewCache {
|
||||
return previews[evid]
|
||||
}
|
||||
|
||||
func store(evid: String, preview: LinkViewRepresentable?) {
|
||||
func store(evid: String, preview: LPLinkMetadata?) {
|
||||
switch preview {
|
||||
case .none:
|
||||
previews[evid] = .failed
|
||||
case .some(let meta):
|
||||
previews[evid] = .value(meta)
|
||||
previews[evid] = .value(CachedMetadata(meta: meta))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,21 @@ struct NoteContentView: View {
|
||||
let event: NostrEvent
|
||||
let show_images: Bool
|
||||
let size: EventViewKind
|
||||
let preview_height: CGFloat?
|
||||
|
||||
@State var artifacts: NoteArtifacts
|
||||
@State var preview: LinkViewRepresentable? = nil
|
||||
@State var preview: LinkViewRepresentable?
|
||||
|
||||
init(damus_state: DamusState, event: NostrEvent, show_images: Bool, size: EventViewKind, artifacts: NoteArtifacts) {
|
||||
self.damus_state = damus_state
|
||||
self.event = event
|
||||
self.show_images = show_images
|
||||
self.size = size
|
||||
self._artifacts = State(initialValue: artifacts)
|
||||
self.preview_height = lookup_cached_preview_size(previews: damus_state.previews, evid: event.id)
|
||||
self._preview = State(initialValue: load_cached_preview(previews: damus_state.previews, evid: event.id))
|
||||
self._artifacts = State(initialValue: render_note_content(ev: event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey))
|
||||
}
|
||||
|
||||
func MainContent() -> some View {
|
||||
return VStack(alignment: .leading) {
|
||||
@@ -58,23 +70,21 @@ struct NoteContentView: View {
|
||||
}
|
||||
|
||||
if let preview = self.preview, show_images {
|
||||
preview
|
||||
} else {
|
||||
ForEach(artifacts.links, id:\.self) { link in
|
||||
if let url = link {
|
||||
LinkViewRepresentable(meta: .url(url))
|
||||
.frame(height: 50)
|
||||
}
|
||||
if let preview_height {
|
||||
preview
|
||||
.frame(height: preview_height)
|
||||
} else {
|
||||
preview
|
||||
}
|
||||
} else if let link = artifacts.links.first {
|
||||
LinkViewRepresentable(meta: .url(link))
|
||||
.frame(height: 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
MainContent()
|
||||
.onAppear() {
|
||||
self.artifacts = render_note_content(ev: event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
|
||||
}
|
||||
.onReceive(handle_notify(.profile_updated)) { notif in
|
||||
let profile = notif.object as! ProfileUpdate
|
||||
let blocks = event.blocks(damus_state.keypair.privkey)
|
||||
@@ -92,21 +102,19 @@ struct NoteContentView: View {
|
||||
}
|
||||
}
|
||||
.task {
|
||||
if let preview = damus_state.previews.lookup(self.event.id) {
|
||||
switch preview {
|
||||
case .value(let view):
|
||||
self.preview = view
|
||||
case .failed:
|
||||
// don't try to refetch meta if we've failed
|
||||
return
|
||||
}
|
||||
guard self.preview == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
if show_images, artifacts.links.count == 1 {
|
||||
let meta = await getMetaData(for: artifacts.links.first!)
|
||||
|
||||
let view = meta.map { LinkViewRepresentable(meta: .linkmeta($0)) }
|
||||
damus_state.previews.store(evid: self.event.id, preview: view)
|
||||
damus_state.previews.store(evid: self.event.id, preview: meta)
|
||||
guard case .value(let cached) = damus_state.previews.lookup(self.event.id) else {
|
||||
return
|
||||
}
|
||||
let view = LinkViewRepresentable(meta: .linkmeta(cached))
|
||||
|
||||
self.preview = view
|
||||
}
|
||||
|
||||
@@ -233,3 +241,23 @@ func is_image_url(_ url: URL) -> Bool {
|
||||
return str.hasSuffix("png") || str.hasSuffix("jpg") || str.hasSuffix("jpeg") || str.hasSuffix("gif")
|
||||
}
|
||||
|
||||
func lookup_cached_preview_size(previews: PreviewCache, evid: String) -> CGFloat? {
|
||||
guard case .value(let cached) = previews.lookup(evid) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let height = cached.intrinsic_height else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return height
|
||||
}
|
||||
|
||||
|
||||
func load_cached_preview(previews: PreviewCache, evid: String) -> LinkViewRepresentable? {
|
||||
guard case .value(let meta) = previews.lookup(evid) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return LinkViewRepresentable(meta: .linkmeta(meta))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user