From 9b4fd537596ff27d3207494aee4a9a2d5eb462f0 Mon Sep 17 00:00:00 2001 From: Sunghun Kim <81547954+kimsh153@users.noreply.github.com> Date: Sat, 13 Jul 2024 01:09:02 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=EC=9D=B4=EC=8A=88=EB=93=A4=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Intent/GSMAuthenticationFormIntent.swift | 16 +++- .../GSMAuthenticationFormIntentProtocol.swift | 6 +- .../Model/GSMAuthenticationFormModel.swift | 13 ++- .../GSMAuthenticationFormModelProtocol.swift | 6 ++ .../Components/FormBuilderAreaView.swift | 1 + .../Environment/OptionSelectPresenter.swift | 4 + .../Scene/GSMAuthenticationFormView.swift | 80 +++++++++++++------ 7 files changed, 96 insertions(+), 30 deletions(-) diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntent.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntent.swift index bf0c4112..ffdb20f7 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntent.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntent.swift @@ -83,7 +83,19 @@ final class GSMAuthenticationFormIntent: GSMAuthenticationFormIntentProtocol { } } - func saveButtonDidTap(state: any GSMAuthenticationFormStateProtocol) { + func saveButtonDidTap() { + model?.updateIsPresentedSubmitDialog(isPresentedSubmitDialog: true) + } + + func toastDismissed() { + model?.updateIsSubmitting(isSubmitting: false) + } + + func dialogDismissed() { + model?.updateIsPresentedSubmitDialog(isPresentedSubmitDialog: false) + } + + func submitButtonDidTap(state: any GSMAuthenticationFormStateProtocol) { Task { do { guard let authenticationEntity = state.authenticationEntity else { return } @@ -93,6 +105,8 @@ final class GSMAuthenticationFormIntent: GSMAuthenticationFormIntentProtocol { entity: authenticationEntity ) ) + dialogDismissed() + model?.updateIsSubmitting(isSubmitting: true) } } } diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntentProtocol.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntentProtocol.swift index e312b875..f51f46d0 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntentProtocol.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntentProtocol.swift @@ -3,7 +3,10 @@ import Foundation protocol GSMAuthenticationFormIntentProtocol { func viewOnAppear() func formOnAppear() - func saveButtonDidTap(state: any GSMAuthenticationFormStateProtocol) + func saveButtonDidTap() + func toastDismissed() + func dialogDismissed() + func submitButtonDidTap(state: any GSMAuthenticationFormStateProtocol) func appendField( area: Int, sectionIndex: Int, @@ -43,7 +46,6 @@ protocol GSMAuthenticationFormIntentProtocol { file: Data, fileName: String ) - func updateSelectField( area: Int, sectionIndex: Int, diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModel.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModel.swift index 9d31a1ae..a043f6d9 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModel.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModel.swift @@ -5,8 +5,10 @@ import FoundationUtil final class GSMAuthenticationFormModel: ObservableObject, GSMAuthenticationFormStateProtocol { @Published var uiModel: GSMAuthenticationFormUIModel = .init(areas: [], files: []) @Published var fieldContents: [GSMAuthenticationFormFieldModel] = [] - @Published var stateModel: GSMAuthenticationStateModel = .init(name: "", score: 0, markingBoardType: .notSubmitted) + @Published var stateModel: GSMAuthenticationStateModel = .init(name: "", score: 0, markingBoardType: .underReview) @Published var isLoading: Bool = false + @Published var isSubmitting: Bool = false + @Published var isPresentedSubmitDialog: Bool = false var authenticationEntity: AuthenticationFormEntity? var authenticationStateEntity: AuthenticationStateEntity? } @@ -101,10 +103,17 @@ extension GSMAuthenticationFormModel: GSMAuthenticationFormActionProtocol { uiModel.areas[area].sections[sectionIndex].groups.remove(at: groupIndex) } - func updateIsLoading(isLoading: Bool) { self.isLoading = isLoading } + + func updateIsSubmitting(isSubmitting: Bool) { + self.isSubmitting = isSubmitting + } + + func updateIsPresentedSubmitDialog(isPresentedSubmitDialog: Bool) { + self.isPresentedSubmitDialog = isPresentedSubmitDialog + } } private extension GSMAuthenticationFormModel { diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModelProtocol.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModelProtocol.swift index 53fb25e6..c9adc438 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModelProtocol.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModelProtocol.swift @@ -7,6 +7,8 @@ protocol GSMAuthenticationFormStateProtocol { var authenticationStateEntity: AuthenticationStateEntity? { get } var stateModel: GSMAuthenticationStateModel { get } var isLoading: Bool { get } + var isSubmitting: Bool { get } + var isPresentedSubmitDialog: Bool { get } } protocol GSMAuthenticationFormActionProtocol: AnyObject { @@ -68,4 +70,8 @@ protocol GSMAuthenticationFormActionProtocol: AnyObject { ) func updateIsLoading(isLoading: Bool) + + func updateIsSubmitting(isSubmitting: Bool) + + func updateIsPresentedSubmitDialog(isPresentedSubmitDialog: Bool) } diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/FormBuilderAreaView.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/FormBuilderAreaView.swift index 842397de..e64e22c5 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/FormBuilderAreaView.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/FormBuilderAreaView.swift @@ -231,6 +231,7 @@ struct FormBuilderAreaView: View { } ) ) + .keyboardType(.numberPad) } @ViewBuilder diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Environment/OptionSelectPresenter.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Environment/OptionSelectPresenter.swift index 5a0cf265..11c64127 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Environment/OptionSelectPresenter.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Environment/OptionSelectPresenter.swift @@ -43,6 +43,10 @@ internal final class OptionPickerPresenter { optionSelectSubject.send(option) isPresentedSubject.send(false) } + + func cancleSelectedOption() { + isPresentedSubject.send(false) + } } internal extension EnvironmentValues { diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/GSMAuthenticationFormView.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/GSMAuthenticationFormView.swift index 584b0983..441a119e 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/GSMAuthenticationFormView.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/GSMAuthenticationFormView.swift @@ -27,7 +27,8 @@ struct GSMAuthenticationFormView: View { case .notSubmitted: authenticationView() case .underReview: - underReview() + // underReview() + authenticationView() case .pendingReview: pendingView() case .completed: @@ -37,6 +38,7 @@ struct GSMAuthenticationFormView: View { .onAppear { intent.viewOnAppear() } + .hideKeyboardWhenTap() .navigationTitle("인증제") .smsBackButton( dismiss: dismiss @@ -44,16 +46,38 @@ struct GSMAuthenticationFormView: View { .smsBottomSheet( isShowing: Binding( get: { isPresented }, - set: { _ in } + set: { _ in optionPickerPresenter.cancleSelectedOption() } ) ) { valueListView() } .navigationBarTitleDisplayMode(.inline) + .edgesIgnoringSafeArea([.bottom]) + .ignoresSafeArea(.keyboard) .onAppear { subscribeToIsPresentedPublisher() } .smsLoading(isLoading: state.isLoading) + .smsDialog(title: "정말로 인증제를 제출하시겠습니까?", isShowing: Binding(get: { state.isPresentedSubmitDialog }, set: { _ in + intent.dialogDismissed() + }), dialogActions: [ + .init(text: "취소", style: .outline, action: { + intent.dialogDismissed() + }), + .init(text: "확인", style: .default, action: { + intent.submitButtonDidTap(state: state) + dismiss() + }) + ]) + .animation(.easeIn, value: state.isPresentedSubmitDialog) + .smsToast( + text: "인증제 제출이 완료되었습니다", + isShowing: Binding( + get: { state.isSubmitting }, + set: { _ in intent.toastDismissed() }) + ) { + SMSIcon(.greenCheck) + } } func subscribeToIsPresentedPublisher() { @@ -150,34 +174,40 @@ struct GSMAuthenticationFormView: View { @ViewBuilder func authenticationView() -> some View { - GSMAuthenticationFormBuilderView(intent: intent, uiModel: state.uiModel) { field in - switch field { - case let .fieldChanges(area, section, group, field, fieldChanges): - switch fieldChanges { - case let .text(text): - intent.updateTextField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, text: text) - case let .number(number): - intent.updateNumberField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, number: number) - case let .boolean(select): - intent.updateBoolField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, select: select) - case let .file(file, fileName): - intent.updateFileField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, file: file, fileName: fileName) - case let .select(select): - intent.updateSelectField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, select: select) + VStack { + Spacer() + .frame(height: 1) + GSMAuthenticationFormBuilderView(intent: intent, uiModel: state.uiModel) { field in + switch field { + case let .fieldChanges(area, section, group, field, fieldChanges): + switch fieldChanges { + case let .text(text): + intent.updateTextField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, text: text) + case let .number(number): + intent.updateNumberField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, number: number) + case let .boolean(select): + intent.updateBoolField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, select: select) + case let .file(file, fileName): + intent.updateFileField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, file: file, fileName: fileName) + case let .select(select): + intent.updateSelectField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, select: select) + } + case let .groupAdd(area, section, group): + intent.appendField(area: area, sectionIndex: section, groupIndex: group) + case let .groupRemove(area, section, group): + intent.deleteField(area: area, sectionIndex: section, groupIndex: group) } - case let .groupAdd(area, section, group): - intent.appendField(area: area, sectionIndex: section, groupIndex: group) - case let .groupRemove(area, section, group): - intent.deleteField(area: area, sectionIndex: section, groupIndex: group) } - } - .padding(.bottom, safeAreaInsets.bottom + 16) - .overlay(alignment: .bottom) { + CTAButton(text: "저장") { - intent.saveButtonDidTap(state: state) + intent.saveButtonDidTap() } - .frame(maxWidth: .infinity) .padding(.horizontal, 20) + .padding(.bottom, safeAreaInsets.bottom + 16) + .background { + Color.sms(.system(.white)) + } + .ignoresSafeArea() } .onAppear { intent.formOnAppear() From e4b167704edd305bab37a718c2ad85ffe25e9f15 Mon Sep 17 00:00:00 2001 From: baegteun Date: Sat, 13 Jul 2024 17:52:48 +0900 Subject: [PATCH 2/3] =?UTF-8?q?:bug:=20::=20[#348]=20=ED=82=A4=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=EC=9E=85=EB=A0=A5=20=EC=8B=9C=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=9D=B4=20=EB=B0=9C=EA=B4=91=ED=95=98=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Application/NeedleGenerated.swift | 56 +++---- .../Intent/GSMAuthenticationFormIntent.swift | 13 +- .../Model/GSMAuthenticationFormModel.swift | 22 +-- .../GSMAuthenticationFormModelProtocol.swift | 2 + .../Components/FormBuilderAreaListView.swift | 14 ++ .../GSMAuthenticationFileDownloadView.swift | 8 +- .../GSMAuthenticationFormBuilderView.swift | 51 ------ .../Scene/GSMAuthenticationFormView.swift | 153 ++++++++++++++---- 8 files changed, 191 insertions(+), 128 deletions(-) delete mode 100644 Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/GSMAuthenticationFormBuilderView.swift diff --git a/Projects/App/Sources/Application/NeedleGenerated.swift b/Projects/App/Sources/Application/NeedleGenerated.swift index 04504c8d..9ef8e0c0 100644 --- a/Projects/App/Sources/Application/NeedleGenerated.swift +++ b/Projects/App/Sources/Application/NeedleGenerated.swift @@ -474,33 +474,33 @@ extension JwtStoreComponent: Registration { extension AppComponent: Registration { public func registerItems() { - localTable["rootComponent-RootComponent"] = { self.rootComponent as Any } - localTable["signinBuildable-any SigninBuildable"] = { self.signinBuildable as Any } - localTable["inputInformationBuildable-any InputInformationBuildable"] = { self.inputInformationBuildable as Any } - localTable["inputProfileInfoBuildable-any InputProfileInfoBuildable"] = { self.inputProfileInfoBuildable as Any } - localTable["inputSchoolLifeInfoBuildable-any InputSchoolLifeInfoBuildable"] = { self.inputSchoolLifeInfoBuildable as Any } - localTable["inputWorkInfoBuildable-any InputWorkInfoBuildable"] = { self.inputWorkInfoBuildable as Any } - localTable["inputMilitaryInfoBuildable-any InputMilitaryInfoBuildable"] = { self.inputMilitaryInfoBuildable as Any } - localTable["inputCertificateInfoBuildable-any InputCertificateInfoBuildable"] = { self.inputCertificateInfoBuildable as Any } - localTable["inputLanguageInfoBuildable-any InputLanguageInfoBuildable"] = { self.inputLanguageInfoBuildable as Any } - localTable["inputPrizeInfoBuildable-any InputPrizeInfoBuildable"] = { self.inputPrizeInfoBuildable as Any } - localTable["inputProjectInfoBuildable-any InputProjectInfoBuildable"] = { self.inputProjectInfoBuildable as Any } - localTable["mainBuildable-any MainBuildable"] = { self.mainBuildable as Any } - localTable["myPageBuildable-any MyPageBuildable"] = { self.myPageBuildable as Any } - localTable["techStackAppendBuildable-any TechStackAppendBuildable"] = { self.techStackAppendBuildable as Any } - localTable["studentDetailBuildable-any StudentDetailBuildable"] = { self.studentDetailBuildable as Any } - localTable["filterBuildable-any FilterBuildable"] = { self.filterBuildable as Any } - localTable["splashBuildable-any SplashBuildable"] = { self.splashBuildable as Any } - localTable["gsmAuthenticationBuildable-any GSMAuthenticationBuildable"] = { self.gsmAuthenticationBuildable as Any } - localTable["authDomainBuildable-any AuthDomainBuildable"] = { self.authDomainBuildable as Any } - localTable["studentDomainBuildable-any StudentDomainBuildable"] = { self.studentDomainBuildable as Any } - localTable["majorDomainBuildable-any MajorDomainBuildable"] = { self.majorDomainBuildable as Any } - localTable["fileDomainBuildable-any FileDomainBuildable"] = { self.fileDomainBuildable as Any } - localTable["userDomainBuildable-any UserDomainBuildable"] = { self.userDomainBuildable as Any } - localTable["techStackDomainBuildable-any TechStackDomainBuildable"] = { self.techStackDomainBuildable as Any } - localTable["jwtStoreBuildable-any JwtStoreBuildable"] = { self.jwtStoreBuildable as Any } - localTable["keychainBuildable-any KeychainBuildable"] = { self.keychainBuildable as Any } - localTable["authenticationDomainBuildable-any AuthenticationDomainBuildable"] = { self.authenticationDomainBuildable as Any } + localTable["rootComponent-RootComponent"] = { [unowned self] in self.rootComponent as Any } + localTable["signinBuildable-any SigninBuildable"] = { [unowned self] in self.signinBuildable as Any } + localTable["inputInformationBuildable-any InputInformationBuildable"] = { [unowned self] in self.inputInformationBuildable as Any } + localTable["inputProfileInfoBuildable-any InputProfileInfoBuildable"] = { [unowned self] in self.inputProfileInfoBuildable as Any } + localTable["inputSchoolLifeInfoBuildable-any InputSchoolLifeInfoBuildable"] = { [unowned self] in self.inputSchoolLifeInfoBuildable as Any } + localTable["inputWorkInfoBuildable-any InputWorkInfoBuildable"] = { [unowned self] in self.inputWorkInfoBuildable as Any } + localTable["inputMilitaryInfoBuildable-any InputMilitaryInfoBuildable"] = { [unowned self] in self.inputMilitaryInfoBuildable as Any } + localTable["inputCertificateInfoBuildable-any InputCertificateInfoBuildable"] = { [unowned self] in self.inputCertificateInfoBuildable as Any } + localTable["inputLanguageInfoBuildable-any InputLanguageInfoBuildable"] = { [unowned self] in self.inputLanguageInfoBuildable as Any } + localTable["inputPrizeInfoBuildable-any InputPrizeInfoBuildable"] = { [unowned self] in self.inputPrizeInfoBuildable as Any } + localTable["inputProjectInfoBuildable-any InputProjectInfoBuildable"] = { [unowned self] in self.inputProjectInfoBuildable as Any } + localTable["mainBuildable-any MainBuildable"] = { [unowned self] in self.mainBuildable as Any } + localTable["myPageBuildable-any MyPageBuildable"] = { [unowned self] in self.myPageBuildable as Any } + localTable["techStackAppendBuildable-any TechStackAppendBuildable"] = { [unowned self] in self.techStackAppendBuildable as Any } + localTable["studentDetailBuildable-any StudentDetailBuildable"] = { [unowned self] in self.studentDetailBuildable as Any } + localTable["filterBuildable-any FilterBuildable"] = { [unowned self] in self.filterBuildable as Any } + localTable["splashBuildable-any SplashBuildable"] = { [unowned self] in self.splashBuildable as Any } + localTable["gsmAuthenticationBuildable-any GSMAuthenticationBuildable"] = { [unowned self] in self.gsmAuthenticationBuildable as Any } + localTable["authDomainBuildable-any AuthDomainBuildable"] = { [unowned self] in self.authDomainBuildable as Any } + localTable["studentDomainBuildable-any StudentDomainBuildable"] = { [unowned self] in self.studentDomainBuildable as Any } + localTable["majorDomainBuildable-any MajorDomainBuildable"] = { [unowned self] in self.majorDomainBuildable as Any } + localTable["fileDomainBuildable-any FileDomainBuildable"] = { [unowned self] in self.fileDomainBuildable as Any } + localTable["userDomainBuildable-any UserDomainBuildable"] = { [unowned self] in self.userDomainBuildable as Any } + localTable["techStackDomainBuildable-any TechStackDomainBuildable"] = { [unowned self] in self.techStackDomainBuildable as Any } + localTable["jwtStoreBuildable-any JwtStoreBuildable"] = { [unowned self] in self.jwtStoreBuildable as Any } + localTable["keychainBuildable-any KeychainBuildable"] = { [unowned self] in self.keychainBuildable as Any } + localTable["authenticationDomainBuildable-any AuthenticationDomainBuildable"] = { [unowned self] in self.authenticationDomainBuildable as Any } } } extension KeychainComponent: Registration { @@ -669,7 +669,7 @@ private func registerProviderFactory(_ componentPath: String, _ factory: @escapi #if !NEEDLE_DYNAMIC -private func register1() { +@inline(never) private func register1() { registerProviderFactory("^->AppComponent->JwtStoreComponent", factoryb27d5aae1eb7e73575a6f47b58f8f304c97af4d5) registerProviderFactory("^->AppComponent", factoryEmptyDependencyProvider) registerProviderFactory("^->AppComponent->KeychainComponent", factoryEmptyDependencyProvider) diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntent.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntent.swift index ffdb20f7..51a21434 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntent.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Intent/GSMAuthenticationFormIntent.swift @@ -34,13 +34,12 @@ final class GSMAuthenticationFormIntent: GSMAuthenticationFormIntentProtocol { model?.updateAuthenticationStateEntity(authenticationStateEntity: authenticationStateEntity) model?.updateAuthenticationStateModel( - stateModel: - .init( - name: authenticationStateEntity.name, - score: authenticationStateEntity.score, - grader: authenticationStateEntity.grader, - markingBoardType: authenticationStateEntity.markingBoardType - ) + stateModel: .init( + name: authenticationStateEntity.name, + score: authenticationStateEntity.score, + grader: authenticationStateEntity.grader, + markingBoardType: authenticationStateEntity.markingBoardType + ) ) if authenticationStateEntity.markingBoardType != .notSubmitted { model?.updateIsLoading(isLoading: false) diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModel.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModel.swift index a043f6d9..f9f43f2f 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModel.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModel.swift @@ -11,11 +11,15 @@ final class GSMAuthenticationFormModel: ObservableObject, GSMAuthenticationFormS @Published var isPresentedSubmitDialog: Bool = false var authenticationEntity: AuthenticationFormEntity? var authenticationStateEntity: AuthenticationStateEntity? + @Published var areas: [GSMAuthenticationFormUIModel.Area] = [] + @Published var files: [GSMAuthenticationFormUIModel.File] = [] } extension GSMAuthenticationFormModel: GSMAuthenticationFormActionProtocol { func updateGSMAuthenticationFormUIModel(uiModel: GSMAuthenticationFormUIModel) { self.uiModel = uiModel + self.areas = uiModel.areas + self.files = uiModel.files } func updateAuthenticationEntity(authenticationEntity: AuthenticationFormEntity) { @@ -63,7 +67,7 @@ extension GSMAuthenticationFormModel: GSMAuthenticationFormActionProtocol { ) } - uiModel.areas[area].sections[sectionIndex].groups.insert( + areas[area].sections[sectionIndex].groups.insert( .init( groupId: selectedGroup.groupId, maxScore: selectedGroup.maxScore, @@ -74,25 +78,25 @@ extension GSMAuthenticationFormModel: GSMAuthenticationFormActionProtocol { } func updateTextField(area: Int, sectionIndex: Int, groupIndex: Int, fieldIndex: Int, text: String) { - uiModel.areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = .text(value: text) + areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = .text(value: text) } func updateNumberField(area: Int, sectionIndex: Int, groupIndex: Int, fieldIndex: Int, number: Int) { - uiModel.areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = .number(value: number) + areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = .number(value: number) } func updateBoolField(area: Int, sectionIndex: Int, groupIndex: Int, fieldIndex: Int, select: String) { - uiModel.areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = - .boolean(selectedValue: select, values: uiModel.areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].findFieldTypeValue()) + areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = + .boolean(selectedValue: select, values: areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].findFieldTypeValue()) } func updateFileField(area: Int, sectionIndex: Int, groupIndex: Int, fieldIndex: Int, file: String) { - uiModel.areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = .file(fileName: file) + areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = .file(fileName: file) } func updateSelectField(area: Int, sectionIndex: Int, groupIndex: Int, fieldIndex: Int, select: String) { - uiModel.areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = - .select(selectedValue: select, values: uiModel.areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].findFieldTypeValue()) + areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].type = + .select(selectedValue: select, values: areas[area].sections[sectionIndex].groups[groupIndex].fields[fieldIndex].findFieldTypeValue()) } func deleteField( @@ -100,7 +104,7 @@ extension GSMAuthenticationFormModel: GSMAuthenticationFormActionProtocol { sectionIndex: Int, groupIndex: Int ) { - uiModel.areas[area].sections[sectionIndex].groups.remove(at: groupIndex) + areas[area].sections[sectionIndex].groups.remove(at: groupIndex) } func updateIsLoading(isLoading: Bool) { diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModelProtocol.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModelProtocol.swift index c9adc438..66d204c7 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModelProtocol.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Model/GSMAuthenticationFormModelProtocol.swift @@ -3,6 +3,8 @@ import Foundation protocol GSMAuthenticationFormStateProtocol { var uiModel: GSMAuthenticationFormUIModel { get } + var areas: [GSMAuthenticationFormUIModel.Area] { get } + var files: [GSMAuthenticationFormUIModel.File] { get } var authenticationEntity: AuthenticationFormEntity? { get } var authenticationStateEntity: AuthenticationStateEntity? { get } var stateModel: GSMAuthenticationStateModel { get } diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/FormBuilderAreaListView.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/FormBuilderAreaListView.swift index ae1784a2..2f5c52ae 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/FormBuilderAreaListView.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/FormBuilderAreaListView.swift @@ -2,6 +2,20 @@ import DesignSystem import SwiftUI import ViewUtil +enum FieldChanges { + case text(String) + case number(Int) + case boolean(String) + case file(Data, String) + case select(String) +} + +enum FieldInteraction { + case fieldChanges(area: Int, sectionIndex: Int, groupIndex: Int, fieldIndex: Int, fieldChanges: FieldChanges) + case groupAdd(area: Int, section: Int, group: Int) + case groupRemove(area: Int, section: Int, group: Int) +} + struct FormBuilderAreaListView: View { private typealias Area = GSMAuthenticationFormUIModel.Area private typealias Section = Area.Section diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/GSMAuthenticationFileDownloadView.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/GSMAuthenticationFileDownloadView.swift index c8bf6e94..c567cfa9 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/GSMAuthenticationFileDownloadView.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/GSMAuthenticationFileDownloadView.swift @@ -4,18 +4,18 @@ import SwiftUI struct GSMAuthenticationFileDownloadView: View { @Environment(\.openURL) var openURL + private let files: [GSMAuthenticationFormUIModel.File] @State private var isCollapsed = false - private let uiModel: [GSMAuthenticationFormUIModel.File] private typealias File = GSMAuthenticationFormUIModel.File init( - uiModel: [GSMAuthenticationFormUIModel.File] + files: [GSMAuthenticationFormUIModel.File] ) { - self.uiModel = uiModel + self.files = files } var body: some View { - fileAreaView(files: uiModel) + fileAreaView(files: files) } @ViewBuilder diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/GSMAuthenticationFormBuilderView.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/GSMAuthenticationFormBuilderView.swift deleted file mode 100644 index fe65636e..00000000 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/Components/GSMAuthenticationFormBuilderView.swift +++ /dev/null @@ -1,51 +0,0 @@ -import DesignSystem -import Foundation -import SwiftUI -import BaseFeature -import ViewUtil - -enum FieldChanges { - case text(String) - case number(Int) - case boolean(String) - case file(Data, String) - case select(String) -} - -enum FieldInteraction { - case fieldChanges(area: Int, sectionIndex: Int, groupIndex: Int, fieldIndex: Int, fieldChanges: FieldChanges) - case groupAdd(area: Int, section: Int, group: Int) - case groupRemove(area: Int, section: Int, group: Int) -} - -struct GSMAuthenticationFormBuilderView: View { - @Environment(\.fileFieldPresenter) var fileFieldPresenter - @Environment(\.optionPickerPresenter) var optionPickerPresenter - private let uiModel: GSMAuthenticationFormUIModel - private let onFieldInteraction: (FieldInteraction) -> Void - private typealias Area = GSMAuthenticationFormUIModel.Area - private typealias Section = Area.Section - private typealias Group = Section.Group - private typealias Field = Group.Field - let intent: GSMAuthenticationFormIntentProtocol - - init( - intent: GSMAuthenticationFormIntentProtocol, - uiModel: GSMAuthenticationFormUIModel, - onFieldInteraction: @escaping (FieldInteraction) -> Void - ) { - self.intent = intent - self.uiModel = uiModel - self.onFieldInteraction = onFieldInteraction - } - - var body: some View { - ScrollView { - GSMAuthenticationFileDownloadView(uiModel: uiModel.files) - - FormBuilderAreaListView(areas: uiModel.areas) { fieldInteraction in - onFieldInteraction(fieldInteraction) - } - } - } -} diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/GSMAuthenticationFormView.swift b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/GSMAuthenticationFormView.swift index 441a119e..78abe1ce 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/GSMAuthenticationFormView.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Sources/Scene/GSMAuthenticationFormView.swift @@ -22,18 +22,78 @@ struct GSMAuthenticationFormView: View { } var body: some View { - ZStack { - switch state.stateModel.markingBoardType { - case .notSubmitted: - authenticationView() - case .underReview: - // underReview() - authenticationView() - case .pendingReview: - pendingView() - case .completed: - completed() + ScrollView { + GSMAuthenticationFileDownloadView(files: state.files) + + FormBuilderAreaListView(areas: state.areas) { fieldInteraction in + switch fieldInteraction { + case let .fieldChanges(area, section, group, field, fieldChanges): + switch fieldChanges { + case let .text(text): + intent.updateTextField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + text: text + ) + case let .number(number): + intent.updateNumberField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + number: number + ) + case let .boolean(select): + intent.updateBoolField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + select: select + ) + case let .file(file, fileName): + intent.updateFileField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + file: file, + fileName: fileName + ) + case let .select(select): + intent.updateSelectField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + select: select + ) + } + case let .groupAdd(area, section, group): + intent.appendField(area: area, sectionIndex: section, groupIndex: group) + case let .groupRemove(area, section, group): + intent.deleteField(area: area, sectionIndex: section, groupIndex: group) + } + } + + Spacer() + .frame(height: 120) + } + .overlay(alignment: .bottom) { + CTAButton(text: "저장") { + intent.saveButtonDidTap() } + .padding(.horizontal, 20) + .padding(.bottom, safeAreaInsets.bottom + 16) + .background { + Color.sms(.system(.white)) + } + .ignoresSafeArea() + } + .onAppear { + intent.formOnAppear() } .onAppear { intent.viewOnAppear() @@ -177,25 +237,60 @@ struct GSMAuthenticationFormView: View { VStack { Spacer() .frame(height: 1) - GSMAuthenticationFormBuilderView(intent: intent, uiModel: state.uiModel) { field in - switch field { - case let .fieldChanges(area, section, group, field, fieldChanges): - switch fieldChanges { - case let .text(text): - intent.updateTextField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, text: text) - case let .number(number): - intent.updateNumberField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, number: number) - case let .boolean(select): - intent.updateBoolField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, select: select) - case let .file(file, fileName): - intent.updateFileField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, file: file, fileName: fileName) - case let .select(select): - intent.updateSelectField(area: area, sectionIndex: section, groupIndex: group, fieldIndex: field, select: select) + ScrollView { + GSMAuthenticationFileDownloadView(files: state.files) + + FormBuilderAreaListView(areas: state.areas) { fieldInteraction in + switch fieldInteraction { + case let .fieldChanges(area, section, group, field, fieldChanges): + switch fieldChanges { + case let .text(text): + intent.updateTextField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + text: text + ) + case let .number(number): + intent.updateNumberField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + number: number + ) + case let .boolean(select): + intent.updateBoolField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + select: select + ) + case let .file(file, fileName): + intent.updateFileField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + file: file, + fileName: fileName + ) + case let .select(select): + intent.updateSelectField( + area: area, + sectionIndex: section, + groupIndex: group, + fieldIndex: field, + select: select + ) + } + case let .groupAdd(area, section, group): + intent.appendField(area: area, sectionIndex: section, groupIndex: group) + case let .groupRemove(area, section, group): + intent.deleteField(area: area, sectionIndex: section, groupIndex: group) } - case let .groupAdd(area, section, group): - intent.appendField(area: area, sectionIndex: section, groupIndex: group) - case let .groupRemove(area, section, group): - intent.deleteField(area: area, sectionIndex: section, groupIndex: group) } } From 7c18fbe994f6abffb50aa46e5a4795dd675a7977 Mon Sep 17 00:00:00 2001 From: baegteun Date: Sat, 13 Jul 2024 20:41:49 +0900 Subject: [PATCH 3/3] :bug: :: [#348] Demo error --- .../Demo/Sources/AppDelegate.swift | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/Projects/Feature/GSMAuthenticationFormFeature/Demo/Sources/AppDelegate.swift b/Projects/Feature/GSMAuthenticationFormFeature/Demo/Sources/AppDelegate.swift index 42ca07e7..072442a5 100644 --- a/Projects/Feature/GSMAuthenticationFormFeature/Demo/Sources/AppDelegate.swift +++ b/Projects/Feature/GSMAuthenticationFormFeature/Demo/Sources/AppDelegate.swift @@ -5,11 +5,9 @@ import FileDomainTesting @main struct GSMAuthenticationFormDemoApp: App { - let make: DemoMakable = DemoMakable() - var body: some Scene { WindowGroup { - let uiModel = GSMAuthenticationFormUIModel( + FormBuilderAreaListView( areas: [ GSMAuthenticationFormUIModel.Area( title: "Area 1", @@ -66,29 +64,8 @@ struct GSMAuthenticationFormDemoApp: App { ) ] ) - ], - files: [ - GSMAuthenticationFormUIModel.File(name: "File 1", url: "http://example.com/file1"), - GSMAuthenticationFormUIModel.File(name: "File 2", url: "http://example.com/file2") ] - ) - - make.makeView(uiModel: uiModel) - } - } -} - -final class DemoMakable { - func makeView(uiModel: GSMAuthenticationFormUIModel) -> GSMAuthenticationFormBuilderView { - return GSMAuthenticationFormBuilderView( - intent: GSMAuthenticationFormIntent( - model: GSMAuthenticationFormModel(), - fetchAuthenticationFormUseCase: FetchAuthenticationFormUseCaseSpy(), - inputAuthenticationUseCase: InputAuthenticationUseCaseSpy(), - fileUploadUseCase: FileUploadUseCaseSpy(), fetchAuthenticationStateUseCase: FetchAuthenticationStateUseCaseSpy() - ), - uiModel: uiModel - ) { _ in + ) { _ in } } } }