diff --git a/damus/Assets.xcassets/damoose.imageset/Contents.json b/damus/Assets.xcassets/damoose.imageset/Contents.json new file mode 100644 index 00000000..08bec4c4 --- /dev/null +++ b/damus/Assets.xcassets/damoose.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "damoose.jpeg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/damus/Assets.xcassets/damoose.imageset/damoose.jpeg b/damus/Assets.xcassets/damoose.imageset/damoose.jpeg new file mode 100644 index 00000000..86d64a90 Binary files /dev/null and b/damus/Assets.xcassets/damoose.imageset/damoose.jpeg differ diff --git a/damus/Views/BannerImageView.swift b/damus/Views/BannerImageView.swift index 10bbac6f..b1afd01a 100644 --- a/damus/Views/BannerImageView.swift +++ b/damus/Views/BannerImageView.swift @@ -13,7 +13,7 @@ struct EditBannerImageView: View { var damus_state: DamusState @ObservedObject var viewModel: ImageUploadingObserver let callback: (URL?) -> Void - let defaultImage = UIImage(named: "profile-banner") ?? UIImage() + let defaultImage = UIImage(named: "damoose") ?? UIImage() @State var banner_image: URL? = nil @@ -38,7 +38,7 @@ struct EditBannerImageView: View { struct InnerBannerImageView: View { let disable_animation: Bool let url: URL? - let defaultImage = UIImage(named: "profile-banner") ?? UIImage() + let defaultImage = UIImage(named: "damoose") ?? UIImage() var body: some View { ZStack { diff --git a/damus/Views/Profile/EditMetadataView.swift b/damus/Views/Profile/EditMetadataView.swift index 99eb473d..dc982175 100644 --- a/damus/Views/Profile/EditMetadataView.swift +++ b/damus/Views/Profile/EditMetadataView.swift @@ -21,13 +21,15 @@ struct EditMetadataView: View { @State var ln: String @State var website: String - @Environment(\.dismiss) var dismiss - @State var confirm_ln_address: Bool = false + @State var confirm_save_alert: Bool = false @StateObject var profileUploadObserver = ImageUploadingObserver() @StateObject var bannerUploadObserver = ImageUploadingObserver() + @Environment(\.dismiss) var dismiss + @Environment(\.presentationMode) var presentationMode + init(damus_state: DamusState) { self.damus_state = damus_state let profile_txn = damus_state.profiles.lookup(id: damus_state.pubkey) @@ -77,7 +79,7 @@ struct EditMetadataView: View { var TopSection: some View { ZStack(alignment: .top) { GeometryReader { geo in - EditBannerImageView(damus_state: damus_state, viewModel: bannerUploadObserver, callback: uploadedBanner(image_url:)) + EditBannerImageView(damus_state: damus_state, viewModel: bannerUploadObserver, callback: uploadedBanner(image_url:), banner_image: URL(string: banner)) .aspectRatio(contentMode: .fill) .frame(width: geo.size.width, height: BANNER_HEIGHT) .clipped() @@ -86,7 +88,7 @@ struct EditMetadataView: View { let pfp_size: CGFloat = 90.0 HStack(alignment: .center) { - EditProfilePictureView(pubkey: damus_state.pubkey, damus_state: damus_state, size: pfp_size, uploadObserver: profileUploadObserver, callback: uploadedProfilePicture(image_url:)) + EditProfilePictureView(profile_url: URL(string: picture), pubkey: damus_state.pubkey, damus_state: damus_state, size: pfp_size, uploadObserver: profileUploadObserver, callback: uploadedProfilePicture(image_url:)) .offset(y: -(pfp_size/2.0)) // Increase if set a frame Spacer() @@ -97,6 +99,28 @@ struct EditMetadataView: View { } } + func navImage(img: String) -> some View { + Image(img) + .frame(width: 33, height: 33) + .background(Color.black.opacity(0.6)) + .clipShape(Circle()) + } + + var navBackButton: some View { + HStack { + Button { + if didChange() { + confirm_save_alert.toggle() + } else { + presentationMode.wrappedValue.dismiss() + } + } label: { + navImage(img: "chevron-left") + } + Spacer() + } + } + var body: some View { VStack(alignment: .leading) { TopSection @@ -116,18 +140,6 @@ struct EditMetadataView: View { } - Section (NSLocalizedString("Profile Picture", comment: "Label for Profile Picture section of user profile form.")) { - TextField(NSLocalizedString("https://example.com/pic.jpg", comment: "Placeholder example text for profile picture URL."), text: $picture) - .autocorrectionDisabled(true) - .textInputAutocapitalization(.never) - } - - Section (NSLocalizedString("Banner Image", comment: "Label for Banner Image section of user profile form.")) { - TextField(NSLocalizedString("https://example.com/pic.jpg", comment: "Placeholder example text for profile picture URL."), text: $banner) - .autocorrectionDisabled(true) - .textInputAutocapitalization(.never) - } - Section(NSLocalizedString("Website", comment: "Label for Website section of user profile form.")) { TextField(NSLocalizedString("https://jb55.com", comment: "Placeholder example text for website URL for user profile."), text: $website) .autocorrectionDisabled(true) @@ -139,10 +151,10 @@ struct EditMetadataView: View { ZStack(alignment: .topLeading) { TextEditor(text: $about) .textInputAutocapitalization(.sentences) - .frame(minHeight: 20, alignment: .leading) + .frame(minHeight: 45, alignment: .leading) .multilineTextAlignment(.leading) Text(about.isEmpty ? placeholder : about) - .padding(.leading, 4) + .padding(4) .opacity(about.isEmpty ? 1 : 0) .foregroundColor(Color(uiColor: .placeholderText)) } @@ -175,25 +187,48 @@ struct EditMetadataView: View { } }) - Button(NSLocalizedString("Save", comment: "Button for saving profile.")) { - if !ln.isEmpty && !is_ln_valid(ln: ln) { - confirm_ln_address = true - } else { - save() - dismiss() - } + + } + + Button(action: { + if !ln.isEmpty && !is_ln_valid(ln: ln) { + confirm_ln_address = true + } else { + save() + dismiss() } - .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.")) { - } - } message: { - Text("The address should either begin with LNURL or should look like an email address.", comment: "Giving the description of the alert message.") + }, label: { + Text(NSLocalizedString("Save", comment: "Button for saving profile.")) + .frame(minWidth: 300, maxWidth: .infinity, alignment: .center) + }) + .buttonStyle(GradientButtonStyle(padding: 15)) + .padding(.horizontal, 10) + .padding(.bottom, 10) + .disabled(!didChange()) + .opacity(!didChange() ? 0.5 : 1) + .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.")) { } + } message: { + Text("The address should either begin with LNURL or should look like an email address.", comment: "Giving the description of the alert message.") } } .ignoresSafeArea(edges: .top) .background(Color(.systemGroupedBackground)) + .navigationBarBackButtonHidden() + .toolbar { + ToolbarItem(placement: .principal) { + navBackButton + } + } + .alert(NSLocalizedString("Discard changes?", comment: "Alert user that changes have been made."), isPresented: $confirm_save_alert) { + Button(NSLocalizedString("No", comment: "Do not discard changes."), role: .cancel) { + } + Button(NSLocalizedString("Yes", comment: "Agree to discard changes made to profile.")) { + dismiss() + } + } } func uploadedProfilePicture(image_url: URL?) { @@ -203,6 +238,45 @@ struct EditMetadataView: View { func uploadedBanner(image_url: URL?) { banner = image_url?.absoluteString ?? "" } + + func didChange() -> Bool { + let profile_txn = damus_state.profiles.lookup(id: damus_state.pubkey) + let data = profile_txn?.unsafeUnownedValue + + if data?.name ?? "" != name { + return true + } + + if data?.display_name ?? "" != display_name { + return true + } + + if data?.about ?? "" != about { + return true + } + + if data?.website ?? "" != website { + return true + } + + if data?.picture ?? "" != picture { + return true + } + + if data?.banner ?? "" != banner { + return true + } + + if data?.nip05 ?? "" != nip05 { + return true + } + + if data?.lud16 ?? data?.lud06 ?? "" != ln { + return true + } + + return false + } } struct EditMetadataView_Previews: PreviewProvider { diff --git a/damus/Views/Profile/EditPictureControl.swift b/damus/Views/Profile/EditPictureControl.swift index 6df5f2cf..454ced3b 100644 --- a/damus/Views/Profile/EditPictureControl.swift +++ b/damus/Views/Profile/EditPictureControl.swift @@ -18,6 +18,7 @@ struct EditPictureControl: View { var size: CGFloat? = 25 var setup: Bool? = false @Binding var image_url: URL? + @State var image_url_temp: URL? @ObservedObject var uploadObserver: ImageUploadingObserver let callback: (URL?) -> Void @@ -25,12 +26,21 @@ struct EditPictureControl: View { @State private var show_camera = false @State private var show_library = false + @State private var show_url_sheet = false @State var image_upload_confirm: Bool = false @State var preUploadedMedia: PreUploadedMedia? = nil + + @Environment(\.dismiss) var dismiss var body: some View { Menu { + Button(action: { + self.show_url_sheet = true + }) { + Text("Image URL", comment: "Option to enter a url") + } + Button(action: { self.show_library = true }) { @@ -51,7 +61,7 @@ struct EditPictureControl: View { .background(DamusColors.white.opacity(0.7)) .clipShape(Circle()) .shadow(color: DamusColors.purple, radius: 15, x: 0, y: 0) - } else if let url = image_url { + } else if let url = image_url, setup ?? false { KFAnimatedImage(url) .imageContext(.pfp, disable_animation: false) .onFailure(fallbackUrl: URL(string: robohash(pubkey)), cacheKey: url.absoluteString) @@ -115,6 +125,70 @@ struct EditPictureControl: View { Button(NSLocalizedString("Cancel", comment: "Button to cancel the upload."), role: .cancel) {} } } + .sheet(isPresented: $show_url_sheet) { + ZStack { + DamusColors.adaptableWhite.edgesIgnoringSafeArea(.all) + VStack { + Text("Image URL") + .bold() + + Divider() + .padding(.horizontal) + + HStack { + Image(systemName: "doc.on.clipboard") + .foregroundColor(.gray) + .onTapGesture { + if let pastedURL = UIPasteboard.general.string { + image_url_temp = URL(string: pastedURL) + } + } + TextField(image_url_temp?.absoluteString ?? "", text: Binding( + get: { image_url_temp?.absoluteString ?? "" }, + set: { image_url_temp = URL(string: $0) } + )) + } + .padding(12) + .background { + RoundedRectangle(cornerRadius: 12) + .stroke(.gray.opacity(0.5), lineWidth: 1) + .background { + RoundedRectangle(cornerRadius: 12) + .foregroundColor(.damusAdaptableWhite) + } + } + .padding(10) + + Button(action: { + show_url_sheet.toggle() + }, label: { + Text("Cancel", comment: "Cancel button text for dismissing updating image url.") + .frame(minWidth: 300, maxWidth: .infinity, alignment: .center) + .padding(10) + }) + .buttonStyle(NeutralButtonStyle()) + .padding(10) + + Button(action: { + image_url = image_url_temp + callback(image_url) + show_url_sheet.toggle() + }, label: { + Text("Update", comment: "Update button text for updating image url.") + .frame(minWidth: 300, maxWidth: .infinity, alignment: .center) + }) + .buttonStyle(GradientButtonStyle(padding: 10)) + .padding(.horizontal, 10) + .disabled(image_url_temp == image_url) + .opacity(image_url_temp == image_url ? 0.5 : 1) + } + } + .onAppear { + image_url_temp = image_url + } + .presentationDetents([.height(300)]) + .presentationDragIndicator(.visible) + } } private func handle_upload(media: MediaUpload) {