commit c9c51c6d4ad7cb4ed102da8d097d799ff11a9e43
parent b63159a29f55539aad2c84ff543d501005c733dc
Author: Joel Klabo <joelklabo@gmail.com>
Date: Thu, 30 Mar 2023 20:59:24 -0700
Profile Picture Upload
Changelog-Added: Profile Picture Upload
Closes: #849
Diffstat:
11 files changed, 272 insertions(+), 119 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -253,10 +253,12 @@
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
E9E4ED0B295867B900DD7078 /* ThreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E4ED0A295867B900DD7078 /* ThreadView.swift */; };
+ F757933A29D7AECD007DEAC1 /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F757933929D7AECD007DEAC1 /* ImagePicker.swift */; };
F75BA12D29A1855400E10810 /* BookmarksManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75BA12C29A1855400E10810 /* BookmarksManager.swift */; };
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 */; };
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */; };
F7F0BA272978E54D009531F3 /* ParicipantsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA262978E54D009531F3 /* ParicipantsView.swift */; };
/* End PBXBuildFile section */
@@ -642,10 +644,12 @@
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
E9E4ED0A295867B900DD7078 /* ThreadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadView.swift; sourceTree = "<group>"; };
+ F757933929D7AECD007DEAC1 /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = "<group>"; };
F75BA12C29A1855400E10810 /* BookmarksManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksManager.swift; sourceTree = "<group>"; };
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>"; };
F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToDismiss.swift; sourceTree = "<group>"; };
F7F0BA262978E54D009531F3 /* ParicipantsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParicipantsView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -859,6 +863,7 @@
4C75EFAC28049CFB0006080F /* PostButton.swift */,
4C75EFA327FA577B0006080F /* PostView.swift */,
9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */,
+ F757933929D7AECD007DEAC1 /* ImagePicker.swift */,
9C83F89229A937B900136C08 /* TextViewWrapper.swift */,
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */,
4C3AC7A42836987600E1F516 /* MainTabView.swift */,
@@ -985,6 +990,7 @@
children = (
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
+ F79C7FAC29D5E9620000F946 /* EditProfilePictureControl.swift */,
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
4C8682862814DE470026224F /* ProfileView.swift */,
4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */,
@@ -1428,6 +1434,7 @@
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */,
4C54AA0C29A5543C003E4487 /* ZapGroup.swift in Sources */,
4C75EFB728049D990006080F /* RelayPool.swift in Sources */,
+ F757933A29D7AECD007DEAC1 /* ImagePicker.swift in Sources */,
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */,
4CB8838D296F710400DC99E7 /* Reposted.swift in Sources */,
4CCEB7A929B29DD50078AA28 /* SearchResultsModel.swift in Sources */,
@@ -1542,6 +1549,7 @@
4CE0E2B629A3ED5500DB4CA2 /* InnerTimelineView.swift in Sources */,
4C363A8828236948006E126D /* BlocksView.swift in Sources */,
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */,
+ F79C7FAD29D5E9620000F946 /* EditProfilePictureControl.swift in Sources */,
4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */,
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
@@ -1931,7 +1939,9 @@
INFOPLIST_FILE = damus/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Damus;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
+ INFOPLIST_KEY_NSCameraUsageDescription = "Damus needs access to your camera if you want to upload photos from it";
INFOPLIST_KEY_NSFaceIDUsageDescription = "Local authentication to access private key";
+ INFOPLIST_KEY_NSMicrophoneUsageDescription = "Damus needs access to your microphone if you want to upload recorded videos from it";
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images.";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -1973,7 +1983,9 @@
INFOPLIST_FILE = damus/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Damus;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
+ INFOPLIST_KEY_NSCameraUsageDescription = "Damus needs access to your camera if you want to upload photos from it";
INFOPLIST_KEY_NSFaceIDUsageDescription = "Local authentication to access private key";
+ INFOPLIST_KEY_NSMicrophoneUsageDescription = "Damus needs access to your microphone if you want to upload recorded videos from it";
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images.";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
diff --git a/damus/Models/CreateAccountModel.swift b/damus/Models/CreateAccountModel.swift
@@ -14,6 +14,7 @@ class CreateAccountModel: ObservableObject {
@Published var about: String = ""
@Published var pubkey: String = ""
@Published var privkey: String = ""
+ @Published var profile_image: String? = nil
var pubkey_bech32: String {
return bech32_pubkey(self.pubkey) ?? ""
diff --git a/damus/Nostr/NostrMetadata.swift b/damus/Nostr/NostrMetadata.swift
@@ -21,5 +21,5 @@ struct NostrMetadata: Codable {
}
func create_account_to_metadata(_ model: CreateAccountModel) -> NostrMetadata {
- return NostrMetadata(display_name: model.real_name, name: model.nick_name, about: model.about, website: nil, nip05: nil, picture: nil, banner: nil, lud06: nil, lud16: nil)
+ return NostrMetadata(display_name: model.real_name, name: model.nick_name, about: model.about, website: nil, nip05: nil, picture: model.profile_image, banner: nil, lud06: nil, lud16: nil)
}
diff --git a/damus/Views/AttachMediaUtility.swift b/damus/Views/AttachMediaUtility.swift
@@ -80,118 +80,6 @@ func create_upload_request(mediaToUpload: MediaUpload, mediaUploader: MediaUploa
}
}
-extension PostView {
- struct ImagePicker: UIViewControllerRepresentable {
-
- @Environment(\.presentationMode)
- private var presentationMode
-
- let sourceType: UIImagePickerController.SourceType
- let damusState: DamusState
- let onImagePicked: (URL) -> Void
- let onVideoPicked: (URL) -> Void
-
- final class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
- @Binding private var presentationMode: PresentationMode
- private let sourceType: UIImagePickerController.SourceType
- private let onImagePicked: (URL) -> Void
- private let onVideoPicked: (URL) -> Void
-
- init(presentationMode: Binding<PresentationMode>,
- sourceType: UIImagePickerController.SourceType,
- onImagePicked: @escaping (URL) -> Void,
- onVideoPicked: @escaping (URL) -> Void) {
- _presentationMode = presentationMode
- self.sourceType = sourceType
- self.onImagePicked = onImagePicked
- self.onVideoPicked = onVideoPicked
- }
-
- func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
- if let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
- // Handle the selected video
- onVideoPicked(videoURL)
- } else if let imageURL = info[UIImagePickerController.InfoKey.imageURL] as? URL {
- // Handle the selected image
- onImagePicked(imageURL)
- } else if let cameraImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
- if let imageURL = saveImageToTemporaryFolder(image: cameraImage, imageType: "jpeg") {
- onImagePicked(imageURL)
- }
- } else if let editedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
- if let editedImageURL = saveImageToTemporaryFolder(image: editedImage) {
- onImagePicked(editedImageURL)
- }
- }
- presentationMode.dismiss()
- }
-
- func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
- presentationMode.dismiss()
- }
-
- func saveImageToTemporaryFolder(image: UIImage, imageType: String = "png") -> URL? {
- // Convert UIImage to Data
- let imageData: Data?
- if imageType.lowercased() == "jpeg" {
- imageData = image.jpegData(compressionQuality: 1.0)
- } else {
- imageData = image.pngData()
- }
-
- guard let data = imageData else {
- print("Failed to convert UIImage to Data.")
- return nil
- }
-
- // Generate a temporary URL with a unique filename
- let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
- let uniqueImageName = "\(UUID().uuidString).\(imageType)"
- let temporaryImageURL = temporaryDirectoryURL.appendingPathComponent(uniqueImageName)
-
- // Save the image data to the temporary URL
- do {
- try data.write(to: temporaryImageURL)
- return temporaryImageURL
- } catch {
- print("Error saving image data to temporary URL: \(error.localizedDescription)")
- return nil
- }
- }
- }
-
- func makeCoordinator() -> Coordinator {
- return Coordinator(presentationMode: presentationMode,
- sourceType: sourceType,
- onImagePicked: { url in
- // Handle the selected image URL
- onImagePicked(url)
- },
- onVideoPicked: { videoURL in
- // Handle the selected video URL
- onVideoPicked(videoURL)
- })
- }
-
- func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
- let picker = UIImagePickerController()
- picker.sourceType = sourceType
- let mediaUploader = get_media_uploader(damusState.keypair.pubkey)
- picker.mediaTypes = ["public.image", "com.compuserve.gif"]
- if mediaUploader.supportsVideo {
- picker.mediaTypes.append("public.movie")
- }
- picker.delegate = context.coordinator
- return picker
- }
-
- func updateUIViewController(_ uiViewController: UIImagePickerController,
- context: UIViewControllerRepresentableContext<ImagePicker>) {
-
- }
- }
-}
-
extension NSMutableData {
func appendString(string: String) {
guard let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true) else {
diff --git a/damus/Views/CreateAccountView.swift b/damus/Views/CreateAccountView.swift
@@ -9,9 +9,12 @@ import SwiftUI
struct CreateAccountView: View {
@StateObject var account: CreateAccountModel = CreateAccountModel()
+ @StateObject var profileUploadViewModel = ProfileUploadingViewModel()
+
@State var is_light: Bool = false
@State var is_done: Bool = false
@State var reading_eula: Bool = false
+ @State var profile_image: URL? = nil
func SignupForm<FormContent: View>(@ViewBuilder content: () -> FormContent) -> some View {
return VStack(alignment: .leading, spacing: 10.0, content: content)
@@ -32,7 +35,7 @@ struct CreateAccountView: View {
.font(.title.bold())
.foregroundColor(.white)
- ProfilePictureSelector(pubkey: account.pubkey)
+ ProfilePictureSelector(pubkey: account.pubkey, viewModel: profileUploadViewModel, callback: uploadedProfilePicture(image_url:))
HStack(alignment: .top) {
VStack {
@@ -81,6 +84,8 @@ struct CreateAccountView: View {
self.is_done = true
}
.padding()
+ .disabled(profileUploadViewModel.isLoading)
+ .opacity(profileUploadViewModel.isLoading ? 0.5 : 1)
}
.padding(.leading, 14.0)
.padding(.trailing, 20.0)
@@ -91,6 +96,10 @@ struct CreateAccountView: View {
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: BackNav())
}
+
+ func uploadedProfilePicture(image_url: URL?) {
+ account.profile_image = image_url?.absoluteString
+ }
}
struct BackNav: View {
diff --git a/damus/Views/EditMetadataView.swift b/damus/Views/EditMetadataView.swift
@@ -67,6 +67,7 @@ struct EditMetadataView: View {
@Environment(\.colorScheme) var colorScheme
@State var confirm_ln_address: Bool = false
+ @StateObject var profileUploadViewModel = ProfileUploadingViewModel()
init (damus_state: DamusState) {
self.damus_state = damus_state
@@ -126,7 +127,7 @@ struct EditMetadataView: View {
let pfp_size: CGFloat = 90.0
HStack(alignment: .center) {
- ProfilePicView(pubkey: damus_state.pubkey, size: pfp_size, highlight: .custom(imageBorderColor(), 4.0), profiles: damus_state.profiles)
+ ProfilePictureSelector(pubkey: damus_state.pubkey, damus_state: damus_state, viewModel: profileUploadViewModel, callback: uploadedProfilePicture(image_url:))
.offset(y: -(pfp_size/2.0)) // Increase if set a frame
Spacer()
@@ -214,6 +215,7 @@ struct EditMetadataView: View {
dismiss()
}
}
+ .disabled(profileUploadViewModel.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.")) {
}
@@ -224,6 +226,10 @@ struct EditMetadataView: View {
}
.ignoresSafeArea(edges: .top)
}
+
+ func uploadedProfilePicture(image_url: URL?) {
+ picture = image_url?.absoluteString ?? ""
+ }
}
struct EditMetadataView_Previews: PreviewProvider {
diff --git a/damus/Views/ImagePicker.swift b/damus/Views/ImagePicker.swift
@@ -0,0 +1,83 @@
+//
+// ImagePicker.swift
+// damus
+//
+// Created by Swift on 3/31/23.
+//
+
+import UIKit
+import SwiftUI
+
+struct ImagePicker: UIViewControllerRepresentable {
+
+ @Environment(\.presentationMode)
+ private var presentationMode
+
+ let sourceType: UIImagePickerController.SourceType
+ let pubkey: String
+ var imagesOnly: Bool = false
+ let onImagePicked: (URL) -> Void
+ let onVideoPicked: (URL) -> Void
+
+ final class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
+ @Binding private var presentationMode: PresentationMode
+ private let sourceType: UIImagePickerController.SourceType
+ private let onImagePicked: (URL) -> Void
+ private let onVideoPicked: (URL) -> Void
+
+ init(presentationMode: Binding<PresentationMode>,
+ sourceType: UIImagePickerController.SourceType,
+ onImagePicked: @escaping (URL) -> Void,
+ onVideoPicked: @escaping (URL) -> Void) {
+ _presentationMode = presentationMode
+ self.sourceType = sourceType
+ self.onImagePicked = onImagePicked
+ self.onVideoPicked = onVideoPicked
+ }
+
+ func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
+ if let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
+ // Handle the selected video
+ onVideoPicked(videoURL)
+ } else if let imageURL = info[UIImagePickerController.InfoKey.imageURL] as? URL {
+ // Handle the selected image
+ self.onImagePicked(imageURL)
+ }
+ presentationMode.dismiss()
+ }
+
+ func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
+ presentationMode.dismiss()
+ }
+ }
+
+ func makeCoordinator() -> Coordinator {
+ return Coordinator(presentationMode: presentationMode,
+ sourceType: sourceType,
+ onImagePicked: { url in
+ // Handle the selected image URL
+ onImagePicked(url)
+ },
+ onVideoPicked: { videoURL in
+ // Handle the selected video URL
+ onVideoPicked(videoURL)
+ })
+ }
+
+ func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
+ let picker = UIImagePickerController()
+ picker.sourceType = sourceType
+ let mediaUploader = get_media_uploader(pubkey)
+ picker.mediaTypes = ["public.image", "com.compuserve.gif"]
+ if mediaUploader.supportsVideo && !imagesOnly {
+ picker.mediaTypes.append("public.movie")
+ }
+ picker.delegate = context.coordinator
+ return picker
+ }
+
+ func updateUIViewController(_ uiViewController: UIImagePickerController,
+ context: UIViewControllerRepresentableContext<ImagePicker>) {
+
+ }
+}
diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift
@@ -246,14 +246,14 @@ struct PostView: View {
}
.padding()
.sheet(isPresented: $attach_media) {
- ImagePicker(sourceType: .photoLibrary, damusState: damus_state) { img in
+ ImagePicker(sourceType: .photoLibrary, pubkey: damus_state.pubkey) { img in
handle_upload(media: .image(img))
} onVideoPicked: { url in
handle_upload(media: .video(url))
}
}
.sheet(isPresented: $attach_camera) {
- ImagePicker(sourceType: .camera, damusState: damus_state) { img in
+ ImagePicker(sourceType: .camera, pubkey: damus_state.pubkey) { img in
handle_upload(media: .image(img))
} onVideoPicked: { url in
handle_upload(media: .video(url))
diff --git a/damus/Views/Profile/EditProfilePictureControl.swift b/damus/Views/Profile/EditProfilePictureControl.swift
@@ -0,0 +1,84 @@
+//
+// ProfilePictureEditView.swift
+// damus
+//
+// Created by Joel Klabo on 3/30/23.
+//
+
+import SwiftUI
+
+struct EditProfilePictureControl: View {
+
+ 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
+
+ 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(systemName: "camera")
+ .resizable()
+ .scaledToFit()
+ .frame(width: 25, height: 25)
+ .foregroundColor(DamusColors.white)
+ }
+ }
+ .sheet(isPresented: $show_camera) {
+ ImagePicker(sourceType: .camera, pubkey: pubkey, imagesOnly: true) { img in
+ handle_upload(media: .image(img))
+ } onVideoPicked: { url in
+ print("Cannot upload videos as profile image")
+ }
+ }
+ .sheet(isPresented: $show_library) {
+ ImagePicker(sourceType: .photoLibrary, pubkey: pubkey, imagesOnly: true) { img in
+ handle_upload(media: .image(img))
+ } onVideoPicked: { url in
+ print("Cannot upload videos as profile image")
+ }
+ }
+ }
+
+ private func handle_upload(media: MediaUpload) {
+ viewModel.isLoading = true
+ let uploader = get_media_uploader(pubkey)
+ 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/ProfilePicView.swift b/damus/Views/Profile/ProfilePicView.swift
@@ -32,6 +32,60 @@ func pfp_line_width(_ h: Highlight) -> CGFloat {
}
}
+struct EditProfilePictureView: View {
+
+ @Binding var url: URL?
+
+ let pubkey: String
+ let size: CGFloat
+ let highlight: Highlight
+
+ var damus_state: DamusState?
+
+ var PlaceholderColor: Color {
+ return id_to_color(pubkey)
+ }
+
+ var Placeholder: some View {
+ PlaceholderColor
+ .frame(width: size, height: size)
+ .clipShape(Circle())
+ .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
+ .padding(2)
+ }
+
+ var body: some View {
+ ZStack {
+ Color(uiColor: .systemBackground)
+
+ KFAnimatedImage(get_profile_url())
+ .imageContext(.pfp)
+ .cancelOnDisappear(true)
+ .configure { view in
+ view.framePreloadCount = 3
+ }
+ .placeholder { _ in
+ Placeholder
+ }
+ .scaledToFill()
+ .opacity(0.5)
+ }
+ .frame(width: size, height: size)
+ .clipShape(Circle())
+ .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
+ }
+
+ private func get_profile_url() -> URL? {
+ if let url {
+ return url
+ } else if let state = damus_state, let picture = state.profiles.lookup(id: pubkey)?.picture {
+ return URL(string: picture)
+ } else {
+ return url ?? URL(string: robohash(pubkey))
+ }
+ }
+}
+
struct InnerProfilePicView: View {
let url: URL?
diff --git a/damus/Views/Profile/ProfilePictureSelector.swift b/damus/Views/Profile/ProfilePictureSelector.swift
@@ -7,13 +7,27 @@
import SwiftUI
+import Combine
+
+class ProfileUploadingViewModel: ObservableObject {
+ @Published var isLoading: Bool = false
+}
+
struct ProfilePictureSelector: View {
+
let pubkey: String
+ var size: CGFloat = 80.0
+ var damus_state: DamusState?
+ @ObservedObject var viewModel: ProfileUploadingViewModel
+ let callback: (URL?) -> Void
+
+ @State var profile_image: URL? = nil
var body: some View {
let highlight: Highlight = .custom(Color.white, 2.0)
ZStack {
- ProfilePicView(pubkey: pubkey, size: 80.0, highlight: highlight, profiles: Profiles())
+ EditProfilePictureView(url: $profile_image, pubkey: pubkey, size: size, highlight: highlight, damus_state: damus_state)
+ EditProfilePictureControl(pubkey: pubkey, profile_image: $profile_image, viewModel: viewModel, callback: callback)
}
}
}
@@ -21,6 +35,8 @@ struct ProfilePictureSelector: View {
struct ProfilePictureSelector_Previews: PreviewProvider {
static var previews: some View {
let test_pubkey = "ff48854ac6555fed8e439ebb4fa2d928410e0eef13fa41164ec45aaaa132d846"
- ProfilePictureSelector(pubkey: test_pubkey)
+ ProfilePictureSelector(pubkey: test_pubkey, viewModel: ProfileUploadingViewModel()) { _ in
+ //
+ }
}
}