20dc672dbf
This adds a sync mechanism in Ndb.swift to coordinate certain usage of nostrdb.c calls and the need to close nostrdb due to app lifecycle requirements. Furthermore, it fixes the order of operations when re-opening NostrDB, to avoid race conditions where a query uses an older Ndb generation. This sync mechanism allows multiple queries to happen simultaneously (from the Swift-side), while preventing ndb from simultaneously closing during such usages. It also does that while keeping the Ndb interface sync and nonisolated, which keeps the API easy to use from Swift/SwiftUI and allows for parallel operations to occur. If Swift Actors were to be used (e.g. creating an NdbActor), the Ndb.swift interface would change in such a way that it would propagate the need for several changes throughout the codebase, including loading logic in some ViewModels. Furthermore, it would likely decrease performance by forcing Ndb.swift operations to run sequentially when they could run in parallel. Changelog-Fixed: Fixed crashes that happened when the app went into background mode Closes: https://github.com/damus-io/damus/issues/3245 Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
136 lines
4.4 KiB
Swift
136 lines
4.4 KiB
Swift
//
|
|
// BannerImageView.swift
|
|
// damus
|
|
//
|
|
// Created by Jason Jōb on 2023-01-10.
|
|
//
|
|
|
|
import SwiftUI
|
|
import Kingfisher
|
|
|
|
struct EditBannerImageView: View {
|
|
|
|
var damus_state: DamusState
|
|
@ObservedObject var viewModel: ImageUploadingObserver
|
|
let callback: (URL?) -> Void
|
|
let defaultImage = UIImage(named: "damoose") ?? UIImage()
|
|
let safeAreaInsets: EdgeInsets
|
|
|
|
@State var banner_image: URL? = nil
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
Color(uiColor: .systemBackground)
|
|
KFAnimatedImage(get_banner_url(banner: banner_image?.absoluteString, pubkey: damus_state.pubkey, profiles: damus_state.profiles))
|
|
.imageContext(.banner, disable_animation: damus_state.settings.disable_animation)
|
|
.configure { view in
|
|
view.framePreloadCount = .max
|
|
}
|
|
.placeholder { _ in
|
|
Color(uiColor: .secondarySystemBackground)
|
|
}
|
|
.onFailureImage(defaultImage)
|
|
.kfClickable()
|
|
|
|
EditPictureControl(
|
|
uploader: damus_state.settings.default_media_uploader,
|
|
context: .normal,
|
|
keypair: damus_state.keypair,
|
|
pubkey: damus_state.pubkey,
|
|
current_image_url: $banner_image,
|
|
upload_observer: viewModel,
|
|
callback: callback
|
|
)
|
|
.padding(10)
|
|
.backwardsCompatibleSafeAreaPadding(self.safeAreaInsets)
|
|
.accessibilityLabel(NSLocalizedString("Edit banner image", comment: "Accessibility label for edit banner image button"))
|
|
.accessibilityIdentifier(AppAccessibilityIdentifiers.own_profile_banner_image_edit_button.rawValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
fileprivate func backwardsCompatibleSafeAreaPadding(_ insets: EdgeInsets) -> some View {
|
|
if #available(iOS 17.0, *) {
|
|
return self.safeAreaPadding(insets)
|
|
} else {
|
|
return self.padding(.top, insets.top)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct InnerBannerImageView: View {
|
|
let disable_animation: Bool
|
|
let url: URL?
|
|
let defaultImage = UIImage(named: "damoose") ?? UIImage()
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
Color(uiColor: .systemBackground)
|
|
|
|
if (url != nil) {
|
|
KFAnimatedImage(url)
|
|
.imageContext(.banner, disable_animation: disable_animation)
|
|
.configure { view in
|
|
view.framePreloadCount = 3
|
|
}
|
|
.placeholder { _ in
|
|
Color(uiColor: .secondarySystemBackground)
|
|
}
|
|
.onFailureImage(defaultImage)
|
|
.kfClickable()
|
|
} else {
|
|
Image(uiImage: defaultImage).resizable()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct BannerImageView: View {
|
|
let disable_animation: Bool
|
|
let pubkey: Pubkey
|
|
let profiles: Profiles
|
|
let damusState: DamusState
|
|
|
|
@State var banner: String?
|
|
|
|
init(pubkey: Pubkey, profiles: Profiles, disable_animation: Bool, banner: String? = nil, damusState: DamusState) {
|
|
self.pubkey = pubkey
|
|
self.profiles = profiles
|
|
self._banner = State(initialValue: banner)
|
|
self.disable_animation = disable_animation
|
|
self.damusState = damusState
|
|
}
|
|
|
|
var body: some View {
|
|
InnerBannerImageView(disable_animation: disable_animation, url: get_banner_url(banner: banner, pubkey: pubkey, profiles: profiles))
|
|
.task {
|
|
for await profile in await damusState.nostrNetwork.profilesManager.streamProfile(pubkey: pubkey) {
|
|
if let bannerImage = profile.banner, bannerImage != self.banner {
|
|
self.banner = bannerImage
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func get_banner_url(banner: String?, pubkey: Pubkey, profiles: Profiles) -> URL? {
|
|
let bannerUrlString = banner ?? (try? profiles.lookup(id: pubkey)?.banner) ?? ""
|
|
if let url = URL(string: bannerUrlString) {
|
|
return url
|
|
}
|
|
return nil
|
|
}
|
|
|
|
struct BannerImageView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
BannerImageView(
|
|
pubkey: test_pubkey,
|
|
profiles: make_preview_profiles(test_pubkey),
|
|
disable_animation: false,
|
|
damusState: test_damus_state
|
|
)
|
|
}
|
|
}
|
|
|