diff --git a/BaseStyle/BaseStyle.xcodeproj/project.pbxproj b/BaseStyle/BaseStyle.xcodeproj/project.pbxproj index 6f9a37e6..21ddd063 100644 --- a/BaseStyle/BaseStyle.xcodeproj/project.pbxproj +++ b/BaseStyle/BaseStyle.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 213A1F2A2C52335D00BF9800 /* CheckmarkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 213A1F292C52335D00BF9800 /* CheckmarkButton.swift */; }; 213F377E2C416C9C00972316 /* ScrollToTopButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 213F377D2C416C9C00972316 /* ScrollToTopButton.swift */; }; + 2163D3B82D27B2FD004B4F20 /* MediaPickerOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2163D3B72D27B2FD004B4F20 /* MediaPickerOptionsView.swift */; }; 217620462C4F7CE700FED0D4 /* BackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217620452C4F7CE700FED0D4 /* BackButton.swift */; }; 2176204A2C521EDF00FED0D4 /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217620492C521EDF00FED0D4 /* RadioButton.swift */; }; 219F43D92CCA4A7000729C67 /* RestoreButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 219F43D82CCA4A7000729C67 /* RestoreButton.swift */; }; @@ -71,6 +72,7 @@ 174198EE4AF2FC82DADEB060 /* Pods_BaseStyle.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BaseStyle.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 213A1F292C52335D00BF9800 /* CheckmarkButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckmarkButton.swift; sourceTree = ""; }; 213F377D2C416C9C00972316 /* ScrollToTopButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollToTopButton.swift; sourceTree = ""; }; + 2163D3B72D27B2FD004B4F20 /* MediaPickerOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPickerOptionsView.swift; sourceTree = ""; }; 217620452C4F7CE700FED0D4 /* BackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackButton.swift; sourceTree = ""; }; 217620492C521EDF00FED0D4 /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; 219F43D82CCA4A7000729C67 /* RestoreButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreButton.swift; sourceTree = ""; }; @@ -306,6 +308,7 @@ D89C93452BC42DE500FACD16 /* MailComposeView.swift */, D82174BD2BBAD86D00DB42C3 /* ProfileImageView.swift */, D8E244C02B986CD800C6C82A /* ImagePickerView.swift */, + 2163D3B72D27B2FD004B4F20 /* MediaPickerOptionsView.swift */, ); path = CustomUI; sourceTree = ""; @@ -521,6 +524,7 @@ 213A1F2A2C52335D00BF9800 /* CheckmarkButton.swift in Sources */, D8E244C12B986CD800C6C82A /* ImagePickerView.swift in Sources */, D8D42A862B85D08F009B345D /* Font+Extension.swift in Sources */, + 2163D3B82D27B2FD004B4F20 /* MediaPickerOptionsView.swift in Sources */, D8EB0ED82CAD8C9F00AC6A44 /* ErrorView.swift in Sources */, D8D42A992B870961009B345D /* AlertPrompt.swift in Sources */, 2176204A2C521EDF00FED0D4 /* RadioButton.swift in Sources */, diff --git a/BaseStyle/BaseStyle/CustomUI/MediaPickerOptionsView.swift b/BaseStyle/BaseStyle/CustomUI/MediaPickerOptionsView.swift new file mode 100644 index 00000000..af420690 --- /dev/null +++ b/BaseStyle/BaseStyle/CustomUI/MediaPickerOptionsView.swift @@ -0,0 +1,51 @@ +// +// MediaPickerOptionsView.swift +// BaseStyle +// +// Created by Nirali Sonani on 03/01/25. +// + +import SwiftUI + +public struct MediaPickerOptionsView: View { + + public let image: UIImage? + public let imageUrl: String? + public let withRemoveAllOption: Bool + + public let handleActionSelection: (ActionsOfSheet) -> Void + + public init(image: UIImage? = nil, imageUrl: String? = nil, withRemoveAllOption: Bool = false, + handleActionSelection: @escaping (ActionsOfSheet) -> Void) { + self.image = image + self.imageUrl = imageUrl + self.withRemoveAllOption = withRemoveAllOption + self.handleActionSelection = handleActionSelection + } + + public var body: some View { + if !withRemoveAllOption { + Button("Take a picture") { + handleActionSelection(.camera) + } + } + + Button(withRemoveAllOption ? "Gallery" : "Choose from Library") { + handleActionSelection(.gallery) + } + + if withRemoveAllOption || (image != nil || (imageUrl != nil && !(imageUrl?.isEmpty ?? false))) { + Button(withRemoveAllOption ? "Remove all" : "Remove", role: .destructive) { + handleActionSelection(withRemoveAllOption ? .removeAll : .remove) + } + } + } +} + +// MARK: - Media Picker Action sheet +public enum ActionsOfSheet { + case camera + case gallery + case remove + case removeAll +} diff --git a/Splito/Localization/Localizable.xcstrings b/Splito/Localization/Localizable.xcstrings index 02e7dda9..2c4d9a87 100644 --- a/Splito/Localization/Localizable.xcstrings +++ b/Splito/Localization/Localizable.xcstrings @@ -277,10 +277,7 @@ "Choose date" : { "extractionState" : "manual" }, - "Choose from Library" : { - - }, - "Choose mode" : { + "Choose mode\n Please choose your preferred mode to includes attachment with feedback" : { }, "Contact Support" : { @@ -405,9 +402,6 @@ }, "Forgot password?" : { - }, - "Gallery" : { - }, "get back" : { "extractionState" : "manual" @@ -627,9 +621,6 @@ }, "Phone Number" : { "extractionState" : "manual" - }, - "Please choose your preferred mode to attach image with feedback" : { - }, "Please enter a cost for your expense first!" : { "extractionState" : "manual" @@ -669,12 +660,6 @@ }, "recorded a payment from" : { "extractionState" : "manual" - }, - "Remove" : { - - }, - "Remove All" : { - }, "Remove from group" : { @@ -810,9 +795,6 @@ }, "Success" : { "extractionState" : "manual" - }, - "Take a picture" : { - }, "The amounts do not add up to the total cost of %@. You are %@ by %@." : { "extractionState" : "manual" diff --git a/Splito/UI/Home/Account/Feedback/FeedbackView.swift b/Splito/UI/Home/Account/Feedback/FeedbackView.swift index 5d2ddedf..fa40bc8c 100644 --- a/Splito/UI/Home/Account/Feedback/FeedbackView.swift +++ b/Splito/UI/Home/Account/Feedback/FeedbackView.swift @@ -26,14 +26,14 @@ struct FeedbackView: View { VSpacer(24) VStack(spacing: 24) { - FeedbackTitleFieldView(titleText: $viewModel.title, focusField: $focusField, - isSelected: focusField == .title, isValidTitle: viewModel.isValidTitle, - shouldShowValidationMessage: viewModel.shouldShowValidationMessage) + FeedbackTitleView(titleText: $viewModel.title, focusField: $focusField, + isSelected: focusField == .title, isValidTitle: viewModel.isValidTitle, + shouldShowValidationMessage: viewModel.shouldShowValidationMessage) FeedbackDescriptionView(titleText: $viewModel.description, focusField: $focusField, isSelected: focusField == .description) - FeedbackAttachImageView( + FeedbackAddAttachmentView( attachedImages: $viewModel.selectedAttachments, uploadingAttachments: $viewModel.uploadingAttachments, failedAttachments: $viewModel.failedAttachments, selectedAttachments: $viewModel.selectedAttachments, showImagePickerOption: $viewModel.showImagePickerOption, handleAttachmentTap: viewModel.handleAttachmentTap, onRemoveAttachmentTap: viewModel.onRemoveAttachment, onRetryButtonTap: viewModel.onRetryAttachment(_:), @@ -74,7 +74,7 @@ struct FeedbackView: View { } } -struct FeedbackTitleFieldView: View { +private struct FeedbackTitleView: View { @Binding var titleText: String var focusField: FocusState.Binding @@ -130,7 +130,7 @@ struct FeedbackTitleFieldView: View { } } -struct FeedbackDescriptionView: View { +private struct FeedbackDescriptionView: View { @Binding var titleText: String var focusField: FocusState.Binding @@ -167,7 +167,7 @@ struct FeedbackDescriptionView: View { } } -struct FeedbackAttachImageView: View { +private struct FeedbackAddAttachmentView: View { @Binding var attachedImages: [Attachment] @Binding var uploadingAttachments: [Attachment] @@ -178,14 +178,14 @@ struct FeedbackAttachImageView: View { let handleAttachmentTap: () -> Void let onRemoveAttachmentTap: (Attachment) -> Void let onRetryButtonTap: (Attachment) -> Void - let handleActionSelection: (FeedbackViewModel.ActionsOfSheet) -> Void + let handleActionSelection: (ActionsOfSheet) -> Void @FocusState var focusField: FeedbackViewModel.FocusedField? var body: some View { VStack(alignment: .leading, spacing: 16) { ForEach(attachedImages, id: \.id) { attachment in - AttachmentCellView( + FeedbackAttachmentCellView( attachment: attachment, isUploading: uploadingAttachments.contains(where: { $0.id == attachment.id }), shouldShowRetryButton: failedAttachments.contains(where: { $0.id == attachment.id }), @@ -213,29 +213,15 @@ struct FeedbackAttachImageView: View { .buttonStyle(.scale) } .frame(maxWidth: .infinity, alignment: .leading) - .actionSheet(isPresented: $showImagePickerOption) { - getActionSheet(withRemoveAllOption: $selectedAttachments.count >= 1, selection: handleActionSelection) + .confirmationDialog("Choose mode\n Please choose your preferred mode to includes attachment with feedback", + isPresented: $showImagePickerOption, titleVisibility: .visible) { + MediaPickerOptionsView(withRemoveAllOption: $selectedAttachments.count >= 1, + handleActionSelection: handleActionSelection) } } - - func getActionSheet(withRemoveAllOption: Bool, selection: @escaping ((FeedbackViewModel.ActionsOfSheet) -> Void)) -> ActionSheet { - let gallery: ActionSheet.Button = .default( - Text("Gallery")) { - selection(.gallery) - } - let removeAll: ActionSheet.Button = .destructive( - Text("Remove All")) { - selection(.removeAll) - } - let btn_cancel: ActionSheet.Button = .cancel(Text("Cancel")) - - return ActionSheet(title: Text("Choose mode"), - message: Text("Please choose your preferred mode to attach image with feedback"), - buttons: withRemoveAllOption ? [gallery, removeAll, btn_cancel] : [gallery, btn_cancel]) - } } -struct AttachmentCellView: View { +private struct FeedbackAttachmentCellView: View { let attachment: Attachment let isUploading: Bool @@ -270,7 +256,7 @@ struct AttachmentCellView: View { } } -struct AttachmentThumbnailView: View { +private struct AttachmentThumbnailView: View { let attachment: Attachment let isUploading: Bool diff --git a/Splito/UI/Home/Account/Feedback/FeedbackViewModel.swift b/Splito/UI/Home/Account/Feedback/FeedbackViewModel.swift index 6be177e3..981d5df7 100644 --- a/Splito/UI/Home/Account/Feedback/FeedbackViewModel.swift +++ b/Splito/UI/Home/Account/Feedback/FeedbackViewModel.swift @@ -137,6 +137,8 @@ extension FeedbackViewModel { showImagePicker = true case .removeAll: removeAllAttachments() + case .camera, .remove: + break } } @@ -208,13 +210,6 @@ extension FeedbackViewModel { } } -extension FeedbackViewModel { - enum ActionsOfSheet { - case gallery - case removeAll - } -} - extension FeedbackViewModel { enum FocusedField { case title, description diff --git a/Splito/UI/Home/Account/User Profile/UserProfileView.swift b/Splito/UI/Home/Account/User Profile/UserProfileView.swift index 8d23e5cf..baa2a6fc 100644 --- a/Splito/UI/Home/Account/User Profile/UserProfileView.swift +++ b/Splito/UI/Home/Account/User Profile/UserProfileView.swift @@ -99,7 +99,7 @@ private struct DecoratedProfileImageView: View { UserProfileImageView(image: $profileImage, profileImageUrl: profileImageUrl, size: (80, 80), showOverlay: true, handleProfileTap: handleProfileTap) .confirmationDialog("", isPresented: $showImagePickerOption, titleVisibility: .hidden) { - ImagePickerOptionsView(image: profileImage, imageUrl: profileImageUrl, handleActionSelection: handleActionSelection) + MediaPickerOptionsView(image: profileImage, imageUrl: profileImageUrl, handleActionSelection: handleActionSelection) } } } diff --git a/Splito/UI/Home/Account/User Profile/UserProfileViewModel.swift b/Splito/UI/Home/Account/User Profile/UserProfileViewModel.swift index b8d3e8e2..d053d5c9 100644 --- a/Splito/UI/Home/Account/User Profile/UserProfileViewModel.swift +++ b/Splito/UI/Home/Account/User Profile/UserProfileViewModel.swift @@ -107,6 +107,7 @@ public class UserProfileViewModel: BaseViewModel, ObservableObject { case .remove: profileImage = nil profileImageUrl = nil + case .removeAll: break } } diff --git a/Splito/UI/Home/Expense/AddExpenseView.swift b/Splito/UI/Home/Expense/AddExpenseView.swift index a36d8bb1..ba1cb47b 100644 --- a/Splito/UI/Home/Expense/AddExpenseView.swift +++ b/Splito/UI/Home/Expense/AddExpenseView.swift @@ -227,7 +227,7 @@ struct AddNoteImageFooterView: View { ImageAttachmentView(image: image, imageUrl: imageUrl, handleImageBtnTap: handleImageTap) .confirmationDialog("", isPresented: $showImagePickerOptions, titleVisibility: .hidden) { - ImagePickerOptionsView(image: image, imageUrl: imageUrl, handleActionSelection: handleActionSelection) + MediaPickerOptionsView(image: image, imageUrl: imageUrl, handleActionSelection: handleActionSelection) } NoteButtonView(isNoteEmpty: isNoteEmpty, handleNoteBtnTap: handleNoteBtnTap) diff --git a/Splito/UI/Home/Expense/AddExpenseViewModel.swift b/Splito/UI/Home/Expense/AddExpenseViewModel.swift index 28f7ac90..9272df34 100644 --- a/Splito/UI/Home/Expense/AddExpenseViewModel.swift +++ b/Splito/UI/Home/Expense/AddExpenseViewModel.swift @@ -213,10 +213,11 @@ extension AddExpenseViewModel { sourceTypeIsCamera = false showImagePicker = true case .remove: - withAnimation { - expenseImage = nil - expenseImageUrl = nil + withAnimation { [weak self] in + self?.expenseImage = nil + self?.expenseImageUrl = nil } + case .removeAll: break } } diff --git a/Splito/UI/Home/Groups/Create Group/CreateGroupView.swift b/Splito/UI/Home/Groups/Create Group/CreateGroupView.swift index 2e0d1ddc..9f9d9f8f 100644 --- a/Splito/UI/Home/Groups/Create Group/CreateGroupView.swift +++ b/Splito/UI/Home/Groups/Create Group/CreateGroupView.swift @@ -120,29 +120,7 @@ private struct AddGroupImageView: View { .padding(.horizontal, 16) .onTapGesture(perform: handleProfileTap) .confirmationDialog("", isPresented: $showImagePickerOptions, titleVisibility: .hidden) { - ImagePickerOptionsView(image: image, imageUrl: imageUrl, handleActionSelection: handleActionSelection) - } - } -} - -struct ImagePickerOptionsView: View { - - let image: UIImage? - let imageUrl: String? - - let handleActionSelection: (ActionsOfSheet) -> Void - - var body: some View { - Button("Take a picture") { - handleActionSelection(.camera) - } - Button("Choose from Library") { - handleActionSelection(.gallery) - } - if image != nil || (imageUrl != nil && !(imageUrl?.isEmpty ?? false)) { - Button("Remove", role: .destructive) { - handleActionSelection(.remove) - } + MediaPickerOptionsView(image: image, imageUrl: imageUrl, handleActionSelection: handleActionSelection) } } } diff --git a/Splito/UI/Home/Groups/Create Group/CreateGroupViewModel.swift b/Splito/UI/Home/Groups/Create Group/CreateGroupViewModel.swift index c389d3ce..623b5838 100644 --- a/Splito/UI/Home/Groups/Create Group/CreateGroupViewModel.swift +++ b/Splito/UI/Home/Groups/Create Group/CreateGroupViewModel.swift @@ -6,7 +6,6 @@ // import Data -import UIKit import BaseStyle import AVFoundation import FirebaseFirestore @@ -79,6 +78,7 @@ class CreateGroupViewModel: BaseViewModel, ObservableObject { case .remove: profileImage = nil profileImageUrl = nil + case .removeAll: break } } @@ -151,10 +151,3 @@ class CreateGroupViewModel: BaseViewModel, ObservableObject { return imageData } } - -// MARK: - Image Picker Action sheet -enum ActionsOfSheet { - case camera - case gallery - case remove -} diff --git a/Splito/UI/Home/Groups/Group/Group Options/Settle up/Payment/GroupPaymentViewModel.swift b/Splito/UI/Home/Groups/Group/Group Options/Settle up/Payment/GroupPaymentViewModel.swift index fff9c290..8c7253c0 100644 --- a/Splito/UI/Home/Groups/Group/Group Options/Settle up/Payment/GroupPaymentViewModel.swift +++ b/Splito/UI/Home/Groups/Group/Group Options/Settle up/Payment/GroupPaymentViewModel.swift @@ -175,6 +175,7 @@ class GroupPaymentViewModel: BaseViewModel, ObservableObject { self?.paymentImage = nil self?.paymentImageUrl = nil } + case .removeAll: break } }