commit bf95a8b328e88a898a9e393c893dc79c2dcbfaa5
parent e316d5d6359807a6428da9b01d761c3178fc5d42
Author: Joel Klabo <joelklabo@gmail.com>
Date: Mon, 3 Apr 2023 10:57:07 -0700
Banner Image Upload
Changelog-Added: Enable banner image editing
Diffstat:
6 files changed, 151 insertions(+), 116 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -329,7 +329,7 @@
F75BA12F29A18EF500E10810 /* BookmarksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75BA12E29A18EF500E10810 /* BookmarksView.swift */; };
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7908E91298B0F0700AB113A /* RelayDetailView.swift */; };
F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */; };
- F79C7FAD29D5E9620000F946 /* EditProfilePictureControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79C7FAC29D5E9620000F946 /* EditProfilePictureControl.swift */; };
+ F79C7FAD29D5E9620000F946 /* EditPictureControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79C7FAC29D5E9620000F946 /* EditPictureControl.swift */; };
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */; };
F7F0BA272978E54D009531F3 /* ParticipantsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA262978E54D009531F3 /* ParticipantsView.swift */; };
F944F56E29EA9CCC0067B3BF /* DamusParseContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F944F56D29EA9CCC0067B3BF /* DamusParseContentTests.swift */; };
@@ -804,7 +804,7 @@
F75BA12E29A18EF500E10810 /* BookmarksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksView.swift; sourceTree = "<group>"; };
F7908E91298B0F0700AB113A /* RelayDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayDetailView.swift; sourceTree = "<group>"; };
F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIPURLBuilder.swift; sourceTree = "<group>"; };
- F79C7FAC29D5E9620000F946 /* EditProfilePictureControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfilePictureControl.swift; sourceTree = "<group>"; };
+ F79C7FAC29D5E9620000F946 /* EditPictureControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditPictureControl.swift; sourceTree = "<group>"; };
F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToDismiss.swift; sourceTree = "<group>"; };
F7F0BA262978E54D009531F3 /* ParticipantsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantsView.swift; sourceTree = "<group>"; };
F944F56D29EA9CCC0067B3BF /* DamusParseContentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusParseContentTests.swift; sourceTree = "<group>"; };
@@ -1312,6 +1312,7 @@
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
F79C7FAC29D5E9620000F946 /* EditProfilePictureControl.swift */,
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
+ F79C7FAC29D5E9620000F946 /* EditPictureControl.swift */,
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
4C8682862814DE470026224F /* ProfileView.swift */,
4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */,
@@ -1930,6 +1931,7 @@
3A23838E2A297DD200E5AA2E /* ZapButtonModel.swift in Sources */,
5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */,
F79C7FAD29D5E9620000F946 /* EditProfilePictureControl.swift in Sources */,
+ F79C7FAD29D5E9620000F946 /* EditPictureControl.swift in Sources */,
4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */,
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */,
4C1A9A2A29DDF54400516EAC /* DamusVideoPlayer.swift in Sources */,
diff --git a/damus/Views/BannerImageView.swift b/damus/Views/BannerImageView.swift
@@ -8,6 +8,33 @@
import SwiftUI
import Kingfisher
+struct EditBannerImageView: View {
+
+ var damus_state: DamusState
+ @ObservedObject var viewModel: ImageUploadingObserver
+ let callback: (URL?) -> Void
+ let defaultImage = UIImage(named: "profile-banner") ?? UIImage()
+
+ @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 = 3
+ }
+ .placeholder { _ in
+ Color(uiColor: .secondarySystemBackground)
+ }
+ .onFailureImage(defaultImage)
+
+ EditPictureControl(uploader: damus_state.settings.default_media_uploader, pubkey: damus_state.pubkey, image_url: $banner_image, uploadObserver: viewModel, callback: callback)
+ }
+ }
+}
+
struct InnerBannerImageView: View {
let disable_animation: Bool
let url: URL?
diff --git a/damus/Views/Profile/EditMetadataView.swift b/damus/Views/Profile/EditMetadataView.swift
@@ -25,7 +25,9 @@ struct EditMetadataView: View {
@Environment(\.dismiss) var dismiss
@State var confirm_ln_address: Bool = false
- @StateObject var profileUploadViewModel = ProfileUploadingViewModel()
+
+ @StateObject var profileUploadObserver = ImageUploadingObserver()
+ @StateObject var bannerUploadObserver = ImageUploadingObserver()
init (damus_state: DamusState) {
self.damus_state = damus_state
@@ -78,7 +80,7 @@ struct EditMetadataView: View {
var TopSection: some View {
ZStack(alignment: .top) {
GeometryReader { geo in
- BannerImageView(pubkey: damus_state.pubkey, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
+ EditBannerImageView(damus_state: damus_state, viewModel: bannerUploadObserver, callback: uploadedBanner(image_url:))
.aspectRatio(contentMode: .fill)
.frame(width: geo.size.width, height: BANNER_HEIGHT)
.clipped()
@@ -87,7 +89,7 @@ struct EditMetadataView: View {
let pfp_size: CGFloat = 90.0
HStack(alignment: .center) {
- ProfilePictureSelector(pubkey: damus_state.pubkey, damus_state: damus_state, viewModel: profileUploadViewModel, callback: uploadedProfilePicture(image_url:))
+ ProfilePictureSelector(pubkey: damus_state.pubkey, damus_state: damus_state, uploadObserver: profileUploadObserver, callback: uploadedProfilePicture(image_url:))
.offset(y: -(pfp_size/2.0)) // Increase if set a frame
Spacer()
@@ -182,7 +184,7 @@ struct EditMetadataView: View {
dismiss()
}
}
- .disabled(profileUploadViewModel.isLoading)
+ .disabled(profileUploadObserver.isLoading || bannerUploadObserver.isLoading)
.alert(NSLocalizedString("Invalid Tip Address", comment: "Title of alerting as invalid tip address."), isPresented: $confirm_ln_address) {
Button(NSLocalizedString("Ok", comment: "Button to dismiss the alert.")) {
}
@@ -198,6 +200,10 @@ struct EditMetadataView: View {
func uploadedProfilePicture(image_url: URL?) {
picture = image_url?.absoluteString ?? ""
}
+
+ func uploadedBanner(image_url: URL?) {
+ banner = image_url?.absoluteString ?? ""
+ }
}
struct EditMetadataView_Previews: PreviewProvider {
diff --git a/damus/Views/Profile/EditPictureControl.swift b/damus/Views/Profile/EditPictureControl.swift
@@ -0,0 +1,106 @@
+//
+// EditPictureControl.swift
+// damus
+//
+// Created by Joel Klabo on 3/30/23.
+//
+
+import SwiftUI
+
+struct EditPictureControl: View {
+ let uploader: MediaUploader
+ let pubkey: String
+ @Binding var image_url: URL?
+ @ObservedObject var uploadObserver: ImageUploadingObserver
+ let callback: (URL?) -> Void
+
+ @StateObject var image_upload: ImageUploadModel = ImageUploadModel()
+
+ @State private var show_camera = false
+ @State private var show_library = false
+ @State var image_upload_confirm: Bool = false
+
+ @State var mediaToUpload: MediaUpload? = nil
+
+ var body: some View {
+ Menu {
+ Button(action: {
+ self.show_library = true
+ }) {
+ Text("Choose from Library", comment: "Option to select photo from library")
+ }
+
+ Button(action: {
+ self.show_camera = true
+ }) {
+ Text("Take Photo", comment: "Option to take a photo with the camera")
+ }
+ } label: {
+ if uploadObserver.isLoading {
+ ProgressView()
+ } else {
+ Image("camera")
+ .resizable()
+ .scaledToFit()
+ .frame(width: 25, height: 25)
+ .foregroundColor(DamusColors.white)
+ }
+ }
+ .sheet(isPresented: $show_camera) {
+
+ ImagePicker(uploader: uploader, sourceType: .camera, pubkey: pubkey, image_upload_confirm: $image_upload_confirm, imagesOnly: true) { img in
+ self.mediaToUpload = .image(img)
+ } onVideoPicked: { url in
+ print("Cannot upload videos as profile image")
+ }
+ .alert(NSLocalizedString("Are you sure you want to upload this image?", comment: "Alert message asking if the user wants to upload an image."), isPresented: $image_upload_confirm) {
+ Button(NSLocalizedString("Upload", comment: "Button to proceed with uploading."), role: .none) {
+ if let mediaToUpload {
+ self.handle_upload(media: mediaToUpload)
+ self.show_camera = false
+ }
+ }
+ Button(NSLocalizedString("Cancel", comment: "Button to cancel the upload."), role: .cancel) {}
+ }
+ }
+ .sheet(isPresented: $show_library) {
+ ImagePicker(uploader: uploader, sourceType: .photoLibrary, pubkey: pubkey, image_upload_confirm: $image_upload_confirm, imagesOnly: true) { img in
+ self.mediaToUpload = .image(img)
+
+ } onVideoPicked: { url in
+ print("Cannot upload videos as profile image")
+ }
+ .alert(NSLocalizedString("Are you sure you want to upload this image?", comment: "Alert message asking if the user wants to upload an image."), isPresented: $image_upload_confirm) {
+ Button(NSLocalizedString("Upload", comment: "Button to proceed with uploading."), role: .none) {
+ if let mediaToUpload {
+ self.handle_upload(media: mediaToUpload)
+ self.show_library = false
+ }
+ }
+ Button(NSLocalizedString("Cancel", comment: "Button to cancel the upload."), role: .cancel) {}
+ }
+ }
+ }
+
+ private func handle_upload(media: MediaUpload) {
+ uploadObserver.isLoading = true
+ Task {
+ let res = await image_upload.start(media: media, uploader: uploader)
+
+ switch res {
+ case .success(let urlString):
+ let url = URL(string: urlString)
+ image_url = url
+ callback(url)
+ case .failed(let error):
+ if let error {
+ print("Error uploading profile image \(error.localizedDescription)")
+ } else {
+ print("Error uploading image :(")
+ }
+ callback(nil)
+ }
+ uploadObserver.isLoading = false
+ }
+ }
+}
diff --git a/damus/Views/Profile/EditProfilePictureControl.swift b/damus/Views/Profile/EditProfilePictureControl.swift
@@ -1,106 +0,0 @@
-//
-// ProfilePictureEditView.swift
-// damus
-//
-// Created by Joel Klabo on 3/30/23.
-//
-
-import SwiftUI
-
-struct EditProfilePictureControl: View {
- let uploader: MediaUploader
- let pubkey: String
- @Binding var profile_image: URL?
- @ObservedObject var viewModel: ProfileUploadingViewModel
- let callback: (URL?) -> Void
-
- @StateObject var image_upload: ImageUploadModel = ImageUploadModel()
-
- @State private var show_camera = false
- @State private var show_library = false
- @State var image_upload_confirm: Bool = false
-
- @State var mediaToUpload: MediaUpload? = nil
-
- var body: some View {
- Menu {
- Button(action: {
- self.show_library = true
- }) {
- Text("Choose from Library", comment: "Option to select photo from library")
- }
-
- Button(action: {
- self.show_camera = true
- }) {
- Text("Take Photo", comment: "Option to take a photo with the camera")
- }
- } label: {
- if viewModel.isLoading {
- ProgressView()
- } else {
- Image("camera")
- .resizable()
- .scaledToFit()
- .frame(width: 25, height: 25)
- .foregroundColor(DamusColors.white)
- }
- }
- .sheet(isPresented: $show_camera) {
-
- ImagePicker(uploader: uploader, sourceType: .camera, pubkey: pubkey, image_upload_confirm: $image_upload_confirm, imagesOnly: true) { img in
- self.mediaToUpload = .image(img)
- } onVideoPicked: { url in
- print("Cannot upload videos as profile image")
- }
- .alert(NSLocalizedString("Are you sure you want to upload this image?", comment: "Alert message asking if the user wants to upload an image."), isPresented: $image_upload_confirm) {
- Button(NSLocalizedString("Upload", comment: "Button to proceed with uploading."), role: .none) {
- if let mediaToUpload {
- self.handle_upload(media: mediaToUpload)
- self.show_camera = false
- }
- }
- Button(NSLocalizedString("Cancel", comment: "Button to cancel the upload."), role: .cancel) {}
- }
- }
- .sheet(isPresented: $show_library) {
- ImagePicker(uploader: uploader, sourceType: .photoLibrary, pubkey: pubkey, image_upload_confirm: $image_upload_confirm, imagesOnly: true) { img in
- self.mediaToUpload = .image(img)
-
- } onVideoPicked: { url in
- print("Cannot upload videos as profile image")
- }
- .alert(NSLocalizedString("Are you sure you want to upload this image?", comment: "Alert message asking if the user wants to upload an image."), isPresented: $image_upload_confirm) {
- Button(NSLocalizedString("Upload", comment: "Button to proceed with uploading."), role: .none) {
- if let mediaToUpload {
- self.handle_upload(media: mediaToUpload)
- self.show_library = false
- }
- }
- Button(NSLocalizedString("Cancel", comment: "Button to cancel the upload."), role: .cancel) {}
- }
- }
- }
-
- private func handle_upload(media: MediaUpload) {
- viewModel.isLoading = true
- Task {
- let res = await image_upload.start(media: media, uploader: uploader)
-
- switch res {
- case .success(let urlString):
- let url = URL(string: urlString)
- profile_image = url
- callback(url)
- case .failed(let error):
- if let error {
- print("Error uploading profile image \(error.localizedDescription)")
- } else {
- print("Error uploading image :(")
- }
- callback(nil)
- }
- viewModel.isLoading = false
- }
- }
-}
diff --git a/damus/Views/Profile/ProfilePictureSelector.swift b/damus/Views/Profile/ProfilePictureSelector.swift
@@ -9,7 +9,7 @@ import SwiftUI
import Combine
-class ProfileUploadingViewModel: ObservableObject {
+class ImageUploadingObserver: ObservableObject {
@Published var isLoading: Bool = false
}
@@ -18,7 +18,7 @@ struct ProfilePictureSelector: View {
let pubkey: String
var size: CGFloat = 80.0
var damus_state: DamusState?
- @ObservedObject var viewModel: ProfileUploadingViewModel
+ @ObservedObject var uploadObserver: ImageUploadingObserver
let callback: (URL?) -> Void
@State var profile_image: URL? = nil
@@ -31,7 +31,7 @@ struct ProfilePictureSelector: View {
let highlight: Highlight = .custom(Color.white, 2.0)
ZStack {
EditProfilePictureView(url: $profile_image, pubkey: pubkey, size: size, highlight: highlight, damus_state: damus_state)
- EditProfilePictureControl(uploader: uploader, pubkey: pubkey, profile_image: $profile_image, viewModel: viewModel, callback: callback)
+ EditPictureControl(pubkey: pubkey, image_url: $profile_image, uploadObserver: uploadObserver, callback: callback)
}
}
}
@@ -39,7 +39,7 @@ struct ProfilePictureSelector: View {
struct ProfilePictureSelector_Previews: PreviewProvider {
static var previews: some View {
let test_pubkey = "ff48854ac6555fed8e439ebb4fa2d928410e0eef13fa41164ec45aaaa132d846"
- ProfilePictureSelector(pubkey: test_pubkey, viewModel: ProfileUploadingViewModel()) { _ in
+ ProfilePictureSelector(pubkey: test_pubkey, uploadObserver: ImageUploadingObserver()) { _ in
//
}
}