From bccb4eb999a74a2503f56f49bb5d0afdd3fdf19a Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Wed, 23 Oct 2024 00:05:27 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[Add]=20#220=20-=20View+=20UIScreen=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=B3=80=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 12 ++++++++++ .../Global/Extensions/SwiftUI/View+.swift | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index bb1fd923..ecfac5cb 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 3728B8D52A2CFF2C00EF4CF8 /* StepOneKoreanDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3728B8D42A2CFF2C00EF4CF8 /* StepOneKoreanDiaryViewController.swift */; }; 3728B8D72A2CFF3A00EF4CF8 /* StepTwoKoreanDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3728B8D62A2CFF3A00EF4CF8 /* StepTwoKoreanDiaryViewController.swift */; }; 373A7F552C3D78CC00CF554C /* CustomBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A7F542C3D78CC00CF554C /* CustomBannerView.swift */; }; + 373D29932CBFE17B00A559A3 /* View+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D29922CBFE17B00A559A3 /* View+.swift */; }; 373E38592A2F889500DF96F0 /* DiaryDetailRandomSubjectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373E38582A2F889500DF96F0 /* DiaryDetailRandomSubjectView.swift */; }; 374F828C2AC327A200C128B9 /* SmeemTextViewHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374F828B2AC327A200C128B9 /* SmeemTextViewHandler.swift */; }; 374F828E2AC5DFFE00C128B9 /* DiaryBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374F828D2AC5DFFE00C128B9 /* DiaryBottomView.swift */; }; @@ -267,6 +268,7 @@ 3728B8D42A2CFF2C00EF4CF8 /* StepOneKoreanDiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepOneKoreanDiaryViewController.swift; sourceTree = ""; }; 3728B8D62A2CFF3A00EF4CF8 /* StepTwoKoreanDiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepTwoKoreanDiaryViewController.swift; sourceTree = ""; }; 373A7F542C3D78CC00CF554C /* CustomBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBannerView.swift; sourceTree = ""; }; + 373D29922CBFE17B00A559A3 /* View+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+.swift"; sourceTree = ""; }; 373E38582A2F889500DF96F0 /* DiaryDetailRandomSubjectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryDetailRandomSubjectView.swift; sourceTree = ""; }; 37499E3C2BD0C3A100BA6FAF /* SharedDiaryDataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDiaryDataService.swift; sourceTree = ""; }; 374F828B2AC327A200C128B9 /* SmeemTextViewHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmeemTextViewHandler.swift; sourceTree = ""; }; @@ -556,6 +558,14 @@ path = EditGoal; sourceTree = ""; }; + 373D29912CBFE16B00A559A3 /* SwiftUI */ = { + isa = PBXGroup; + children = ( + 373D29922CBFE17B00A559A3 /* View+.swift */, + ); + path = SwiftUI; + sourceTree = ""; + }; 378E46182B5516A900D2A473 /* DeepL */ = { isa = PBXGroup; children = ( @@ -625,6 +635,7 @@ 37A574B629FE207C00312453 /* Extensions */ = { isa = PBXGroup; children = ( + 373D29912CBFE16B00A559A3 /* SwiftUI */, 37A574C729FF6F1C00312453 /* UIView+.swift */, 37A574C929FF980F00312453 /* UIStackView+.swift */, 3761116B2A278D0E0095EC5A /* String+.swift */, @@ -2050,6 +2061,7 @@ 4AC7058F2BEA1E40003C5310 /* NicknameContainerView.swift in Sources */, 4ABCBCDA2BE22733003138A8 /* MyPlanCollectionViewLayout.swift in Sources */, 3728B8D32A2CFED300EF4CF8 /* ForeignDiaryViewController.swift in Sources */, + 373D29932CBFE17B00A559A3 /* View+.swift in Sources */, 4ABE3FD12A811F8400D1D22E /* SplashViewController.swift in Sources */, 4A9FAB492A4C3D1200C40D5A /* DetailBadgeCollectionViewCell.swift in Sources */, 4ABCBCD02BDE9BA1003138A8 /* MySummaryViewModel.swift in Sources */, diff --git a/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift b/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift new file mode 100644 index 00000000..70f0b5ba --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift @@ -0,0 +1,22 @@ +// +// View+.swift +// Smeem-iOS +// +// Created by Joon Baek on 2024/10/16. +// + +import SwiftUI + +extension View { + var screenSize: CGRect { + return UIScreen.main.bounds + } + + var screenHeight: Double { + return screenSize.height + } + + var screenWidth: Double { + return screenSize.width + } +} From 520b76f5d553137f1f4b8cee6084689775c839ec Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Wed, 23 Oct 2024 00:06:08 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[Feat]=20#220=20-=20=EC=BD=94=EC=B9=AD?= =?UTF-8?q?=EB=B7=B0=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 12 +++ .../UIComponents/CoachingCompletedView.swift | 46 ++++++++++++ .../UIComponents/CustomSegmentedControl.swift | 74 +++++++++++++++++++ .../UIComponents/SwiftUINavigationView.swift | 40 ++++++++++ 4 files changed, 172 insertions(+) create mode 100644 Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift create mode 100644 Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift create mode 100644 Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index ecfac5cb..40972201 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 3728B8D72A2CFF3A00EF4CF8 /* StepTwoKoreanDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3728B8D62A2CFF3A00EF4CF8 /* StepTwoKoreanDiaryViewController.swift */; }; 373A7F552C3D78CC00CF554C /* CustomBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A7F542C3D78CC00CF554C /* CustomBannerView.swift */; }; 373D29932CBFE17B00A559A3 /* View+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D29922CBFE17B00A559A3 /* View+.swift */; }; + 373D29962CC2A84E00A559A3 /* SwiftUINavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D29952CC2A84E00A559A3 /* SwiftUINavigationView.swift */; }; + 373D29982CC7A53C00A559A3 /* CustomSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D29972CC7A53C00A559A3 /* CustomSegmentedControl.swift */; }; 373E38592A2F889500DF96F0 /* DiaryDetailRandomSubjectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373E38582A2F889500DF96F0 /* DiaryDetailRandomSubjectView.swift */; }; 374F828C2AC327A200C128B9 /* SmeemTextViewHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374F828B2AC327A200C128B9 /* SmeemTextViewHandler.swift */; }; 374F828E2AC5DFFE00C128B9 /* DiaryBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374F828D2AC5DFFE00C128B9 /* DiaryBottomView.swift */; }; @@ -64,6 +66,7 @@ 37A574E82A02424F00312453 /* RandomTopicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A574E72A02424F00312453 /* RandomTopicView.swift */; }; 37ADCC022B0289AD00E474AA /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37ADCC012B0289AD00E474AA /* Observable.swift */; }; 37B360AA2AC2A4680006C8ED /* SmeemTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B360A92AC2A4680006C8ED /* SmeemTextView.swift */; }; + 37BB09BB2CBF8BA800A3B28A /* CoachingCompletedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */; }; 37BCADF22BC3FFBF006EF960 /* DetailDiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BCADF12BC3FFBF006EF960 /* DetailDiaryViewModel.swift */; }; 37BDC9872C4FEA940075F68A /* SendFeedbackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BDC9862C4FEA940075F68A /* SendFeedbackView.swift */; }; 37DCA6572A47574300FF8F90 /* RandomTopicAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6562A47574300FF8F90 /* RandomTopicAPI.swift */; }; @@ -269,6 +272,8 @@ 3728B8D62A2CFF3A00EF4CF8 /* StepTwoKoreanDiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepTwoKoreanDiaryViewController.swift; sourceTree = ""; }; 373A7F542C3D78CC00CF554C /* CustomBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBannerView.swift; sourceTree = ""; }; 373D29922CBFE17B00A559A3 /* View+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+.swift"; sourceTree = ""; }; + 373D29952CC2A84E00A559A3 /* SwiftUINavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUINavigationView.swift; sourceTree = ""; }; + 373D29972CC7A53C00A559A3 /* CustomSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSegmentedControl.swift; sourceTree = ""; }; 373E38582A2F889500DF96F0 /* DiaryDetailRandomSubjectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryDetailRandomSubjectView.swift; sourceTree = ""; }; 37499E3C2BD0C3A100BA6FAF /* SharedDiaryDataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDiaryDataService.swift; sourceTree = ""; }; 374F828B2AC327A200C128B9 /* SmeemTextViewHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmeemTextViewHandler.swift; sourceTree = ""; }; @@ -303,6 +308,7 @@ 37A574E72A02424F00312453 /* RandomTopicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomTopicView.swift; sourceTree = ""; }; 37ADCC012B0289AD00E474AA /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; 37B360A92AC2A4680006C8ED /* SmeemTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmeemTextView.swift; sourceTree = ""; }; + 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingCompletedView.swift; sourceTree = ""; }; 37BCADF12BC3FFBF006EF960 /* DetailDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryViewModel.swift; sourceTree = ""; }; 37BDC9862C4FEA940075F68A /* SendFeedbackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendFeedbackView.swift; sourceTree = ""; }; 37DCA6562A47574300FF8F90 /* RandomTopicAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTopicAPI.swift; sourceTree = ""; }; @@ -663,6 +669,9 @@ 4A1EE9532A4DFB42007BFEF3 /* Diary */, 4A3F2E952A0ABB9C00F6AC60 /* Alarm */, 373A7F542C3D78CC00CF554C /* CustomBannerView.swift */, + 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */, + 373D29952CC2A84E00A559A3 /* SwiftUINavigationView.swift */, + 373D29972CC7A53C00A559A3 /* CustomSegmentedControl.swift */, ); path = UIComponents; sourceTree = ""; @@ -1992,6 +2001,7 @@ 4AB7C9192B75F9B500845733 /* GesturePublisher+.swift in Sources */, 6F294A462A2617D500856CC8 /* TrainingWayView.swift in Sources */, 37A574CC29FF990E00312453 /* UIViewController+.swift in Sources */, + 373D29982CC7A53C00A559A3 /* CustomSegmentedControl.swift in Sources */, 4ABCBCEA2BE26079003138A8 /* TrainingPlanViewModel.swift in Sources */, 4A9E10E42A43468600295D07 /* BaseTargetType.swift in Sources */, 4A4FEB092B728144001BBDF3 /* TrainingWayViewModel.swift in Sources */, @@ -2069,6 +2079,7 @@ 4AFD7C292B600FED00FE7EFF /* NetworkManager.swift in Sources */, 37DCA6652A48052200FF8F90 /* DetailDiaryResponse.swift in Sources */, 4ABCBCD82BE2230F003138A8 /* MyPlanCollectionViewDataSource.swift in Sources */, + 37BB09BB2CBF8BA800A3B28A /* CoachingCompletedView.swift in Sources */, 4AC4B82D2A2F9FE200E147AA /* UserDefaultsManager.swift in Sources */, 37A574E02A00CDCD00312453 /* SeparationLine.swift in Sources */, 4ABCBCDC2BE2484B003138A8 /* MyPlanAppData.swift in Sources */, @@ -2127,6 +2138,7 @@ 3797891C2AAE11D400C61EF4 /* NavigationBarConfigurationBuilder.swift in Sources */, 4A3373F82A461ABC00EFE6C4 /* OnboardingService.swift in Sources */, 4A9731D72BAAB6E100DEC0C8 /* SplashServiceProtocol.swift in Sources */, + 373D29962CC2A84E00A559A3 /* SwiftUINavigationView.swift in Sources */, 4AA5E4B42BF2355300F308C8 /* MySummaryViewModel2.swift in Sources */, 37A574E82A02424F00312453 /* RandomTopicView.swift in Sources */, 4ABCBCCA2BDD3E3D003138A8 /* MySummaryResponse.swift in Sources */, diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift new file mode 100644 index 00000000..34f06813 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift @@ -0,0 +1,46 @@ +// +// CoachingCompletedView.swift +// Smeem-iOS +// +// Created by Joon Baek on 2024/10/16. +// + +import SwiftUI + +struct CoachingCompletedView: View { + var body: some View { + + SwiftUINavigationView() + + VStack() { + Text(MockData.headerText) + .font(Font.custom("Pretendard", size: 16)) + // Colors 상수 등록 필요 + .foregroundColor(Color(UIColor.smeemBlack)) + .frame(maxWidth: .infinity, alignment: .leading) + + ScrollView { + Text(MockData.diaryEntry) + .font(Font.custom("Pretendard", size: 16)) + .foregroundColor(Color(UIColor.gray400)) + .lineSpacing(0.375) + } + .frame(height: screenHeight * 0.32) + + Spacer() + } + .padding(.horizontal, screenWidth * 0.048) + } +} + +extension CoachingCompletedView { + struct MockData { + static let headerText = "일기를 잘 작성하셨어요! \n내용이 명확하고 흥미로웠습니다.\n이제 몇 가지 문법적 오류를 수정해 볼까요?" + static let diaryEntry = "I watched Avatar with my boyfriend at Hongdae CGV. I should have skimmed the previous season 
what they were saying and the universe(??). What I was annoyed then was 두팔 didn’t know that as me. I think 두팔 who is my boyfriend should study before wathcing…. but Avatar2 is amazing movie I think. In my personal opinion, the jjin main character of Avatar2 is not Sully, but his son.the jjin main character of Avatar2 is not Sully, but his son.the jjin main character of Avatar2 is not Sully, but his son.the jjin main character of Avatar2 is not Sully, but his son.character of Avatar2 is not Sully, but his son.ly, but his ly, but his ly, but 여기부터 스크롤영역이라 내려가면 글자가 계속 나올거에욤ㄴㅇㄹ리ㅏㄴㅇ리ㅏㅓㄴ이ㅏ러민아ㅓ리ㅏㄴㅁ어리" + } +} + +@available (iOS 17, *) +#Preview { + CoachingCompletedView() +} diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift new file mode 100644 index 00000000..e35f94af --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift @@ -0,0 +1,74 @@ +// +// CustomSegmentedControl.swift +// Smeem-iOS +// +// Created by Joon Baek on 2024/10/22. +// + +import SwiftUI + +struct CustomSegmentedControl: View { + @Binding var selectedIndex: Int + let options: [String] + + var body: some View { + HStack(spacing: 0) { + ForEach(options.indices, id: \.self) { index in + SegmentButton( + title: options[index], + isSelected: selectedIndex == index, + action: { selectedIndex = index } + ) + } + } + .background(Color.gray.opacity(0.2)) + .cornerRadius(8) + } +} + +struct SegmentButton: View { + let title: String + let isSelected: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + Text(title) + .padding(.vertical, 8) + .padding(.horizontal, 16) + .frame(maxWidth: .infinity) + .background(backgroundColor) + .foregroundColor(foregroundColor) + .font(Font(UIFont.c5)) + } + } + + private var backgroundColor: Color { + isSelected ? (isCoachingOn ? Color(UIColor.point) : Color(UIColor.gray200)) : Color(UIColor.gray100) + } + + private var foregroundColor: Color { + isSelected ? .white : Color(UIColor.gray500) + } + + private var isCoachingOn: Bool { + title == "코칭 ON" + } +} + +struct PreviewWrapper: View { + @State private var selectedIndex = 0 + let options = ["코칭 OFF", "코칭 ON"] + + var body: some View { + CustomSegmentedControl(selectedIndex: $selectedIndex, options: options) + .frame(height: 40) + .padding(117) + } +} + +@available(iOS 17.0, *) +#Preview { + PreviewWrapper() +} + diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift new file mode 100644 index 00000000..fa95556b --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift @@ -0,0 +1,40 @@ +// +// SwiftUINavigationView.swift +// Smeem-iOS +// +// Created by Joon Baek on 2024/10/18. +// + +import SwiftUI + +struct SwiftUINavigationView: View { + @State private var selectedIndex = 0 + let options = ["코칭 OFF", "코칭 ON"] + + var body: some View { + HStack() { + Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, + label: { + Image("icnBack") + .imageScale(.large) + }) + .padding(.leading, 10) + + CustomSegmentedControl(selectedIndex: $selectedIndex, options: options) + .frame(height: 32) + .padding(65) + + Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, + label: { + Text("닫기") + .tint(.black) + }) + .padding(.trailing, 18) + } + .frame(height: 66) + } +} + +#Preview { + SwiftUINavigationView() +} From 369ab577c14204888c29412dee3cc79bd57c1863 Mon Sep 17 00:00:00 2001 From: cchanmi <113524@naver.com> Date: Sun, 17 Nov 2024 17:04:53 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[FEAT]=20#220=20-=20=EC=BD=94=EC=B9=AD?= =?UTF-8?q?=EB=B7=B0=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 12 ++++ .../UIComponents/CoachingComparisonView.swift | 71 +++++++++++++++++++ .../UIComponents/CoachingCompletedView.swift | 3 +- .../CoachingExplanationView.swift | 36 ++++++++++ .../UIComponents/CustomSegmentedControl.swift | 2 +- .../UIComponents/SwiftUINavigationView.swift | 8 ++- .../Smeem-iOS/Presentation/CoachingView.swift | 44 ++++++++++++ 7 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift create mode 100644 Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/CoachingView.swift diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index 40972201..a3aae256 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -90,6 +90,7 @@ 4A004D5E2B4EE2B4003C8936 /* MyPageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A004D5D2B4EE2B4003C8936 /* MyPageService.swift */; }; 4A004D602B4EE2BD003C8936 /* MyPageAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A004D5F2B4EE2BD003C8936 /* MyPageAPI.swift */; }; 4A01235F2AAA2C1500E09646 /* SmeemLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A01235E2AAA2C1500E09646 /* SmeemLoadingView.swift */; }; + 4A01A2D72CE8D2E400A9B0AA /* CoachingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A01A2D62CE8D2E400A9B0AA /* CoachingView.swift */; }; 4A07F8A82B7B3E5E004185F2 /* SignupBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07F8A72B7B3E5E004185F2 /* SignupBottomSheetViewController.swift */; }; 4A07F8AC2B7B3E7F004185F2 /* SignupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07F8AB2B7B3E7F004185F2 /* SignupViewModel.swift */; }; 4A07F8AF2B7B58B7004185F2 /* AuthModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07F8AE2B7B58B7004185F2 /* AuthModel.swift */; }; @@ -104,6 +105,7 @@ 4A0EE6042B9301E900158899 /* OnboardingServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0EE6032B9301E900158899 /* OnboardingServiceProtocol.swift */; }; 4A10A3A82AB8AA5800C145E9 /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A10A3A72AB8AA5800C145E9 /* System.swift */; }; 4A1882FA2A8BBD0D0088F590 /* SmeemStartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD923E62A0650B600FF5E27 /* SmeemStartViewController.swift */; }; + 4A1F2AE72CD4A1740055587F /* CoachingComparisonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A1F2AE62CD4A15B0055587F /* CoachingComparisonView.swift */; }; 4A2AEE702C3D78E70000CBCB /* ResignSummaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2AEE6F2C3D78E70000CBCB /* ResignSummaryViewModel.swift */; }; 4A3373F52A45BF6600EFE6C4 /* OnboardingEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3373F42A45BF6600EFE6C4 /* OnboardingEndPoint.swift */; }; 4A3373F82A461ABC00EFE6C4 /* OnboardingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3373F72A461ABC00EFE6C4 /* OnboardingService.swift */; }; @@ -111,6 +113,7 @@ 4A3F2E972A0ABBAA00F6AC60 /* AlarmCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3F2E962A0ABBAA00F6AC60 /* AlarmCollectionViewCell.swift */; }; 4A3F2E9C2A0B4C7100F6AC60 /* AlarmCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3F2E9B2A0B4C7100F6AC60 /* AlarmCollectionView.swift */; }; 4A3F2EA02A0C18B600F6AC60 /* DatePickerFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3F2E9F2A0C18B600F6AC60 /* DatePickerFooterView.swift */; }; + 4A4050EC2CE8C54400E84EDE /* CoachingExplanationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4050EB2CE8C54400E84EDE /* CoachingExplanationView.swift */; }; 4A454A832B6666FF0079D48D /* TrainingGoalViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A454A822B6666FF0079D48D /* TrainingGoalViewModel.swift */; }; 4A454A852B6667180079D48D /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A454A842B6667180079D48D /* ViewModel.swift */; }; 4A454A882B691F850079D48D /* EditGoalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A454A872B691F850079D48D /* EditGoalViewController.swift */; }; @@ -332,6 +335,7 @@ 4A004D5D2B4EE2B4003C8936 /* MyPageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageService.swift; sourceTree = ""; }; 4A004D5F2B4EE2BD003C8936 /* MyPageAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageAPI.swift; sourceTree = ""; }; 4A01235E2AAA2C1500E09646 /* SmeemLoadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmeemLoadingView.swift; sourceTree = ""; }; + 4A01A2D62CE8D2E400A9B0AA /* CoachingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingView.swift; sourceTree = ""; }; 4A07F8A72B7B3E5E004185F2 /* SignupBottomSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupBottomSheetViewController.swift; sourceTree = ""; }; 4A07F8AB2B7B3E7F004185F2 /* SignupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupViewModel.swift; sourceTree = ""; }; 4A07F8AE2B7B58B7004185F2 /* AuthModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthModel.swift; sourceTree = ""; }; @@ -347,6 +351,7 @@ 4A0EE6032B9301E900158899 /* OnboardingServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingServiceProtocol.swift; sourceTree = ""; }; 4A10A3A72AB8AA5800C145E9 /* System.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = System.swift; sourceTree = ""; }; 4A13B7582A13D37000F4FB1E /* UserNickNameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNickNameViewController.swift; sourceTree = ""; }; + 4A1F2AE62CD4A15B0055587F /* CoachingComparisonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingComparisonView.swift; sourceTree = ""; }; 4A23621B2AFA8682005E4C58 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 4A23621C2AFA868D005E4C58 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 4A2AEE6F2C3D78E70000CBCB /* ResignSummaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResignSummaryViewModel.swift; sourceTree = ""; }; @@ -356,6 +361,7 @@ 4A3F2E962A0ABBAA00F6AC60 /* AlarmCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmCollectionViewCell.swift; sourceTree = ""; }; 4A3F2E9B2A0B4C7100F6AC60 /* AlarmCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmCollectionView.swift; sourceTree = ""; }; 4A3F2E9F2A0C18B600F6AC60 /* DatePickerFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerFooterView.swift; sourceTree = ""; }; + 4A4050EB2CE8C54400E84EDE /* CoachingExplanationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingExplanationView.swift; sourceTree = ""; }; 4A454A822B6666FF0079D48D /* TrainingGoalViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingGoalViewModel.swift; sourceTree = ""; }; 4A454A842B6667180079D48D /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 4A454A872B691F850079D48D /* EditGoalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditGoalViewController.swift; sourceTree = ""; }; @@ -618,6 +624,7 @@ 37A574B529FE204500312453 /* Presentation */ = { isa = PBXGroup; children = ( + 4A01A2D62CE8D2E400A9B0AA /* CoachingView.swift */, 4AC047232A8FCA9700EBDC0E /* AuthManagement */, 4AA6FEE72C3ABD7C00E588E9 /* ResignSummray */, 4AA5E4B52BF24CEB00F308C8 /* BadgeBottomSheet */, @@ -672,6 +679,8 @@ 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */, 373D29952CC2A84E00A559A3 /* SwiftUINavigationView.swift */, 373D29972CC7A53C00A559A3 /* CustomSegmentedControl.swift */, + 4A4050EB2CE8C54400E84EDE /* CoachingExplanationView.swift */, + 4A1F2AE62CD4A15B0055587F /* CoachingComparisonView.swift */, ); path = UIComponents; sourceTree = ""; @@ -1995,6 +2004,7 @@ 4AC00EF32AD56C4C009230DA /* SmeemTextButton.swift in Sources */, 37DCA65D2A475B5100FF8F90 /* RandomTopicResponse.swift in Sources */, 4ABCBCCC2BDE8E44003138A8 /* MySummaryService.swift in Sources */, + 4A01A2D72CE8D2E400A9B0AA /* CoachingView.swift in Sources */, 371107DA2AC99099007A4AC2 /* KeyboardFollowingLayoutHandler.swift in Sources */, 4A9FAB452A4C3D1200C40D5A /* BadgeListViewController.swift in Sources */, 4ABCBCE52BE25D82003138A8 /* TrainingPlanViewController.swift in Sources */, @@ -2018,6 +2028,7 @@ 6FE1298C2A498338005536C4 /* HomeService.swift in Sources */, 4AD04B3D2A190E9A004B7A58 /* UITextView+.swift in Sources */, 4ABCBCC72BDD3983003138A8 /* MySummaryServiceProtocol.swift in Sources */, + 4A1F2AE72CD4A1740055587F /* CoachingComparisonView.swift in Sources */, 6F31802F2A24D1DB00089870 /* CalendarCell.swift in Sources */, 6FE1298A2A4982DA005536C4 /* HomeAPI.swift in Sources */, 374F828E2AC5DFFE00C128B9 /* DiaryBottomView.swift in Sources */, @@ -2107,6 +2118,7 @@ 4AB7C9322B78B45300845733 /* LoginViewModel.swift in Sources */, 37A574CA29FF980F00312453 /* UIStackView+.swift in Sources */, 6F294A422A26178C00856CC8 /* UserNickNameViewController.swift in Sources */, + 4A4050EC2CE8C54400E84EDE /* CoachingExplanationView.swift in Sources */, 4AC047252A8FCAE100EBDC0E /* AuthManagementViewController.swift in Sources */, 374FAF812A2CACCF00237A1A /* DiaryViewController.swift in Sources */, 37DCA65A2A47598700FF8F90 /* RandomTopicService.swift in Sources */, diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift new file mode 100644 index 00000000..ea846553 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift @@ -0,0 +1,71 @@ +// +// CoachingComparisonView.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/1/24. +// + +import SwiftUI + +struct CoachingComparisonView: View { + @State private var textHeight: CGFloat = 0 // Text의 높이를 저장할 변수 + + var body: some View { + VStack(spacing: 20) { + VStack(alignment: .leading, spacing: 8) { + HStack { + Rectangle() + .frame(width: 2, height: textHeight) + .foregroundStyle(Color(UIColor.black)) + .padding(.leading, 18) + + Text("나의 일기") + .font(Font.custom("Pretendard", size: 16).weight(.medium)) + .foregroundColor(Color(UIColor.black)) + .background(GeometryReader { geometry in + Color.clear + .preference(key: TextHeightPreferenceKey.self, value: geometry.size.height) + }) + } + .onPreferenceChange(TextHeightPreferenceKey.self) { value in + textHeight = value + } + + Text("I have went to the park yesterday") + .font(Font.custom("Pretendard", size: 14)).fontWeight(.regular) + .frame(maxWidth: .infinity, alignment: .topLeading) + .padding(.leading, 18) + } + + VStack(alignment: .leading, spacing: 8) { + HStack { + Rectangle() + .frame(width: 2, height: textHeight) + .foregroundStyle(Color(UIColor.point)) + .padding(.leading, 18) + + Text("고친 문장") + .font(Font.custom("Pretendard", size: 16).weight(.medium)) + .foregroundColor(Color(UIColor.point)) + } + + Text("I went to the park yesterday") + .font(Font.custom("Pretendard", size: 14)).fontWeight(.medium) + .foregroundColor(Color(UIColor.point)) + .frame(maxWidth: .infinity, alignment: .topLeading) + .padding(.leading, 18) + } + } + } +} + +struct TextHeightPreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = 0 + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = nextValue() + } +} + +#Preview { + CoachingComparisonView() +} diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift index 34f06813..ce075f2f 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift @@ -12,7 +12,7 @@ struct CoachingCompletedView: View { SwiftUINavigationView() - VStack() { + VStack(spacing: 16) { Text(MockData.headerText) .font(Font.custom("Pretendard", size: 16)) // Colors 상수 등록 필요 @@ -25,7 +25,6 @@ struct CoachingCompletedView: View { .foregroundColor(Color(UIColor.gray400)) .lineSpacing(0.375) } - .frame(height: screenHeight * 0.32) Spacer() } diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift new file mode 100644 index 00000000..997f7ee5 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift @@ -0,0 +1,36 @@ +// +// CoachingExplanationView.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/16/24. +// + +import SwiftUI + +struct CoachingExplanationView: View { + var body: some View { + HStack() { + + ZStack(alignment: .leading) { + GeometryReader { geomerty in + Rectangle() + .fill(Color(UIColor.gray100)) + .frame(height: geomerty.size.height) + .cornerRadius(3) + } + + Text("현재완료 시제인 have went는 과거 시제인 went로 바꾸는 것이 맞습니다. yesterday와 함께 사용할 때는 단순 과거 시제를 사용해야 합니다.") + .font(Font.custom("Pretendard", size: 14).weight(.regular)) + .foregroundStyle(.black) + .padding(12) + } + .fixedSize(horizontal: false, vertical: true) + .padding(.leading, 18) + .padding(.trailing, 18) + } + } +} + +#Preview { + CoachingExplanationView() +} diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift index e35f94af..9576441a 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift @@ -35,7 +35,7 @@ struct SegmentButton: View { Button(action: action) { Text(title) .padding(.vertical, 8) - .padding(.horizontal, 16) + .padding(.horizontal, 11) .frame(maxWidth: .infinity) .background(backgroundColor) .foregroundColor(foregroundColor) diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift index fa95556b..0c80a744 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift @@ -20,9 +20,11 @@ struct SwiftUINavigationView: View { }) .padding(.leading, 10) - CustomSegmentedControl(selectedIndex: $selectedIndex, options: options) - .frame(height: 32) - .padding(65) +// CustomSegmentedControl(selectedIndex: $selectedIndex, options: options) +// .frame(height: 32) +// .padding(65) + + Spacer() Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, label: { diff --git a/Smeem-iOS/Smeem-iOS/Presentation/CoachingView.swift b/Smeem-iOS/Smeem-iOS/Presentation/CoachingView.swift new file mode 100644 index 00000000..f9791188 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/CoachingView.swift @@ -0,0 +1,44 @@ +// +// CoachingView.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/16/24. +// + +import SwiftUI + +struct CoachingView: View { + + var body: some View { + + CoachingCompletedView() + + VStack(spacing: 20) { + Rectangle() + .frame(height: 8) + .foregroundStyle(Color(UIColor.gray100)) + + TabView { + ForEach(1...5, id: \.self) { item in + VStack(spacing: 8) { + CoachingComparisonView() + + CoachingExplanationView() + } + .frame(width: screenWidth, height: screenHeight * (326/screenHeight), alignment: .top) + } + } + .onAppear { setIndicator() } + .tabViewStyle(.page(indexDisplayMode: .always)) + } + } + + private func setIndicator() { + UIPageControl.appearance().currentPageIndicatorTintColor = UIColor(Color.primary) + UIPageControl.appearance().pageIndicatorTintColor = UIColor(Color.gray) + } +} + +#Preview { + CoachingView() +} From 6cd683de80e1bf1343c3f9299294e48ed7972955 Mon Sep 17 00:00:00 2001 From: cchanmi <113524@naver.com> Date: Sun, 17 Nov 2024 23:52:19 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[FEAT]=20#220=20-=20=EC=9D=BC=EA=B8=B0=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20=EB=B7=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 22 ++++- .../Smeem-iOS/Global/Constants/Constant.swift | 2 + .../Global/Extensions/MoyaProvier+.swift | 19 +++++ .../icnCrownMono.imageset/Contents.json | 23 ++++++ .../icnCrownMono.imageset/icon-crown-mono.png | Bin 0 -> 390 bytes .../icon-crown-mono@2x.png | Bin 0 -> 624 bytes .../icon-crown-mono@3x.png | Bin 0 -> 915 bytes .../API/Coaching/CoachingEndPoint.swift | 44 ++++++++++ .../API/Coaching/CoachingResponse.swift | 19 +++++ .../API/Coaching/CoachingService.swift | 38 +++++++++ .../API/DetailDiary/DetailDiaryAPI.swift | 33 ++++---- .../DetailDiary/DetailDiaryResponse.swift | 4 + .../{ => Coaching}/CoachingView.swift | 0 .../DetailDiaryViewController.swift | 23 +++--- .../ForeignDiaryViewController.swift | 9 +-- .../StepTwoKoreanDiaryViewController.swift | 8 +- .../DiaryComplete/DiaryCompleteView.swift | 76 ++++++++++++++++++ .../DiaryComplete/DiaryInformationView.swift | 44 ++++++++++ .../Home/HomeViewFloatingViewController.swift | 7 +- 19 files changed, 327 insertions(+), 44 deletions(-) create mode 100644 Smeem-iOS/Smeem-iOS/Global/Extensions/MoyaProvier+.swift create mode 100644 Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/Contents.json create mode 100644 Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/icon-crown-mono.png create mode 100644 Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/icon-crown-mono@2x.png create mode 100644 Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/icon-crown-mono@3x.png create mode 100644 Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingEndPoint.swift create mode 100644 Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingResponse.swift create mode 100644 Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingService.swift rename Smeem-iOS/Smeem-iOS/Presentation/{ => Coaching}/CoachingView.swift (100%) create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index a3aae256..5f4bb4e7 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -90,7 +90,6 @@ 4A004D5E2B4EE2B4003C8936 /* MyPageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A004D5D2B4EE2B4003C8936 /* MyPageService.swift */; }; 4A004D602B4EE2BD003C8936 /* MyPageAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A004D5F2B4EE2BD003C8936 /* MyPageAPI.swift */; }; 4A01235F2AAA2C1500E09646 /* SmeemLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A01235E2AAA2C1500E09646 /* SmeemLoadingView.swift */; }; - 4A01A2D72CE8D2E400A9B0AA /* CoachingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A01A2D62CE8D2E400A9B0AA /* CoachingView.swift */; }; 4A07F8A82B7B3E5E004185F2 /* SignupBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07F8A72B7B3E5E004185F2 /* SignupBottomSheetViewController.swift */; }; 4A07F8AC2B7B3E7F004185F2 /* SignupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07F8AB2B7B3E7F004185F2 /* SignupViewModel.swift */; }; 4A07F8AF2B7B58B7004185F2 /* AuthModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07F8AE2B7B58B7004185F2 /* AuthModel.swift */; }; @@ -219,6 +218,7 @@ 4ADCA2D62A9272E9002904EB /* EditAlarmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ADCA2D52A9272E9002904EB /* EditAlarmViewController.swift */; }; 4AE35E9E2A7FE8CE00465FD5 /* UICollectionViewCell+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AE35E9D2A7FE8CE00465FD5 /* UICollectionViewCell+.swift */; }; 4AEB82952A4CB4F200C1114B /* BadgePopupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEB82942A4CB4F100C1114B /* BadgePopupViewController.swift */; }; + 4AF050EB2CEA300F0055BC3F /* MoyaProvier+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF050EA2CEA300F0055BC3F /* MoyaProvier+.swift */; }; 4AF58CE92A4820AA00305248 /* AuthNetworkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF58CE82A4820AA00305248 /* AuthNetworkModel.swift */; }; 4AF78D9B2B84A1DC0098E1FA /* OnboardingServiceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF78D9A2B84A1DC0098E1FA /* OnboardingServiceTest.swift */; }; 4AF78D9E2B84AB6A0098E1FA /* MockProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF78D9D2B84AB6A0098E1FA /* MockProvider.swift */; }; @@ -335,7 +335,6 @@ 4A004D5D2B4EE2B4003C8936 /* MyPageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageService.swift; sourceTree = ""; }; 4A004D5F2B4EE2BD003C8936 /* MyPageAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageAPI.swift; sourceTree = ""; }; 4A01235E2AAA2C1500E09646 /* SmeemLoadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmeemLoadingView.swift; sourceTree = ""; }; - 4A01A2D62CE8D2E400A9B0AA /* CoachingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingView.swift; sourceTree = ""; }; 4A07F8A72B7B3E5E004185F2 /* SignupBottomSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupBottomSheetViewController.swift; sourceTree = ""; }; 4A07F8AB2B7B3E7F004185F2 /* SignupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupViewModel.swift; sourceTree = ""; }; 4A07F8AE2B7B58B7004185F2 /* AuthModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthModel.swift; sourceTree = ""; }; @@ -471,6 +470,7 @@ 4AE35E9D2A7FE8CE00465FD5 /* UICollectionViewCell+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionViewCell+.swift"; sourceTree = ""; }; 4AEB82942A4CB4F100C1114B /* BadgePopupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgePopupViewController.swift; sourceTree = ""; }; 4AEFD4C92A1392DF00DAB2BD /* TrainingWayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingWayView.swift; sourceTree = ""; }; + 4AF050EA2CEA300F0055BC3F /* MoyaProvier+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MoyaProvier+.swift"; sourceTree = ""; }; 4AF507742A10CD4D007C62B1 /* TrainingGoalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingGoalViewController.swift; sourceTree = ""; }; 4AF507762A10CD96007C62B1 /* TrainingCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingCollectionViewCell.swift; sourceTree = ""; }; 4AF58CE82A4820AA00305248 /* AuthNetworkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthNetworkModel.swift; sourceTree = ""; }; @@ -491,6 +491,12 @@ A3D7ECB62A26566A009857D6 /* EditNicknameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditNicknameViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 4AF050CE2CE9DE010055BC3F /* Coaching */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Coaching; sourceTree = ""; }; + 4AF050D72CEA01E20055BC3F /* DiaryComplete */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = DiaryComplete; sourceTree = ""; }; + 4AF050D82CEA01EA0055BC3F /* Coaching */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Coaching; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 4A07F8D52B821523004185F2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -624,7 +630,8 @@ 37A574B529FE204500312453 /* Presentation */ = { isa = PBXGroup; children = ( - 4A01A2D62CE8D2E400A9B0AA /* CoachingView.swift */, + 4AF050D82CEA01EA0055BC3F /* Coaching */, + 4AF050D72CEA01E20055BC3F /* DiaryComplete */, 4AC047232A8FCA9700EBDC0E /* AuthManagement */, 4AA6FEE72C3ABD7C00E588E9 /* ResignSummray */, 4AA5E4B52BF24CEB00F308C8 /* BadgeBottomSheet */, @@ -662,6 +669,7 @@ 4AC00ECB2AD1C143009230DA /* UICollectionView+.swift */, 4A4FEB002B721956001BBDF3 /* Combine+.swift */, 4AB7C9182B75F9B500845733 /* GesturePublisher+.swift */, + 4AF050EA2CEA300F0055BC3F /* MoyaProvier+.swift */, ); path = Extensions; sourceTree = ""; @@ -946,6 +954,7 @@ 4A0EA45A2A014111006CCE52 /* API */ = { isa = PBXGroup; children = ( + 4AF050CE2CE9DE010055BC3F /* Coaching */, 4AC7059B2BEA2A82003C5310 /* Setting */, 4ABCBCC32BDD3364003138A8 /* MySummary */, 4A9731D12BAAB55000DEC0C8 /* Splash */, @@ -1841,6 +1850,11 @@ ); dependencies = ( ); + fileSystemSynchronizedGroups = ( + 4AF050CE2CE9DE010055BC3F /* Coaching */, + 4AF050D72CEA01E20055BC3F /* DiaryComplete */, + 4AF050D82CEA01EA0055BC3F /* Coaching */, + ); name = "Smeem-iOS"; packageProductDependencies = ( 4AA942C12A2767D000A552A0 /* FSCalendar */, @@ -1997,6 +2011,7 @@ 6F294A482A27818200856CC8 /* HomeViewFloatingViewController.swift in Sources */, 37A574C529FF6EA800312453 /* FontLiterals.swift in Sources */, 4ABCBCC22BD90481003138A8 /* MyBadgeCollectionViewCell.swift in Sources */, + 4AF050EB2CEA300F0055BC3F /* MoyaProvier+.swift in Sources */, 378B20F02BA0A02400604935 /* StepOneKoreanDiaryViewModel.swift in Sources */, 4AB7C92B2B78A8D900845733 /* SmeemStartViewModel.swift in Sources */, 4A004D5B2B4EE29A003C8936 /* PushTestService.swift in Sources */, @@ -2004,7 +2019,6 @@ 4AC00EF32AD56C4C009230DA /* SmeemTextButton.swift in Sources */, 37DCA65D2A475B5100FF8F90 /* RandomTopicResponse.swift in Sources */, 4ABCBCCC2BDE8E44003138A8 /* MySummaryService.swift in Sources */, - 4A01A2D72CE8D2E400A9B0AA /* CoachingView.swift in Sources */, 371107DA2AC99099007A4AC2 /* KeyboardFollowingLayoutHandler.swift in Sources */, 4A9FAB452A4C3D1200C40D5A /* BadgeListViewController.swift in Sources */, 4ABCBCE52BE25D82003138A8 /* TrainingPlanViewController.swift in Sources */, diff --git a/Smeem-iOS/Smeem-iOS/Global/Constants/Constant.swift b/Smeem-iOS/Smeem-iOS/Global/Constants/Constant.swift index 17be86d9..08bb007b 100644 --- a/Smeem-iOS/Smeem-iOS/Global/Constants/Constant.swift +++ b/Smeem-iOS/Smeem-iOS/Global/Constants/Constant.swift @@ -83,5 +83,7 @@ extension Constant { static let icnToolTip = UIImage(named: "icnToolTip") static let splashImage = UIImage(named: "splash") + + static let icnCrownMono = UIImage(named: "icnCrownMono") } } diff --git a/Smeem-iOS/Smeem-iOS/Global/Extensions/MoyaProvier+.swift b/Smeem-iOS/Smeem-iOS/Global/Extensions/MoyaProvier+.swift new file mode 100644 index 00000000..ee1f7e10 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/Extensions/MoyaProvier+.swift @@ -0,0 +1,19 @@ +// +// MoyaProvier+.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/17/24. +// + +import Foundation +import Moya + +extension MoyaProvider { + func request(_ target: Target) async -> Result { + await withCheckedContinuation { continuation in + self.request(target) { result in + continuation.resume(returning: result) + } + } + } +} diff --git a/Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/Contents.json b/Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/Contents.json new file mode 100644 index 00000000..5415ccaf --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icon-crown-mono.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon-crown-mono@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon-crown-mono@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/icon-crown-mono.png b/Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/icon-crown-mono.png new file mode 100644 index 0000000000000000000000000000000000000000..d25f81bb809f378ba801e0b2a489bf318c0625eb GIT binary patch literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%1|*NXY)uAIoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBIcZN9$B+ufr=fQRn+$mP=DaUswq{a(z+%Lt zY|$jWLU==yw1V^w#vBFdBVY8qqP>0!R$O?mH(hAoACpYR2l|a2hMrvBjZNw|9+(_6 zmpLeUtBT#*@oj^O^2FpnPDKJL?vpYY{`&>7I0iKCmpI9Ka=YU7D&1PPb7!mM|1XlyU$7 literal 0 HcmV?d00001 diff --git a/Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/icon-crown-mono@2x.png b/Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/icon-crown-mono@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8ec27feaa72639d7aae7acbf733a0232343563b4 GIT binary patch literal 624 zcmV-$0+0QPP)*LPY*02R8^8v1gQ^oGOwckx(h0%_S6eY^YJC1B#F6|-79w`e ze)hfC1b{>$k+203Us-8c=~+3lvW6wW2E4&rxS$-C0vqfXswrUtRxk~@67fk~ z|AwW&b6!DhNObkg2bgAE?ILh(w1$6C>_dQRp7AULA@h-VyQk;19j`uugY`!V)}Cq|v?N z){0o|xWPfH+<^zEPNSQS(Ic&Jcu=&XI6S=yxlw_G$#d~NcbPTONso3|r`2^_qwnQ- zoVpvU(STZ3Hel|oMmI`Byi$r;g9}LHA0-CWM{g4?DrOV^_7@d}i->na=NqZh7X8>u zagK)eP9nBwro~|eju=K|8KbAhn-L;P_TiR;)6-;G_N=O)HoF+l{b#B|DS|1!Kzu?P zszP_{I73{Jle+S`X$CGVJ36XTC!&FP&`zjMLsYOnykjgiSi}H+LcLi$dtA-w)^8 literal 0 HcmV?d00001 diff --git a/Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/icon-crown-mono@3x.png b/Smeem-iOS/Smeem-iOS/Global/Resources/Assets.xcassets/icnCrownMono.imageset/icon-crown-mono@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f9931b96ed3200d2d10747bcd8c4194a131605d7 GIT binary patch literal 915 zcmV;E18n?>P)RK53fZ!ILv<1+)<&FG$ijM z2?i_F4_s>{E!uYITqFqHayNI=b&LABRJfDz|&(q1{4XU`vu)b(1C zICXAFC^FXnvE=(Z^?>rTMD@V2DI4xlPZm3Ys`k?w-_H~gJ75$I&;Ipd$e9{!DH0C< zMa&n3>gaj&um0@d<`+c(Cd@L2?^lWhc&v5B&3RLwC=M^=p1-YO29%+A(ju|uPt-9m z1HzVRo+3`zwMvh5oTpNAMIeO`X;hlJ zE`M-8f6~`J(oH1@Cqsm=gXtv48y~w{KdjvnTz4|`y3S)Y>UTrB3nvuLPWk^vf!G(H zsOzlS>1Nc1sYuH?D@8yhj_@!mvm#Gm>ajgy^i0$z*up+^!fdBan3c(xw}0`QHng>) zHo%O0&Km|pEHqDjLfBF}ao+Bq+5$cqYUWyK-dk(Kvci{su^W#z+HYCmadz$^!;M&I zUY>2szCs*V%~z&fxLN0%xm^_*%+Q)5E*pf2b7Ae$z*z#o^oo?G%NM(AQwwKHi8Hqw zD`%HL8mCCMkwG^HBf{zc+|9|HrpaH95m%%?G))d1Bd$nO%}Y7QXwD+BlD38=(nOOt z`^Ed5Dw$euVkK>jk4WrBN5X+?IOm?kejfHYZkVNz_R9+I1D&)#bB)(plugins: [MoyaLoggingPlugin()]) + + func coachingPostAPI(diaryId: Int) async throws -> CoachingsResponse { + let result = await self.provider.request(.coaching(diaryId: diaryId)) + + switch result { + case .success(let response): + do { + try NetworkManager.statusCodeErrorHandling(statusCode: response.statusCode) + guard let data = try? response.map(GeneralResponse.self).data else { + throw SmeemError.clientError + } + return data + } catch let error { + if let smeemError = error as? SmeemError { + throw smeemError + } else { + throw SmeemError.unknwnError + } + } + case .failure(_): + throw SmeemError.userError + } + } +} diff --git a/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift b/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift index 89666ec5..a71a51cc 100644 --- a/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift +++ b/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift @@ -11,24 +11,25 @@ final class DetailDiaryAPI { static let shared = DetailDiaryAPI() private let detailDiaryProvider = MoyaProvider(plugins:[MoyaLoggingPlugin()]) - func getDetailDiary(diaryID: Int, - completion: @escaping (Result) -> ()) { - detailDiaryProvider.request(.detailDiary(diaryID: diaryID)) { result in - switch result { - case .success(let response): - do { - try NetworkManager.statusCodeErrorHandling(statusCode: response.statusCode) - guard let data = try? response.map(GeneralResponse.self).data else { - throw SmeemError.clientError - } - completion(.success(data)) - } catch { - guard let error = error as? SmeemError else { return } - completion(.failure(error)) + func getDetailDiary(diaryID: Int) async throws -> DetailDiaryResponse { + let result = await detailDiaryProvider.request(.deleteDiary(diaryID: diaryID)) + switch result { + case .success(let response): + do { + try NetworkManager.statusCodeErrorHandling(statusCode: response.statusCode) + guard let data = try? response.map(GeneralResponse.self).data else { + throw SmeemError.clientError + } + return data + } catch let error { + if let smeemError = error as? SmeemError { + throw smeemError + } else { + throw SmeemError.unknwnError } - case .failure(_): - completion(.failure(.userError)) } + case .failure(_): + throw SmeemError.userError } } diff --git a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift index 80460c2a..e5ceb81c 100644 --- a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift +++ b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift @@ -18,3 +18,7 @@ struct CorrentionsData: Codable { let before: String let after: String } + +extension DetailDiaryResponse { + static let empty = DetailDiaryResponse(diaryId: 0, topic: "", content: "", createdAt: "", username: "") +} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/CoachingView.swift b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingView.swift similarity index 100% rename from Smeem-iOS/Smeem-iOS/Presentation/CoachingView.swift rename to Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingView.swift diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryViewController.swift index 4cc83a92..827d9ae5 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryViewController.swift @@ -148,23 +148,22 @@ extension DetailDiaryViewController { extension DetailDiaryViewController { func detailDiaryWithAPI(diaryID: Int) { - SmeemLoadingView.showLoading() - - DetailDiaryAPI.shared.getDetailDiary(diaryID: diaryId) { result in + Task { + SmeemLoadingView.showLoading() - switch result { - case .success(let response): - self.isRandomTopic = response.topic - self.diaryContent = response.content - self.dateCreated = response.createdAt - self.userName = response.username + do { + let detailDiaryResponse = try await DetailDiaryAPI.shared.getDetailDiary(diaryID: diaryId) + self.isRandomTopic = detailDiaryResponse.topic + self.diaryContent = detailDiaryResponse.content + self.dateCreated = detailDiaryResponse.createdAt + self.userName = detailDiaryResponse.username self.setData() self.setScrollerViewType() - case .failure(let error): - self.showToast(toastType: .smeemErrorToast(message: error)) + } catch let error { + self.showToast(toastType: .smeemErrorToast(message: error as! SmeemError)) } - SmeemLoadingView.hideLoading() + } } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift index 8364aecc..9ded36ca 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift @@ -7,6 +7,7 @@ import UIKit import Combine +import SwiftUI // MARK: - ForeignDiaryViewController @@ -70,11 +71,9 @@ extension ForeignDiaryViewController { .receive(on: DispatchQueue.main) .sink { [weak self] response in self?.rootView.inputTextView.resignFirstResponder() - - let homeVC = HomeViewController() - let rootVC = UINavigationController(rootViewController: homeVC) - homeVC.handlePostDiaryAPI(with: response) - homeVC.changeRootViewControllerAndPresent(rootVC) + let diaryInformantionView = DiaryCompleteView(diaryId: response?.diaryID ?? 0) + let hostingController = UIHostingController(rootView: diaryInformantionView) + self?.navigationController?.pushViewController(hostingController, animated: true) } .store(in: &cancelBag) diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift index 1e8c55d3..2ba3178d 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift @@ -7,6 +7,7 @@ import UIKit import Combine +import SwiftUI // MARK: - StepTwoKoreanDiaryViewController @@ -64,10 +65,9 @@ extension StepTwoKoreanDiaryViewController { .sink { [weak self] response in self?.rootView.inputTextView.resignFirstResponder() - let homeVC = HomeViewController() - let rootVC = UINavigationController(rootViewController: homeVC) - homeVC.handlePostDiaryAPI(with: response) - homeVC.changeRootViewControllerAndPresent(rootVC) + let diaryInformantionView = DiaryCompleteView(diaryId: response?.diaryID ?? 0) + let hostingController = UIHostingController(rootView: diaryInformantionView) + self?.navigationController?.pushViewController(hostingController, animated: true) } .store(in: &cancelBag) diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift new file mode 100644 index 00000000..016f778e --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift @@ -0,0 +1,76 @@ +// +// DiaryCompleteView.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/17/24. +// + +import SwiftUI + +struct DiaryCompleteView: View { + var diaryId: Int + @State private var detailDiaryResponse = DetailDiaryResponse.empty + @State private var errorMessage: SmeemError? + + var body: some View { + HStack() { + Spacer() + + Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, + label: { + Text("닫기") + .tint(.black) + }) + .padding(.trailing, 18) + } + .frame(height: 66) + + Button(action: {}) { + HStack { + Image("icnCrownMono") + .frame(width: 24, height: 24) + Text("하루 한 번 무료 AI 코칭") + .font(Font.custom("Pretendard", size: 16).weight(.bold)) + .lineSpacing(0.19) + .foregroundColor(.white) + } + } + .frame(width: screenWidth-32, height: 48, alignment: .center) + .background( + LinearGradient( + stops: [ + Gradient.Stop(color: Color(red: 1, green: 0, blue: 0.02).opacity(0.2), location: 0.00), + Gradient.Stop(color: .white.opacity(0.2), location: 0.28), + Gradient.Stop(color: .white.opacity(0.2), location: 0.83), + Gradient.Stop(color: Color(red: 1, green: 0, blue: 0.02).opacity(0.2), location: 1.00), + ], + startPoint: UnitPoint(x: 0.15, y: -1.24), + endPoint: UnitPoint(x: 0.72, y: 3.4) + ) + ) + .background(Color(UIColor.point)) + .cornerRadius(5) + + DiaryInformationView(diaryInformation: $detailDiaryResponse) + .onAppear { + diaryDetailAPI(diaryId: diaryId) + } + + Spacer() + } + + private func diaryDetailAPI(diaryId: Int) { + Task { + do { + let response = try await DetailDiaryAPI.shared.getDetailDiary(diaryID: diaryId) + detailDiaryResponse = response + } catch let error as SmeemError { + errorMessage = error + } + } + } +} + +#Preview { + DiaryCompleteView(diaryId: 0) +} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift new file mode 100644 index 00000000..82fb1a4d --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift @@ -0,0 +1,44 @@ +// +// DiaryInformationView.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/17/24. +// + +import SwiftUI + +struct DiaryInformationView: View { + @Binding var diaryInformation: DetailDiaryResponse + let fontHeight = UIFont(name: "Pretendard", size: 16)!.lineHeight + + var body: some View { + VStack(spacing: 24) { + Text(diaryInformation.content) + .font(Font.custom("Pretendard", size: 16)) + .lineSpacing(24 - fontHeight) + + HStack { + Spacer() + VStack(alignment: .trailing, spacing: 4) { + Text(diaryInformation.createdAt) + .font(Font.custom("Pretendard", size: 14)) + .foregroundStyle(Color(UIColor.gray500)) + .multilineTextAlignment(.trailing) + + + Text(diaryInformation.username) + .font(Font.custom("Pretendard", size: 14)) + .foregroundStyle(Color(UIColor.gray500)) + .multilineTextAlignment(.trailing) + } + } + } + .padding(.top, 15) + .padding(.leading, 18) + .padding(.trailing, 18) + } +} + +//#Preview { +// DiaryInformationView(diaryInformation: DetailDiaryResponse) +//} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Home/HomeViewFloatingViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/Home/HomeViewFloatingViewController.swift index f1e0b1fd..06a761da 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Home/HomeViewFloatingViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Home/HomeViewFloatingViewController.swift @@ -166,9 +166,10 @@ final class HomeViewFloatingViewController: UIViewController { @objc func foreignDiaryButtonDidTap(_ gesture: UITapGestureRecognizer) { AmplitudeManager.shared.track(event: AmplitudeConstant.diary.for_writing_click.event) let nextVC = diaryViewControllerFactory.makeForeignDiaryViewController() - nextVC.modalTransitionStyle = .coverVertical - nextVC.modalPresentationStyle = .fullScreen - present(nextVC, animated: true) + let navigationController = UINavigationController(rootViewController: nextVC) + navigationController.modalTransitionStyle = .coverVertical + navigationController.modalPresentationStyle = .fullScreen + present(navigationController, animated: true) } @objc func koreanDiaryButtonDidTap(_ gesture: UITapGestureRecognizer) { From 295df5644a5066b987aa2377391a651237548d66 Mon Sep 17 00:00:00 2001 From: cchanmi <113524@naver.com> Date: Mon, 18 Nov 2024 02:37:45 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[FEAT]=20#220=20-=20=EC=8A=A4=EB=B0=88=20?= =?UTF-8?q?=EB=A1=9C=EB=94=A9=EB=B7=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Smeem-iOS/Global/Extensions/SwiftUI/View+.swift | 8 ++++++++ Smeem-iOS/Smeem-iOS/Global/Resources/smeemLoading.json | 1 + .../UIComponents/SmeemComponent/SmeemToastView.swift | 3 +++ .../Network/API/DetailDiary/DetailDiaryAPI.swift | 2 +- .../Presentation/DiaryComplete/DiaryCompleteView.swift | 9 ++++++--- .../DiaryComplete/DiaryInformationView.swift | 9 ++++++--- 6 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 Smeem-iOS/Smeem-iOS/Global/Resources/smeemLoading.json diff --git a/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift b/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift index 70f0b5ba..d4b33996 100644 --- a/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift +++ b/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift @@ -19,4 +19,12 @@ extension View { var screenWidth: Double { return screenSize.width } + + func changeRootViewController(_ viewController: UIViewController) { + guard let window = UIApplication.shared.windows.first else { return } + UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: { + let rootVC = UINavigationController(rootViewController: viewController) + window.rootViewController = rootVC + }) + } } diff --git a/Smeem-iOS/Smeem-iOS/Global/Resources/smeemLoading.json b/Smeem-iOS/Smeem-iOS/Global/Resources/smeemLoading.json new file mode 100644 index 00000000..e775c2b8 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/Resources/smeemLoading.json @@ -0,0 +1 @@ +{"v":"4.8.0","meta":{"g":"LottieFiles AE 3.6.0","a":"","k":"","d":"","tc":""},"fr":29.9700012207031,"ip":0,"op":56.0000022809268,"w":2280,"h":1000,"nm":"smeemLoading","ddd":0,"assets":[{"id":"image_0","w":111,"h":340,"u":"","p":"data:image/png;base64,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","e":1},{"id":"image_1","w":186,"h":372,"u":"","p":"data:image/png;base64,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","e":1},{"id":"image_2","w":372,"h":372,"u":"","p":"data:image/png;base64,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","e":1}],"layers":[{"ddd":0,"ind":1,"ty":2,"nm":"Layer 2","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.189,"y":1},"o":{"x":0.641,"y":0},"t":7,"s":[-196.61,500.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.144,"y":1},"o":{"x":0,"y":0.004},"t":20.516,"s":[900.11,500.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.012,"y":1},"o":{"x":0.642,"y":0},"t":22,"s":[900.11,500.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":53.0000021587343,"s":[2445.61,500.417,0]}],"ix":2},"a":{"a":0,"k":[54.234,167.978,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":5.00000020365417,"op":905.000036861406,"st":5.00000020365417,"bm":0},{"ddd":0,"ind":2,"ty":2,"nm":"Layer 3","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.12,"y":1},"o":{"x":0.462,"y":0},"t":4,"s":[-235.907,500.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.938,"y":0},"o":{"x":0.014,"y":1},"t":18.823,"s":[1040.408,500.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.07,"y":1},"o":{"x":0.661,"y":0},"t":22,"s":[1040.408,500.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":45.0000018328876,"s":[2471.907,500.417,0]}],"ix":2},"a":{"a":0,"k":[92.786,184.277,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":2.00000008146167,"op":902.000036739213,"st":2.00000008146167,"bm":0},{"ddd":0,"ind":3,"ty":2,"nm":"Layer 4","refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.088,"y":1},"o":{"x":0.385,"y":0},"t":0,"s":[-280.131,500.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0,"y":0},"t":17.693,"s":[1258.131,500.417,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.136,"y":1},"o":{"x":0.679,"y":0},"t":22,"s":[1258.131,500.417,0],"to":[0,0,0],"ti":[0,0,0]},{"t":37.0000015070409,"s":[2514.131,500.417,0]}],"ix":2},"a":{"a":0,"k":[184.279,184.279,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":900.000036657751,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SmeemComponent/SmeemToastView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SmeemComponent/SmeemToastView.swift index b41de970..5b3d8d77 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SmeemComponent/SmeemToastView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SmeemComponent/SmeemToastView.swift @@ -53,6 +53,7 @@ enum SmeemError: Error { case userError case clientError case serverError + case unknwnError var displayText: String { switch self { @@ -62,6 +63,8 @@ enum SmeemError: Error { return "죄송합니다, 시스템 오류가 발생했어요 :(" case .serverError: return "데이터를 불러올 수 없어요 :(" + case .unknwnError: + return "문제가 발생했어요. 다시 접속해 주세요. :(" } } } diff --git a/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift b/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift index a71a51cc..a4cbd03c 100644 --- a/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift +++ b/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift @@ -12,7 +12,7 @@ final class DetailDiaryAPI { private let detailDiaryProvider = MoyaProvider(plugins:[MoyaLoggingPlugin()]) func getDetailDiary(diaryID: Int) async throws -> DetailDiaryResponse { - let result = await detailDiaryProvider.request(.deleteDiary(diaryID: diaryID)) + let result = await detailDiaryProvider.request(.detailDiary(diaryID: diaryID)) switch result { case .success(let response): do { diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift index 016f778e..53dae951 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift @@ -16,7 +16,9 @@ struct DiaryCompleteView: View { HStack() { Spacer() - Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, + Button(action: { + changeRootViewController(HomeViewController()) + }, label: { Text("닫기") .tint(.black) @@ -53,6 +55,7 @@ struct DiaryCompleteView: View { DiaryInformationView(diaryInformation: $detailDiaryResponse) .onAppear { + // showToast(toastType: .smeemToast(bodyType: .completed)) diaryDetailAPI(diaryId: diaryId) } @@ -64,8 +67,8 @@ struct DiaryCompleteView: View { do { let response = try await DetailDiaryAPI.shared.getDetailDiary(diaryID: diaryId) detailDiaryResponse = response - } catch let error as SmeemError { - errorMessage = error + } catch let _ as SmeemError { + // showToast(toastType: .smeemErrorToast(message: error)) } } } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift index 82fb1a4d..29a2854b 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift @@ -13,9 +13,12 @@ struct DiaryInformationView: View { var body: some View { VStack(spacing: 24) { - Text(diaryInformation.content) - .font(Font.custom("Pretendard", size: 16)) - .lineSpacing(24 - fontHeight) + HStack { + Text(diaryInformation.content) + .font(Font.custom("Pretendard", size: 16)) + .lineSpacing(24 - fontHeight) + Spacer() + } HStack { Spacer() From 89cd709b065e79d9d6b2aa4e605c7a43f8feeeca Mon Sep 17 00:00:00 2001 From: cchanmi <113524@naver.com> Date: Mon, 18 Nov 2024 03:20:55 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[FEAT]=20#220=20-=20=EB=A1=9C=EB=94=A9=20jo?= =?UTF-8?q?sn=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 6 +++++- .../NetworkDataModel/PostDiary/PostDiaryResponse.swift | 4 ++++ .../View/Controllers/ForeignDiaryViewController.swift | 2 +- .../Controllers/StepTwoKoreanDiaryViewController.swift | 2 +- .../Diaries/ViewModel/ForeignDiaryViewModel.swift | 6 +++--- .../ViewModel/StepTwoKoreanDiaryViewModel.swift | 6 +++--- .../Presentation/DiaryComplete/DiaryCompleteView.swift | 10 ++++++---- 7 files changed, 23 insertions(+), 13 deletions(-) diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index 5f4bb4e7..96cfc157 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -219,6 +219,7 @@ 4AE35E9E2A7FE8CE00465FD5 /* UICollectionViewCell+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AE35E9D2A7FE8CE00465FD5 /* UICollectionViewCell+.swift */; }; 4AEB82952A4CB4F200C1114B /* BadgePopupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEB82942A4CB4F100C1114B /* BadgePopupViewController.swift */; }; 4AF050EB2CEA300F0055BC3F /* MoyaProvier+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF050EA2CEA300F0055BC3F /* MoyaProvier+.swift */; }; + 4AF050FB2CEA60F80055BC3F /* smeemLoading.json in Resources */ = {isa = PBXBuildFile; fileRef = 4AF050FA2CEA60F80055BC3F /* smeemLoading.json */; }; 4AF58CE92A4820AA00305248 /* AuthNetworkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF58CE82A4820AA00305248 /* AuthNetworkModel.swift */; }; 4AF78D9B2B84A1DC0098E1FA /* OnboardingServiceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF78D9A2B84A1DC0098E1FA /* OnboardingServiceTest.swift */; }; 4AF78D9E2B84AB6A0098E1FA /* MockProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF78D9D2B84AB6A0098E1FA /* MockProvider.swift */; }; @@ -471,6 +472,7 @@ 4AEB82942A4CB4F100C1114B /* BadgePopupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgePopupViewController.swift; sourceTree = ""; }; 4AEFD4C92A1392DF00DAB2BD /* TrainingWayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingWayView.swift; sourceTree = ""; }; 4AF050EA2CEA300F0055BC3F /* MoyaProvier+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MoyaProvier+.swift"; sourceTree = ""; }; + 4AF050FA2CEA60F80055BC3F /* smeemLoading.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = smeemLoading.json; sourceTree = ""; }; 4AF507742A10CD4D007C62B1 /* TrainingGoalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingGoalViewController.swift; sourceTree = ""; }; 4AF507762A10CD96007C62B1 /* TrainingCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingCollectionViewCell.swift; sourceTree = ""; }; 4AF58CE82A4820AA00305248 /* AuthNetworkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthNetworkModel.swift; sourceTree = ""; }; @@ -705,6 +707,7 @@ 37A574B929FE209F00312453 /* Resources */ = { isa = PBXGroup; children = ( + 4AF050FA2CEA60F80055BC3F /* smeemLoading.json */, 37A574C629FF6EC000312453 /* Fonts */, 4A8FFA5829C9E1FE00FB76C0 /* Assets.xcassets */, ); @@ -1944,6 +1947,7 @@ 37A574D929FF9FFF00312453 /* Pretendard-SemiBold.otf in Resources */, 37A574DA29FF9FFF00312453 /* Pretendard-Regular.otf in Resources */, 4A8FFA5929C9E1FE00FB76C0 /* Assets.xcassets in Resources */, + 4AF050FB2CEA60F80055BC3F /* smeemLoading.json in Resources */, 37A574DC29FF9FFF00312453 /* Pretendard-Bold.otf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/PostDiary/PostDiaryResponse.swift b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/PostDiary/PostDiaryResponse.swift index 1f50501a..1b9d731b 100644 --- a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/PostDiary/PostDiaryResponse.swift +++ b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/PostDiary/PostDiaryResponse.swift @@ -19,6 +19,10 @@ struct PostDiaryResponse: Codable { } } +extension PostDiaryResponse { + static let empty = PostDiaryResponse(diaryID: 0, badges: [PopupBadge(name: "", imageUrl: "", type: "")]) +} + // MARK: - Badge struct PopupBadge: Codable { diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift index 9ded36ca..8f0a5211 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift @@ -71,7 +71,7 @@ extension ForeignDiaryViewController { .receive(on: DispatchQueue.main) .sink { [weak self] response in self?.rootView.inputTextView.resignFirstResponder() - let diaryInformantionView = DiaryCompleteView(diaryId: response?.diaryID ?? 0) + let diaryInformantionView = DiaryCompleteView(diaryResponse: response) let hostingController = UIHostingController(rootView: diaryInformantionView) self?.navigationController?.pushViewController(hostingController, animated: true) } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift index 2ba3178d..699bd1a0 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift @@ -65,7 +65,7 @@ extension StepTwoKoreanDiaryViewController { .sink { [weak self] response in self?.rootView.inputTextView.resignFirstResponder() - let diaryInformantionView = DiaryCompleteView(diaryId: response?.diaryID ?? 0) + let diaryInformantionView = DiaryCompleteView(diaryResponse: response) let hostingController = UIHostingController(rootView: diaryInformantionView) self?.navigationController?.pushViewController(hostingController, animated: true) } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/ViewModel/ForeignDiaryViewModel.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/ViewModel/ForeignDiaryViewModel.swift index 87a12b10..ba4ba329 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/ViewModel/ForeignDiaryViewModel.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/ViewModel/ForeignDiaryViewModel.swift @@ -18,7 +18,7 @@ final class ForeignDiaryViewModel: DiaryViewModel { } struct Output { - let rightButtonAction: AnyPublisher + let rightButtonAction: AnyPublisher let randomTopicButtonAction: AnyPublisher let refreshButtonAction: AnyPublisher let toolTipAction: AnyPublisher @@ -58,12 +58,12 @@ final class ForeignDiaryViewModel: DiaryViewModel { .handleEvents(receiveSubscription: { [weak self] _ in self?.loadingViewResult.send(true) }) - .flatMap { [weak self] _ -> AnyPublisher in + .flatMap { [weak self] _ -> AnyPublisher in if self?.isRandomTopicActive.value == false { self?.updateTopicID(to: nil) } - return Future { promise in + return Future { promise in guard let inputText = self?.getDiaryText() else { return } let topicID = SharedDiaryDataService.shared.topicID diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/ViewModel/StepTwoKoreanDiaryViewModel.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/ViewModel/StepTwoKoreanDiaryViewModel.swift index 4a7ea50f..64c60e4a 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/ViewModel/StepTwoKoreanDiaryViewModel.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/ViewModel/StepTwoKoreanDiaryViewModel.swift @@ -21,7 +21,7 @@ final class StepTwoKoreanDiaryViewModel: DiaryViewModel { } struct Output { - let rightButtonAction: AnyPublisher + let rightButtonAction: AnyPublisher let hintButtonAction: AnyPublisher let postHintResult: AnyPublisher let toastValidationResult: AnyPublisher @@ -60,8 +60,8 @@ final class StepTwoKoreanDiaryViewModel: DiaryViewModel { .handleEvents(receiveSubscription: { [weak self] _ in self?.loadingViewResult.send(true) }) - .flatMap { [weak self] _ -> AnyPublisher in - return Future { promise in + .flatMap { [weak self] _ -> AnyPublisher in + return Future { promise in guard let inputText = self?.getDiaryText() else { return } let topicID = SharedDiaryDataService.shared.topicID diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift index 53dae951..4cf0d5a0 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift @@ -8,7 +8,7 @@ import SwiftUI struct DiaryCompleteView: View { - var diaryId: Int + var diaryResponse: PostDiaryResponse @State private var detailDiaryResponse = DetailDiaryResponse.empty @State private var errorMessage: SmeemError? @@ -17,7 +17,9 @@ struct DiaryCompleteView: View { Spacer() Button(action: { - changeRootViewController(HomeViewController()) + let homeVC = HomeViewController() + homeVC.handlePostDiaryAPI(with: diaryResponse) + changeRootViewController(homeVC) }, label: { Text("닫기") @@ -56,7 +58,7 @@ struct DiaryCompleteView: View { DiaryInformationView(diaryInformation: $detailDiaryResponse) .onAppear { // showToast(toastType: .smeemToast(bodyType: .completed)) - diaryDetailAPI(diaryId: diaryId) + diaryDetailAPI(diaryId: diaryResponse.diaryID) } Spacer() @@ -75,5 +77,5 @@ struct DiaryCompleteView: View { } #Preview { - DiaryCompleteView(diaryId: 0) + DiaryCompleteView(diaryResponse: PostDiaryResponse.empty) } From 384016d86d9838ce234523370c72a76815850be2 Mon Sep 17 00:00:00 2001 From: cchanmi <113524@naver.com> Date: Tue, 19 Nov 2024 00:24:42 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[FEAT]=20#220=20-=20cocahingView+TCA=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 42 ++++-- .../xcshareddata/swiftpm/Package.resolved | 129 +++++++++++++++++- .../Global/Extensions/MoyaProvier+.swift | 51 ++++++- .../API/Coaching/CoachingService.swift | 29 ---- .../API/DetailDiary/DetailDiaryAPI.swift | 56 -------- .../API/DetailDiary/DetailDiaryEndPoint.swift | 40 ++++++ .../API/DetailDiary/DetailDiaryService.swift | 53 ++++--- .../DetailDiary/DetailDiaryResponse.swift | 8 +- .../PostDiary/PostDiaryResponse.swift | 6 +- ...{CoachingView.swift => CoachingCell.swift} | 6 +- .../DetailDiaryViewController.swift | 4 +- .../ForeignDiaryViewController.swift | 6 +- .../StepTwoKoreanDiaryViewController.swift | 6 +- .../DiaryComplete/CoachingStore.swift | 42 ++++++ .../DiaryComplete/CoachingView.swift | 75 ++++++++++ .../DiaryComplete/DiaryCompleteView.swift | 81 ----------- ...mationView.swift => DiaryDetailView.swift} | 2 +- 17 files changed, 420 insertions(+), 216 deletions(-) delete mode 100644 Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift create mode 100644 Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryEndPoint.swift rename Smeem-iOS/Smeem-iOS/Presentation/Coaching/{CoachingView.swift => CoachingCell.swift} (93%) create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingStore.swift create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingView.swift delete mode 100644 Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift rename Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/{DiaryInformationView.swift => DiaryDetailView.swift} (97%) diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index 96cfc157..e907e84f 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -74,8 +74,8 @@ 37DCA65D2A475B5100FF8F90 /* RandomTopicResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA65C2A475B5100FF8F90 /* RandomTopicResponse.swift */; }; 37DCA6602A48033400FF8F90 /* DetailDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA65F2A48033400FF8F90 /* DetailDiaryViewController.swift */; }; 37DCA6652A48052200FF8F90 /* DetailDiaryResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6642A48052200FF8F90 /* DetailDiaryResponse.swift */; }; - 37DCA6672A480A7300FF8F90 /* DetailDiaryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6662A480A7300FF8F90 /* DetailDiaryService.swift */; }; - 37DCA6692A480A8E00FF8F90 /* DetailDiaryAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6682A480A8E00FF8F90 /* DetailDiaryAPI.swift */; }; + 37DCA6672A480A7300FF8F90 /* DetailDiaryEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6662A480A7300FF8F90 /* DetailDiaryEndPoint.swift */; }; + 37DCA6692A480A8E00FF8F90 /* DetailDiaryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6682A480A8E00FF8F90 /* DetailDiaryService.swift */; }; 37DCA66E2A484D3100FF8F90 /* PostDiaryResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA66D2A484D3100FF8F90 /* PostDiaryResponse.swift */; }; 37DCA6702A484D3700FF8F90 /* PostDiaryRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA66F2A484D3700FF8F90 /* PostDiaryRequest.swift */; }; 37DCA6722A484D5000FF8F90 /* PostDiaryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6712A484D5000FF8F90 /* PostDiaryService.swift */; }; @@ -146,6 +146,7 @@ 4AA1EEEA2A5873A00017FF97 /* Urbanist-SemiBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 4AA1EEE92A5870690017FF97 /* Urbanist-SemiBold.otf */; }; 4AA5E4B42BF2355300F308C8 /* MySummaryViewModel2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA5E4B32BF2355300F308C8 /* MySummaryViewModel2.swift */; }; 4AA6FEE92C3ABDB500E588E9 /* ResignSummaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA6FEE82C3ABDB500E588E9 /* ResignSummaryViewController.swift */; }; + 4AA88F5B2CEB56DA00C59C6A /* ComposableArchitecture in Frameworks */ = {isa = PBXBuildFile; productRef = 4AA88F5A2CEB56DA00C59C6A /* ComposableArchitecture */; }; 4AA942C22A2767D000A552A0 /* FSCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = 4AA942C12A2767D000A552A0 /* FSCalendar */; }; 4AA942C52A2767D900A552A0 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4AA942C42A2767D900A552A0 /* SnapKit */; }; 4AA942CD2A276A9F00A552A0 /* Moya in Frameworks */ = {isa = PBXBuildFile; productRef = 4AA942CC2A276A9F00A552A0 /* Moya */; }; @@ -320,8 +321,8 @@ 37DCA65C2A475B5100FF8F90 /* RandomTopicResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTopicResponse.swift; sourceTree = ""; }; 37DCA65F2A48033400FF8F90 /* DetailDiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryViewController.swift; sourceTree = ""; }; 37DCA6642A48052200FF8F90 /* DetailDiaryResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryResponse.swift; sourceTree = ""; }; - 37DCA6662A480A7300FF8F90 /* DetailDiaryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryService.swift; sourceTree = ""; }; - 37DCA6682A480A8E00FF8F90 /* DetailDiaryAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryAPI.swift; sourceTree = ""; }; + 37DCA6662A480A7300FF8F90 /* DetailDiaryEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryEndPoint.swift; sourceTree = ""; }; + 37DCA6682A480A8E00FF8F90 /* DetailDiaryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryService.swift; sourceTree = ""; }; 37DCA66D2A484D3100FF8F90 /* PostDiaryResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDiaryResponse.swift; sourceTree = ""; }; 37DCA66F2A484D3700FF8F90 /* PostDiaryRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDiaryRequest.swift; sourceTree = ""; }; 37DCA6712A484D5000FF8F90 /* PostDiaryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDiaryService.swift; sourceTree = ""; }; @@ -522,6 +523,7 @@ 375B62892C590D0D00DA8E30 /* FirebaseCrashlytics in Frameworks */, 4AA942D52A276C7900A552A0 /* KakaoSDKAuth in Frameworks */, 375B628B2C590D0D00DA8E30 /* FirebaseMessaging in Frameworks */, + 4AA88F5B2CEB56DA00C59C6A /* ComposableArchitecture in Frameworks */, 375B62872C590D0D00DA8E30 /* FirebaseAnalytics in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -817,8 +819,8 @@ 37DCA6632A4804F800FF8F90 /* DetailDiary */ = { isa = PBXGroup; children = ( - 37DCA6682A480A8E00FF8F90 /* DetailDiaryAPI.swift */, - 37DCA6662A480A7300FF8F90 /* DetailDiaryService.swift */, + 37DCA6682A480A8E00FF8F90 /* DetailDiaryService.swift */, + 37DCA6662A480A7300FF8F90 /* DetailDiaryEndPoint.swift */, ); path = DetailDiary; sourceTree = ""; @@ -1872,6 +1874,7 @@ 375B62882C590D0D00DA8E30 /* FirebaseCrashlytics */, 375B628A2C590D0D00DA8E30 /* FirebaseMessaging */, 375B628C2C590D0D00DA8E30 /* FirebaseRemoteConfig */, + 4AA88F5A2CEB56DA00C59C6A /* ComposableArchitecture */, ); productName = "Smeem-iOS"; productReference = 4A8FFA4C29C9E1FD00FB76C0 /* Smeem-iOS.app */; @@ -1916,6 +1919,8 @@ A301B0A02A30720F006BE0BA /* XCRemoteSwiftPackageReference "Kingfisher" */, 4A08480A2B554513008327C7 /* XCRemoteSwiftPackageReference "Amplitude-Swift" */, 375B62852C590D0D00DA8E30 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + 4AF0512C2CEA6C050055BC3F /* XCRemoteSwiftPackageReference "lottie-ios" */, + 4AA88F592CEB56DA00C59C6A /* XCRemoteSwiftPackageReference "swift-composable-architecture" */, ); productRefGroup = 4A8FFA4D29C9E1FD00FB76C0 /* Products */; projectDirPath = ""; @@ -2056,7 +2061,7 @@ 379789032AAC705B00C61EF4 /* NavigationBarActionDelegate.swift in Sources */, 37DCA6572A47574300FF8F90 /* RandomTopicAPI.swift in Sources */, 4ABCBCD22BDFB037003138A8 /* TotalMySummaryResponse.swift in Sources */, - 37DCA6672A480A7300FF8F90 /* DetailDiaryService.swift in Sources */, + 37DCA6672A480A7300FF8F90 /* DetailDiaryEndPoint.swift in Sources */, A3D7ECB72A26566A009857D6 /* EditNicknameViewController.swift in Sources */, 371E94192A87DA960098280F /* EditHowGoalViewController.swift in Sources */, 6FE129902A49A5A6005536C4 /* Date+.swift in Sources */, @@ -2130,7 +2135,7 @@ 3728B8D52A2CFF2C00EF4CF8 /* StepOneKoreanDiaryViewController.swift in Sources */, 4AC705A22BEA2AB1003C5310 /* SettingService.swift in Sources */, 378B20F22BA0A03000604935 /* StepTwoKoreanDiaryViewModel.swift in Sources */, - 37DCA6692A480A8E00FF8F90 /* DetailDiaryAPI.swift in Sources */, + 37DCA6692A480A8E00FF8F90 /* DetailDiaryService.swift in Sources */, 37A574C129FF6E9100312453 /* ColorLiterals.swift in Sources */, 371107E92ACC7E3A007A4AC2 /* DiaryLayoutConfigurations.swift in Sources */, 4AB7C9322B78B45300845733 /* LoginViewModel.swift in Sources */, @@ -2509,6 +2514,14 @@ kind = branch; }; }; + 4AA88F592CEB56DA00C59C6A /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/pointfreeco/swift-composable-architecture"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.16.1; + }; + }; 4AA942C02A2767D000A552A0 /* XCRemoteSwiftPackageReference "FSCalendar" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/WenchaoD/FSCalendar.git"; @@ -2541,6 +2554,14 @@ kind = branch; }; }; + 4AF0512C2CEA6C050055BC3F /* XCRemoteSwiftPackageReference "lottie-ios" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/airbnb/lottie-ios"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.5.0; + }; + }; A301B0A02A30720F006BE0BA /* XCRemoteSwiftPackageReference "Kingfisher" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/onevcat/Kingfisher"; @@ -2577,6 +2598,11 @@ package = 4A08480A2B554513008327C7 /* XCRemoteSwiftPackageReference "Amplitude-Swift" */; productName = AmplitudeSwift; }; + 4AA88F5A2CEB56DA00C59C6A /* ComposableArchitecture */ = { + isa = XCSwiftPackageProductDependency; + package = 4AA88F592CEB56DA00C59C6A /* XCRemoteSwiftPackageReference "swift-composable-architecture" */; + productName = ComposableArchitecture; + }; 4AA942C12A2767D000A552A0 /* FSCalendar */ = { isa = XCSwiftPackageProductDependency; package = 4AA942C02A2767D000A552A0 /* XCRemoteSwiftPackageReference "FSCalendar" */; diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Smeem-iOS/Smeem-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 867dde87..f9d0fda6 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "ac64acc48702af6bda50edadd492248a843b468ad6c10b2b95f1226844396b78", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -45,6 +46,15 @@ "version" : "10.19.2" } }, + { + "identity" : "combine-schedulers", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/combine-schedulers", + "state" : { + "revision" : "9fa31f4403da54855f1e2aeaeff478f4f0e40b13", + "version" : "1.0.2" + } + }, { "identity" : "firebase-ios-sdk", "kind" : "remoteSourceControl", @@ -144,6 +154,15 @@ "version" : "1.22.5" } }, + { + "identity" : "lottie-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/airbnb/lottie-ios", + "state" : { + "revision" : "fe4c6fe3a0aa66cdeb51d549623c82ca9704b9a5", + "version" : "4.5.0" + } + }, { "identity" : "moya", "kind" : "remoteSourceControl", @@ -198,6 +217,96 @@ "revision" : "58320fe80522414bf3a7e24c88123581dc586752" } }, + { + "identity" : "swift-case-paths", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-case-paths", + "state" : { + "revision" : "bc92c4b27f9a84bfb498cdbfdf35d5a357e9161f", + "version" : "1.5.6" + } + }, + { + "identity" : "swift-clocks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-clocks", + "state" : { + "revision" : "b9b24b69e2adda099a1fa381cda1eeec272d5b53", + "version" : "1.0.5" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", + "version" : "1.1.4" + } + }, + { + "identity" : "swift-composable-architecture", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-composable-architecture", + "state" : { + "revision" : "69247baf7be2fd6f5820192caef0082d01849cd0", + "version" : "1.16.1" + } + }, + { + "identity" : "swift-concurrency-extras", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-concurrency-extras", + "state" : { + "revision" : "163409ef7dae9d960b87f34b51587b6609a76c1f", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-custom-dump", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-custom-dump", + "state" : { + "revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1", + "version" : "1.3.3" + } + }, + { + "identity" : "swift-dependencies", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-dependencies", + "state" : { + "revision" : "96eecd47660e8307877acb8c41cc5295ba7350a7", + "version" : "1.5.2" + } + }, + { + "identity" : "swift-identified-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-identified-collections", + "state" : { + "revision" : "2f5ab6e091dd032b63dacbda052405756010dc3b", + "version" : "1.1.0" + } + }, + { + "identity" : "swift-navigation", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-navigation", + "state" : { + "revision" : "16a27ab7ae0abfefbbcba73581b3e2380b47a579", + "version" : "2.2.2" + } + }, + { + "identity" : "swift-perception", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-perception", + "state" : { + "revision" : "dccdf5aed1db969afa11d6fbd36b96a4932ebe8c", + "version" : "1.4.0" + } + }, { "identity" : "swift-protobuf", "kind" : "remoteSourceControl", @@ -206,7 +315,25 @@ "revision" : "d57a5aecf24a25b32ec4a74be2f5d0a995a47c4b", "version" : "1.27.0" } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "0687f71944021d616d34d922343dcef086855920", + "version" : "600.0.1" + } + }, + { + "identity" : "xctest-dynamic-overlay", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state" : { + "revision" : "770f990d3e4eececb57ac04a6076e22f8c97daeb", + "version" : "1.4.2" + } } ], - "version" : 2 + "version" : 3 } diff --git a/Smeem-iOS/Smeem-iOS/Global/Extensions/MoyaProvier+.swift b/Smeem-iOS/Smeem-iOS/Global/Extensions/MoyaProvier+.swift index ee1f7e10..bf1bc94c 100644 --- a/Smeem-iOS/Smeem-iOS/Global/Extensions/MoyaProvier+.swift +++ b/Smeem-iOS/Smeem-iOS/Global/Extensions/MoyaProvier+.swift @@ -8,11 +8,56 @@ import Foundation import Moya +class ServiceNetwork { + + static let shared = ServiceNetwork() + + private init() {} + + func request(_ target: Target) async throws -> T { + let provider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) + + return try await withCheckedThrowingContinuation { continuation in + provider.request(target) { result in + switch result { + case .success(let response): + do { + try NetworkManager.statusCodeErrorHandling(statusCode: response.statusCode) + if let data = try response.map(GeneralResponse.self).data { + continuation.resume(returning: data) + } else { + continuation.resume(throwing: SmeemError.clientError) + } + } catch { + continuation.resume(throwing: SmeemError.clientError) + } + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } +} + extension MoyaProvider { - func request(_ target: Target) async -> Result { - await withCheckedContinuation { continuation in + func request(_ target: Target) async throws -> T { + try await withCheckedThrowingContinuation { continuation in self.request(target) { result in - continuation.resume(returning: result) + switch result { + case .success(let response): + do { + try NetworkManager.statusCodeErrorHandling(statusCode: response.statusCode) + if let data = try response.map(GeneralResponse.self).data { + continuation.resume(returning: data) + } else { + continuation.resume(throwing: SmeemError.clientError) + } + } catch { + continuation.resume(throwing: SmeemError.clientError) + } + case .failure(let error): + continuation.resume(throwing: error) + } } } } diff --git a/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingService.swift b/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingService.swift index 088a4349..67bc848e 100644 --- a/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingService.swift +++ b/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingService.swift @@ -7,32 +7,3 @@ import Foundation import Moya - -final class CoachingService { - - static let shared = CoachingService() - private let provider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) - - func coachingPostAPI(diaryId: Int) async throws -> CoachingsResponse { - let result = await self.provider.request(.coaching(diaryId: diaryId)) - - switch result { - case .success(let response): - do { - try NetworkManager.statusCodeErrorHandling(statusCode: response.statusCode) - guard let data = try? response.map(GeneralResponse.self).data else { - throw SmeemError.clientError - } - return data - } catch let error { - if let smeemError = error as? SmeemError { - throw smeemError - } else { - throw SmeemError.unknwnError - } - } - case .failure(_): - throw SmeemError.userError - } - } -} diff --git a/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift b/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift deleted file mode 100644 index a4cbd03c..00000000 --- a/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryAPI.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// DetailDiaryAPI.swift -// Smeem-iOS -// -// Created by Joon Baek on 2023/06/25. -// - -import Moya - -final class DetailDiaryAPI { - static let shared = DetailDiaryAPI() - private let detailDiaryProvider = MoyaProvider(plugins:[MoyaLoggingPlugin()]) - - func getDetailDiary(diaryID: Int) async throws -> DetailDiaryResponse { - let result = await detailDiaryProvider.request(.detailDiary(diaryID: diaryID)) - switch result { - case .success(let response): - do { - try NetworkManager.statusCodeErrorHandling(statusCode: response.statusCode) - guard let data = try? response.map(GeneralResponse.self).data else { - throw SmeemError.clientError - } - return data - } catch let error { - if let smeemError = error as? SmeemError { - throw smeemError - } else { - throw SmeemError.unknwnError - } - } - case .failure(_): - throw SmeemError.userError - } - } - - func deleteDiary(diaryID: Int, - completion: @escaping (Result, SmeemError>) -> ()) { - detailDiaryProvider.request(.deleteDiary(diaryID: diaryID)) { result in - switch result { - case .success(let response): - do { - try NetworkManager.statusCodeErrorHandling(statusCode: response.statusCode) - guard let data = try? response.map(GeneralResponse.self) else { - throw SmeemError.clientError - } - completion(.success(data)) - } catch { - guard let error = error as? SmeemError else { return } - completion(.failure(error)) - } - case .failure(_): - completion(.failure(.userError)) - } - } - } -} diff --git a/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryEndPoint.swift b/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryEndPoint.swift new file mode 100644 index 00000000..236357b5 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryEndPoint.swift @@ -0,0 +1,40 @@ +// +// DetailDiaryService.swift +// Smeem-iOS +// +// Created by Joon Baek on 2023/06/25. +// + +import Moya + +enum DetailDiaryEndPoint { + case detailDiary(diaryID: Int) + case deleteDiary(diaryID: Int) +} + +extension DetailDiaryEndPoint: BaseTargetType { + var path: String { + switch self { + case .detailDiary(let diaryID), .deleteDiary(let diaryID): + return URLConstant.diaryURL + "/\(diaryID)" + } + } + + var method: Moya.Method { + switch self { + case .detailDiary: + return .get + case .deleteDiary: + return .delete + } + } + + var task: Moya.Task { + return .requestPlain + } + + var headers: [String : String]? { + return ["Content-Type": "application/json", + "Authorization": "Bearer " + UserDefaultsManager.accessToken] + } +} diff --git a/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryService.swift b/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryService.swift index c23b84c2..93c62ace 100644 --- a/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryService.swift +++ b/Smeem-iOS/Smeem-iOS/Network/API/DetailDiary/DetailDiaryService.swift @@ -1,5 +1,5 @@ // -// DetailDiaryService.swift +// DetailDiaryAPI.swift // Smeem-iOS // // Created by Joon Baek on 2023/06/25. @@ -7,34 +7,33 @@ import Moya -enum DetailDiaryService { - case detailDiary(diaryID: Int) - case deleteDiary(diaryID: Int) -} - -extension DetailDiaryService: BaseTargetType { - var path: String { - switch self { - case .detailDiary(let diaryID), .deleteDiary(let diaryID): - return URLConstant.diaryURL + "/\(diaryID)" - } - } +final class DetailDiaryService { + static let shared = DetailDiaryService() + private let detailDiaryProvider = MoyaProvider(plugins:[MoyaLoggingPlugin()]) - var method: Moya.Method { - switch self { - case .detailDiary: - return .get - case .deleteDiary: - return .delete - } - } - - var task: Moya.Task { - return .requestPlain + func getDetailDiary(diaryID: Int) async throws -> DetailDiaryResponse { + let result: DetailDiaryResponse = try await detailDiaryProvider.request(.detailDiary(diaryID: diaryID)) + return result } - var headers: [String : String]? { - return ["Content-Type": "application/json", - "Authorization": "Bearer " + UserDefaultsManager.accessToken] + func deleteDiary(diaryID: Int, + completion: @escaping (Result, SmeemError>) -> ()) { + detailDiaryProvider.request(.deleteDiary(diaryID: diaryID)) { result in + switch result { + case .success(let response): + do { + try NetworkManager.statusCodeErrorHandling(statusCode: response.statusCode) + guard let data = try? response.map(GeneralResponse.self) else { + throw SmeemError.clientError + } + completion(.success(data)) + } catch { + guard let error = error as? SmeemError else { return } + completion(.failure(error)) + } + case .failure(_): + completion(.failure(.userError)) + } + } } } diff --git a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift index e5ceb81c..7ec42641 100644 --- a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift +++ b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift @@ -5,12 +5,16 @@ // Created by Joon Baek on 2023/06/25. // -struct DetailDiaryResponse: Codable { +struct DetailDiaryResponse: Codable, Equatable { let diaryId: Int let topic: String let content: String let createdAt: String let username: String + + static func == (lhs: DetailDiaryResponse, rhs: DetailDiaryResponse) -> Bool { + return lhs.diaryId == rhs.diaryId + } } struct CorrentionsData: Codable { @@ -20,5 +24,5 @@ struct CorrentionsData: Codable { } extension DetailDiaryResponse { - static let empty = DetailDiaryResponse(diaryId: 0, topic: "", content: "", createdAt: "", username: "") + static let empty = DetailDiaryResponse(diaryId: 0, topic: "", content: "테스트임?", createdAt: "그래", username: "그래") } diff --git a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/PostDiary/PostDiaryResponse.swift b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/PostDiary/PostDiaryResponse.swift index 1b9d731b..60b8f76a 100644 --- a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/PostDiary/PostDiaryResponse.swift +++ b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/PostDiary/PostDiaryResponse.swift @@ -9,7 +9,7 @@ import Foundation // MARK: - PostDiaryResponse -struct PostDiaryResponse: Codable { +struct PostDiaryResponse: Codable, Equatable { let diaryID: Int let badges: [PopupBadge] @@ -17,6 +17,10 @@ struct PostDiaryResponse: Codable { case diaryID = "diaryId" case badges } + + static func == (lhs: PostDiaryResponse, rhs: PostDiaryResponse) -> Bool { + return lhs.diaryID == rhs.diaryID + } } extension PostDiaryResponse { diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingView.swift b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCell.swift similarity index 93% rename from Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingView.swift rename to Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCell.swift index f9791188..d01bffb8 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCell.swift @@ -1,5 +1,5 @@ // -// CoachingView.swift +// CoachingCell.swift // Smeem-iOS // // Created by 황찬미 on 11/16/24. @@ -7,7 +7,7 @@ import SwiftUI -struct CoachingView: View { +struct CoachingCell: View { var body: some View { @@ -40,5 +40,5 @@ struct CoachingView: View { } #Preview { - CoachingView() + CoachingCell() } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryViewController.swift index 827d9ae5..2d067606 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryViewController.swift @@ -152,7 +152,7 @@ extension DetailDiaryViewController { SmeemLoadingView.showLoading() do { - let detailDiaryResponse = try await DetailDiaryAPI.shared.getDetailDiary(diaryID: diaryId) + let detailDiaryResponse = try await DetailDiaryService.shared.getDetailDiary(diaryID: diaryId) self.isRandomTopic = detailDiaryResponse.topic self.diaryContent = detailDiaryResponse.content self.dateCreated = detailDiaryResponse.createdAt @@ -170,7 +170,7 @@ extension DetailDiaryViewController { func deleteDiaryWithAPI(diaryID: Int) { SmeemLoadingView.showLoading() - DetailDiaryAPI.shared.deleteDiary(diaryID: diaryId) { result in + DetailDiaryService.shared.deleteDiary(diaryID: diaryId) { result in switch result { case .success(_): diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift index 8f0a5211..d0ef4d23 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift @@ -8,6 +8,7 @@ import UIKit import Combine import SwiftUI +import ComposableArchitecture // MARK: - ForeignDiaryViewController @@ -71,7 +72,10 @@ extension ForeignDiaryViewController { .receive(on: DispatchQueue.main) .sink { [weak self] response in self?.rootView.inputTextView.resignFirstResponder() - let diaryInformantionView = DiaryCompleteView(diaryResponse: response) + let diaryInformantionView = CoachingView(store: Store( + initialState: CoachingStore.State(postDiarayResponse: response), + reducer: { CoachingStore() }) + ) let hostingController = UIHostingController(rootView: diaryInformantionView) self?.navigationController?.pushViewController(hostingController, animated: true) } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift index 699bd1a0..44e643df 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift @@ -8,6 +8,7 @@ import UIKit import Combine import SwiftUI +import ComposableArchitecture // MARK: - StepTwoKoreanDiaryViewController @@ -65,7 +66,10 @@ extension StepTwoKoreanDiaryViewController { .sink { [weak self] response in self?.rootView.inputTextView.resignFirstResponder() - let diaryInformantionView = DiaryCompleteView(diaryResponse: response) + let diaryInformantionView = CoachingView(store: Store( + initialState: CoachingStore.State(postDiarayResponse: response), + reducer: { CoachingStore() }) + ) let hostingController = UIHostingController(rootView: diaryInformantionView) self?.navigationController?.pushViewController(hostingController, animated: true) } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingStore.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingStore.swift new file mode 100644 index 00000000..6aa14ba3 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingStore.swift @@ -0,0 +1,42 @@ +// +// CocahingReducer.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/18/24. +// + +import Foundation +import ComposableArchitecture + +struct CoachingStore: Reducer { + + @ObservableState + struct State: Equatable { + var postDiarayResponse: PostDiaryResponse + var detailDiaryResponse = DetailDiaryResponse.empty + } + + enum Action: Equatable { + case getDetailDiary(diaryID: Int) + + case setDetailDiary(response: DetailDiaryResponse) + } + + @Dependency(\.coachingService) var coachingService + + func reduce(into state: inout State, action: Action) -> ComposableArchitecture.Effect { + switch action { + case .getDetailDiary(let diaryID): + return .run { send in + let detailDiaryResponse = try await coachingService.detailDiaryAPI(diaryID: diaryID) + await send(.setDetailDiary(response: detailDiaryResponse)) + } + + // MARK: setter + case .setDetailDiary(let response): + state.detailDiaryResponse = response + return .none + } + } + +} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingView.swift new file mode 100644 index 00000000..4fc91907 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingView.swift @@ -0,0 +1,75 @@ +// +// DiaryCompleteView.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/17/24. +// + +import SwiftUI +import ComposableArchitecture + +struct CoachingView: View { + + let store: StoreOf + + var body: some View { + WithViewStore(store, observe: {$0}) { viewStore in + HStack() { + Spacer() + + Button(action: { + let homeVC = HomeViewController() + homeVC.handlePostDiaryAPI(with: viewStore.state.postDiarayResponse) + changeRootViewController(homeVC) + }, + label: { + Text("닫기") + .tint(.black) + }) + .padding(.trailing, 18) + } + .frame(height: 66) + + Button(action: {}) { + HStack { + Image("icnCrownMono") + .frame(width: 24, height: 24) + Text("하루 한 번 무료 AI 코칭") + .font(Font.custom("Pretendard", size: 16).weight(.bold)) + .lineSpacing(0.19) + .foregroundColor(.white) + } + } + .frame(width: screenWidth-32, height: 48, alignment: .center) + .background( + LinearGradient( + stops: [ + Gradient.Stop(color: Color(red: 1, green: 0, blue: 0.02).opacity(0.2), location: 0.00), + Gradient.Stop(color: .white.opacity(0.2), location: 0.28), + Gradient.Stop(color: .white.opacity(0.2), location: 0.83), + Gradient.Stop(color: Color(red: 1, green: 0, blue: 0.02).opacity(0.2), location: 1.00), + ], + startPoint: UnitPoint(x: 0.15, y: -1.24), + endPoint: UnitPoint(x: 0.72, y: 3.4) + ) + ) + .background(Color(UIColor.point)) + .cornerRadius(5) + + DiaryDetailView(diaryInformation: viewStore.binding( + get: { $0.detailDiaryResponse }, + send: { .setDetailDiary(response: $0)} + ) + ) + .onAppear { + viewStore.send(.getDetailDiary(diaryID: viewStore.state.postDiarayResponse.diaryID)) + } + + Spacer() + } + } +} + +#Preview { + CoachingView(store: Store(initialState: CoachingStore.State(postDiarayResponse: PostDiaryResponse.empty), reducer: { CoachingStore() })) +} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift deleted file mode 100644 index 4cf0d5a0..00000000 --- a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryCompleteView.swift +++ /dev/null @@ -1,81 +0,0 @@ -// -// DiaryCompleteView.swift -// Smeem-iOS -// -// Created by 황찬미 on 11/17/24. -// - -import SwiftUI - -struct DiaryCompleteView: View { - var diaryResponse: PostDiaryResponse - @State private var detailDiaryResponse = DetailDiaryResponse.empty - @State private var errorMessage: SmeemError? - - var body: some View { - HStack() { - Spacer() - - Button(action: { - let homeVC = HomeViewController() - homeVC.handlePostDiaryAPI(with: diaryResponse) - changeRootViewController(homeVC) - }, - label: { - Text("닫기") - .tint(.black) - }) - .padding(.trailing, 18) - } - .frame(height: 66) - - Button(action: {}) { - HStack { - Image("icnCrownMono") - .frame(width: 24, height: 24) - Text("하루 한 번 무료 AI 코칭") - .font(Font.custom("Pretendard", size: 16).weight(.bold)) - .lineSpacing(0.19) - .foregroundColor(.white) - } - } - .frame(width: screenWidth-32, height: 48, alignment: .center) - .background( - LinearGradient( - stops: [ - Gradient.Stop(color: Color(red: 1, green: 0, blue: 0.02).opacity(0.2), location: 0.00), - Gradient.Stop(color: .white.opacity(0.2), location: 0.28), - Gradient.Stop(color: .white.opacity(0.2), location: 0.83), - Gradient.Stop(color: Color(red: 1, green: 0, blue: 0.02).opacity(0.2), location: 1.00), - ], - startPoint: UnitPoint(x: 0.15, y: -1.24), - endPoint: UnitPoint(x: 0.72, y: 3.4) - ) - ) - .background(Color(UIColor.point)) - .cornerRadius(5) - - DiaryInformationView(diaryInformation: $detailDiaryResponse) - .onAppear { - // showToast(toastType: .smeemToast(bodyType: .completed)) - diaryDetailAPI(diaryId: diaryResponse.diaryID) - } - - Spacer() - } - - private func diaryDetailAPI(diaryId: Int) { - Task { - do { - let response = try await DetailDiaryAPI.shared.getDetailDiary(diaryID: diaryId) - detailDiaryResponse = response - } catch let _ as SmeemError { - // showToast(toastType: .smeemErrorToast(message: error)) - } - } - } -} - -#Preview { - DiaryCompleteView(diaryResponse: PostDiaryResponse.empty) -} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryDetailView.swift similarity index 97% rename from Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift rename to Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryDetailView.swift index 29a2854b..56740935 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryInformationView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/DiaryDetailView.swift @@ -7,7 +7,7 @@ import SwiftUI -struct DiaryInformationView: View { +struct DiaryDetailView: View { @Binding var diaryInformation: DetailDiaryResponse let fontHeight = UIFont(name: "Pretendard", size: 16)!.lineHeight From 983fa8e37fc1bbbe4fd79e861261799b340f39e0 Mon Sep 17 00:00:00 2001 From: cchanmi <113524@naver.com> Date: Tue, 19 Nov 2024 00:26:07 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[FEAT]=20#220=20-=20extension=20DependenyVa?= =?UTF-8?q?lues=20=20CoachingService=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DiaryComplete/CoachingService.swift | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingService.swift diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingService.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingService.swift new file mode 100644 index 00000000..1a514ad1 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingService.swift @@ -0,0 +1,38 @@ +// +// CoachingService.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/18/24. +// + +import Foundation +import ComposableArchitecture +import Moya + +extension DependencyValues { + var coachingService: CoachingService { + get { self[CoachingServiceKey.self] } + set { self[CoachingServiceKey.self] = newValue} + } +} + +struct CoachingServiceKey: DependencyKey { + static var liveValue: CoachingService = CoachingServiceLive() +} + +protocol CoachingService { + func coachingPostAPI(diaryID: Int) async throws -> CoachingsResponse + func detailDiaryAPI(diaryID: Int) async throws -> DetailDiaryResponse +} + +final class CoachingServiceLive: CoachingService { + func coachingPostAPI(diaryID: Int) async throws -> CoachingsResponse { + let result: CoachingsResponse = try await ServiceNetwork.shared.request(CoachingEndPoint.coaching(diaryId: diaryID)) + return result + } + + func detailDiaryAPI(diaryID: Int) async throws -> DetailDiaryResponse { + let result: DetailDiaryResponse = try await ServiceNetwork.shared.request(DetailDiaryEndPoint.detailDiary(diaryID: diaryID)) + return result + } +} From 1992edd6019fcde2dfd13701e9b5d3f2a34bd331 Mon Sep 17 00:00:00 2001 From: cchanmi <113524@naver.com> Date: Tue, 19 Nov 2024 17:27:20 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[FEAT]=20#220=20-=20=EC=BD=94=EC=B9=AD?= =?UTF-8?q?=EB=B7=B0=20API=20=EC=97=B0=EA=B2=B0=20=EB=B0=8F=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 26 +++-- .../xcshareddata/swiftpm/Package.resolved | 17 +++- .../UIComponents/CoachingComparisonView.swift | 19 ++-- .../UIComponents/CoachingCompletedView.swift | 29 +++--- .../CoachingExplanationView.swift | 10 +- .../API/Coaching/CoachingResponse.swift | 13 ++- .../API/Coaching/CoachingService.swift | 9 -- .../DetailDiary/DetailDiaryResponse.swift | 2 +- .../Presentation/Coaching/CoachingCell.swift | 44 --------- .../Coaching/CoachingCompleteView.swift | 88 +++++++++++++++++ .../ForeignDiaryViewController.swift | 6 +- .../StepTwoKoreanDiaryViewController.swift | 7 +- .../DiaryComplete/CoachingStore.swift | 68 ++++++++----- .../DiaryComplete/CoachingView.swift | 53 +++++++--- .../DiaryComplete/SmemeToastView.swift | 96 +++++++++++++++++++ .../Presentation/DiaryComplete/Store.swift | 16 ++++ 16 files changed, 357 insertions(+), 146 deletions(-) delete mode 100644 Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingService.swift delete mode 100644 Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCell.swift create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCompleteView.swift create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/SmemeToastView.swift create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/Store.swift diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index e907e84f..9b0bf8f9 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -120,6 +120,7 @@ 4A4FEB092B728144001BBDF3 /* TrainingWayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4FEB082B728144001BBDF3 /* TrainingWayViewModel.swift */; }; 4A4FEB0C2B7289E7001BBDF3 /* TrainingWayAppData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4FEB0B2B7289E7001BBDF3 /* TrainingWayAppData.swift */; }; 4A59EBA02B9367C2004D9823 /* BadgePopupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A59EB9F2B9367C2004D9823 /* BadgePopupViewModel.swift */; }; + 4A6527952CEBB25F000191C3 /* LottieUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4A6527942CEBB25F000191C3 /* LottieUI */; }; 4A6630362A51B0AD00D19CC9 /* EditDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A6630352A51B0AD00D19CC9 /* EditDiaryViewController.swift */; }; 4A6630392A51E04600D19CC9 /* PatchDiaryRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A6630382A51E04600D19CC9 /* PatchDiaryRequest.swift */; }; 4A7F22252B8241DC0011DECF /* TrainingAlarmViewModelTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A07F8E22B821539004185F2 /* TrainingAlarmViewModelTest.swift */; }; @@ -525,6 +526,7 @@ 375B628B2C590D0D00DA8E30 /* FirebaseMessaging in Frameworks */, 4AA88F5B2CEB56DA00C59C6A /* ComposableArchitecture in Frameworks */, 375B62872C590D0D00DA8E30 /* FirebaseAnalytics in Frameworks */, + 4A6527952CEBB25F000191C3 /* LottieUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1875,6 +1877,7 @@ 375B628A2C590D0D00DA8E30 /* FirebaseMessaging */, 375B628C2C590D0D00DA8E30 /* FirebaseRemoteConfig */, 4AA88F5A2CEB56DA00C59C6A /* ComposableArchitecture */, + 4A6527942CEBB25F000191C3 /* LottieUI */, ); productName = "Smeem-iOS"; productReference = 4A8FFA4C29C9E1FD00FB76C0 /* Smeem-iOS.app */; @@ -1919,8 +1922,8 @@ A301B0A02A30720F006BE0BA /* XCRemoteSwiftPackageReference "Kingfisher" */, 4A08480A2B554513008327C7 /* XCRemoteSwiftPackageReference "Amplitude-Swift" */, 375B62852C590D0D00DA8E30 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, - 4AF0512C2CEA6C050055BC3F /* XCRemoteSwiftPackageReference "lottie-ios" */, 4AA88F592CEB56DA00C59C6A /* XCRemoteSwiftPackageReference "swift-composable-architecture" */, + 4A6527932CEBB25F000191C3 /* XCRemoteSwiftPackageReference "LottieUI" */, ); productRefGroup = 4A8FFA4D29C9E1FD00FB76C0 /* Products */; projectDirPath = ""; @@ -2514,6 +2517,14 @@ kind = branch; }; }; + 4A6527932CEBB25F000191C3 /* XCRemoteSwiftPackageReference "LottieUI" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/tfmart/LottieUI"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.3.1; + }; + }; 4AA88F592CEB56DA00C59C6A /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/pointfreeco/swift-composable-architecture"; @@ -2554,14 +2565,6 @@ kind = branch; }; }; - 4AF0512C2CEA6C050055BC3F /* XCRemoteSwiftPackageReference "lottie-ios" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/airbnb/lottie-ios"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 4.5.0; - }; - }; A301B0A02A30720F006BE0BA /* XCRemoteSwiftPackageReference "Kingfisher" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/onevcat/Kingfisher"; @@ -2598,6 +2601,11 @@ package = 4A08480A2B554513008327C7 /* XCRemoteSwiftPackageReference "Amplitude-Swift" */; productName = AmplitudeSwift; }; + 4A6527942CEBB25F000191C3 /* LottieUI */ = { + isa = XCSwiftPackageProductDependency; + package = 4A6527932CEBB25F000191C3 /* XCRemoteSwiftPackageReference "LottieUI" */; + productName = LottieUI; + }; 4AA88F5A2CEB56DA00C59C6A /* ComposableArchitecture */ = { isa = XCSwiftPackageProductDependency; package = 4AA88F592CEB56DA00C59C6A /* XCRemoteSwiftPackageReference "swift-composable-architecture" */; diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Smeem-iOS/Smeem-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f9d0fda6..7a08f5f3 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "ac64acc48702af6bda50edadd492248a843b468ad6c10b2b95f1226844396b78", + "originHash" : "99709ba202d733413a5b5e700df68598b8adb35c0caba72b75c444e7c75164a5", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -155,14 +155,23 @@ } }, { - "identity" : "lottie-ios", + "identity" : "lottie-spm", "kind" : "remoteSourceControl", - "location" : "https://github.com/airbnb/lottie-ios", + "location" : "https://github.com/airbnb/lottie-spm.git", "state" : { - "revision" : "fe4c6fe3a0aa66cdeb51d549623c82ca9704b9a5", + "revision" : "b842598f1295f3ffa1475b1580672d1fe5b83580", "version" : "4.5.0" } }, + { + "identity" : "lottieui", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tfmart/LottieUI", + "state" : { + "revision" : "dd5b2f16213f89ab996a55fc2f51b56236da4bbe", + "version" : "1.3.3" + } + }, { "identity" : "moya", "kind" : "remoteSourceControl", diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift index ea846553..60d3c91d 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift @@ -8,6 +8,7 @@ import SwiftUI struct CoachingComparisonView: View { + @Binding var coachingResponse: CoachingResponse @State private var textHeight: CGFloat = 0 // Text의 높이를 저장할 변수 var body: some View { @@ -17,7 +18,6 @@ struct CoachingComparisonView: View { Rectangle() .frame(width: 2, height: textHeight) .foregroundStyle(Color(UIColor.black)) - .padding(.leading, 18) Text("나의 일기") .font(Font.custom("Pretendard", size: 16).weight(.medium)) @@ -31,30 +31,31 @@ struct CoachingComparisonView: View { textHeight = value } - Text("I have went to the park yesterday") + Text(coachingResponse.original_sentence) .font(Font.custom("Pretendard", size: 14)).fontWeight(.regular) .frame(maxWidth: .infinity, alignment: .topLeading) - .padding(.leading, 18) } + .padding(.leading, 18) + .padding(.trailing, 18) VStack(alignment: .leading, spacing: 8) { HStack { Rectangle() .frame(width: 2, height: textHeight) .foregroundStyle(Color(UIColor.point)) - .padding(.leading, 18) Text("고친 문장") .font(Font.custom("Pretendard", size: 16).weight(.medium)) .foregroundColor(Color(UIColor.point)) } - Text("I went to the park yesterday") + Text(coachingResponse.corrected_sentence) .font(Font.custom("Pretendard", size: 14)).fontWeight(.medium) .foregroundColor(Color(UIColor.point)) .frame(maxWidth: .infinity, alignment: .topLeading) - .padding(.leading, 18) } + .padding(.leading, 18) + .padding(.trailing, 18) } } } @@ -66,6 +67,6 @@ struct TextHeightPreferenceKey: PreferenceKey { } } -#Preview { - CoachingComparisonView() -} +//#Preview { +// CoachingComparisonView() +//} diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift index ce075f2f..55199b66 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift @@ -1,5 +1,5 @@ // -// CoachingCompletedView.swift +// CoachingTextView.swift // Smeem-iOS // // Created by Joon Baek on 2024/10/16. @@ -7,20 +7,20 @@ import SwiftUI -struct CoachingCompletedView: View { +struct CoachingTextView: View { + @Binding var coachingText: String + var body: some View { - SwiftUINavigationView() - VStack(spacing: 16) { - Text(MockData.headerText) + Text("일기를 잘 작성하셨어요! \n내용이 명확하고 흥미로웠습니다.\n이제 몇 가지 문법적 오류를 수정해 볼까요?") .font(Font.custom("Pretendard", size: 16)) // Colors 상수 등록 필요 .foregroundColor(Color(UIColor.smeemBlack)) .frame(maxWidth: .infinity, alignment: .leading) ScrollView { - Text(MockData.diaryEntry) + Text(coachingText) .font(Font.custom("Pretendard", size: 16)) .foregroundColor(Color(UIColor.gray400)) .lineSpacing(0.375) @@ -31,15 +31,8 @@ struct CoachingCompletedView: View { .padding(.horizontal, screenWidth * 0.048) } } - -extension CoachingCompletedView { - struct MockData { - static let headerText = "일기를 잘 작성하셨어요! \n내용이 명확하고 흥미로웠습니다.\n이제 몇 가지 문법적 오류를 수정해 볼까요?" - static let diaryEntry = "I watched Avatar with my boyfriend at Hongdae CGV. I should have skimmed the previous season 
what they were saying and the universe(??). What I was annoyed then was 두팔 didn’t know that as me. I think 두팔 who is my boyfriend should study before wathcing…. but Avatar2 is amazing movie I think. In my personal opinion, the jjin main character of Avatar2 is not Sully, but his son.the jjin main character of Avatar2 is not Sully, but his son.the jjin main character of Avatar2 is not Sully, but his son.the jjin main character of Avatar2 is not Sully, but his son.character of Avatar2 is not Sully, but his son.ly, but his ly, but his ly, but 여기부터 스크롤영역이라 내려가면 글자가 계속 나올거에욤ㄴㅇㄹ리ㅏㄴㅇ리ㅏㅓㄴ이ㅏ러민아ㅓ리ㅏㄴㅁ어리" - } -} - -@available (iOS 17, *) -#Preview { - CoachingCompletedView() -} +// +//@available (iOS 17, *) +//#Preview { +// CoachingTextView(coachingText: "") +//} diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift index 997f7ee5..8686a472 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift @@ -8,6 +8,8 @@ import SwiftUI struct CoachingExplanationView: View { + @Binding var coachingResponse: CoachingResponse + var body: some View { HStack() { @@ -19,7 +21,7 @@ struct CoachingExplanationView: View { .cornerRadius(3) } - Text("현재완료 시제인 have went는 과거 시제인 went로 바꾸는 것이 맞습니다. yesterday와 함께 사용할 때는 단순 과거 시제를 사용해야 합니다.") + Text(coachingResponse.reason) .font(Font.custom("Pretendard", size: 14).weight(.regular)) .foregroundStyle(.black) .padding(12) @@ -31,6 +33,6 @@ struct CoachingExplanationView: View { } } -#Preview { - CoachingExplanationView() -} +//#Preview { +// CoachingExplanationView() +//} diff --git a/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingResponse.swift b/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingResponse.swift index 66297941..88197c48 100644 --- a/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingResponse.swift +++ b/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingResponse.swift @@ -8,7 +8,18 @@ import Foundation struct CoachingsResponse: Codable { - let corrections: [CoachingResponse] + var corrections: [CoachingResponse] +} + +extension CoachingsResponse { + static let empty = CoachingsResponse(corrections: [CoachingResponse(original_sentence: "I have went to the park yesterdayI have went to the park yesterdayI have went to the park yesterdayI have went to the park yesterday", + corrected_sentence: "I went to the park yesterdayI went to the park yesterdayI went to the park yesterdayI went to the park yesterdayI went to the park yesterday", + reason: "현재완료 시제인 have went는 과거 시제인 went로 바꾸는 것이 맞습니다. yesterday와 함께 사용할 때는 단순 과거 시제를 사용해야 합니다.", + is_corrected: true), + CoachingResponse(original_sentence: "I have went to the park yesterdayI have went to the park yesterdayI have went럼뉴름ㄴ람ㄴㄹ 마넝롬나ㅣㅓㅇㄹ ㅗㅁ나ㅓㅇ롬나어롬나어롬나러ㅗㅁ나러 ㅗㄴ마러ㅗㅁ너ㅏ롬 ㄴ라ㅓ ㅗㄴㅁ라 왜 갑자 I have went to the park yesterdayI have went to the park yesterdayI have went to the park yesterdayI have went to the park yesterdayI have went to the park yesterday", + corrected_sentence: "I went to the park yesterdayI went to the park yesterdayI went to the park yesterday", + reason: "이러 이러한 이유로 이건 맞습니다", + is_corrected: true)]) } struct CoachingResponse: Codable { diff --git a/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingService.swift b/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingService.swift deleted file mode 100644 index 67bc848e..00000000 --- a/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingService.swift +++ /dev/null @@ -1,9 +0,0 @@ -// -// CoachingService.swift -// Smeem-iOS -// -// Created by 황찬미 on 11/17/24. -// - -import Foundation -import Moya diff --git a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift index 7ec42641..0d2f934b 100644 --- a/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift +++ b/Smeem-iOS/Smeem-iOS/Network/NetworkDataModel/DetailDiary/DetailDiaryResponse.swift @@ -8,7 +8,7 @@ struct DetailDiaryResponse: Codable, Equatable { let diaryId: Int let topic: String - let content: String + var content: String let createdAt: String let username: String diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCell.swift b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCell.swift deleted file mode 100644 index d01bffb8..00000000 --- a/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCell.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// CoachingCell.swift -// Smeem-iOS -// -// Created by 황찬미 on 11/16/24. -// - -import SwiftUI - -struct CoachingCell: View { - - var body: some View { - - CoachingCompletedView() - - VStack(spacing: 20) { - Rectangle() - .frame(height: 8) - .foregroundStyle(Color(UIColor.gray100)) - - TabView { - ForEach(1...5, id: \.self) { item in - VStack(spacing: 8) { - CoachingComparisonView() - - CoachingExplanationView() - } - .frame(width: screenWidth, height: screenHeight * (326/screenHeight), alignment: .top) - } - } - .onAppear { setIndicator() } - .tabViewStyle(.page(indexDisplayMode: .always)) - } - } - - private func setIndicator() { - UIPageControl.appearance().currentPageIndicatorTintColor = UIColor(Color.primary) - UIPageControl.appearance().pageIndicatorTintColor = UIColor(Color.gray) - } -} - -#Preview { - CoachingCell() -} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCompleteView.swift b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCompleteView.swift new file mode 100644 index 00000000..68339247 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/CoachingCompleteView.swift @@ -0,0 +1,88 @@ +// +// CoachingCell.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/16/24. +// + +import SwiftUI + +struct CoachingCompleteView: View { + + @Binding var diaryText: String + @Binding var coachingResponse: CoachingsResponse + @State var currentIndex = 0 + + var body: some View { + + VStack(spacing: 15) { + VStack(spacing: 16) { + Text("일기를 잘 작성하셨어요! \n내용이 명확하고 흥미로웠습니다.\n이제 몇 가지 문법적 오류를 수정해 볼까요?") + .font(Font.custom("Pretendard", size: 16)) + .foregroundColor(Color(UIColor.smeemBlack)) + .frame(maxWidth: .infinity, alignment: .leading) + + ScrollView { + HStack { + Text(diaryText) + .font(Font.custom("Pretendard", size: 16)) + .foregroundColor(Color(UIColor.gray400)) + .lineSpacing(0.375) + + Spacer() + } + } + + Spacer() + } + .padding(.horizontal, screenWidth * 0.048) + + VStack(spacing: 20) { + Rectangle() + .frame(height: 8) + .foregroundStyle(Color(UIColor.gray100)) + TabView(selection: $currentIndex) { + ForEach(coachingResponse.corrections.indices, id: \.self) { item in + ScrollView { + VStack(spacing: 8) { + CoachingComparisonView(coachingResponse: $coachingResponse.corrections[item]) + + CoachingExplanationView(coachingResponse: $coachingResponse.corrections[item]) + } + } + } + } + .frame(width: screenWidth, height: screenHeight * (326/screenHeight), alignment: .top) + .tabViewStyle(.page(indexDisplayMode: .never)) + + PageControl(currentPage: $currentIndex, + coachingResponse: $coachingResponse) + } + } + } +} + +struct PageControl: View { + @Binding var currentPage: Int + @Binding var coachingResponse: CoachingsResponse + + var body: some View { + HStack(spacing: 8) { + ForEach(coachingResponse.corrections.indices, id: \.self) { pagingIndex in + let isCurrentPage = currentPage == pagingIndex + + Capsule() + .fill(isCurrentPage ? .black : .gray) + .frame(width: 8.0, height: 8.0) + } + } + .animation(.linear, value: currentPage) + } +} + +#Preview { + @State var diaryText = "I watched Avatar with my boyfriend at Hongdae CGV. I should have skimmed the previous season 
what they were saying and the universe(??). What I was annoyed then was 두팔 didn’t know that as me. I think 두팔 who is my boyfriend should study before wathcing…. but Avatar2 is amazing movie I think. In my personal opinion, the jjin main character " + @State var coachingResponse = CoachingsResponse.empty + + CoachingCompleteView(diaryText: $diaryText, coachingResponse: $coachingResponse) +} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift index d0ef4d23..9451c1da 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/ForeignDiaryViewController.swift @@ -72,10 +72,8 @@ extension ForeignDiaryViewController { .receive(on: DispatchQueue.main) .sink { [weak self] response in self?.rootView.inputTextView.resignFirstResponder() - let diaryInformantionView = CoachingView(store: Store( - initialState: CoachingStore.State(postDiarayResponse: response), - reducer: { CoachingStore() }) - ) + + let diaryInformantionView = CoachingView(store: CoachingStore(diaryResponse: response)) let hostingController = UIHostingController(rootView: diaryInformantionView) self?.navigationController?.pushViewController(hostingController, animated: true) } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift index 44e643df..a17093e5 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Diaries/View/Controllers/StepTwoKoreanDiaryViewController.swift @@ -65,11 +65,8 @@ extension StepTwoKoreanDiaryViewController { .receive(on: DispatchQueue.main) .sink { [weak self] response in self?.rootView.inputTextView.resignFirstResponder() - - let diaryInformantionView = CoachingView(store: Store( - initialState: CoachingStore.State(postDiarayResponse: response), - reducer: { CoachingStore() }) - ) + + let diaryInformantionView = CoachingView(store: CoachingStore(diaryResponse: response)) let hostingController = UIHostingController(rootView: diaryInformantionView) self?.navigationController?.pushViewController(hostingController, animated: true) } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingStore.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingStore.swift index 6aa14ba3..07978fa2 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingStore.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingStore.swift @@ -1,42 +1,64 @@ // -// CocahingReducer.swift +// CoachingInteractor.swift // Smeem-iOS // // Created by 황찬미 on 11/18/24. // import Foundation -import ComposableArchitecture +import Dependencies -struct CoachingStore: Reducer { +final class CoachingStore: Store, ObservableObject { - @ObservableState - struct State: Equatable { - var postDiarayResponse: PostDiaryResponse - var detailDiaryResponse = DetailDiaryResponse.empty - } + @Published var state: State - enum Action: Equatable { - case getDetailDiary(diaryID: Int) - - case setDetailDiary(response: DetailDiaryResponse) + init(diaryResponse: PostDiaryResponse) { + self.state = State(diaryResponse: diaryResponse) } @Dependency(\.coachingService) var coachingService - func reduce(into state: inout State, action: Action) -> ComposableArchitecture.Effect { + enum Action { +// case toastMeesage + case detailDiaryAPI(diaryID: Int) +// case backButton + case coachingButton(diaryID: Int) + } + + struct State { + var detailDiaryResponse = DetailDiaryResponse.empty + var coachingResponse = CoachingsResponse.empty + var toastMessage: SmeemError? = SmeemError.clientError + var toastMessgaea: SmeemToast? = .completed + + var diaryResponse: PostDiaryResponse + + var hiddenIndex: Int = 0 + } + + @MainActor + func send(action: Action) { switch action { - case .getDetailDiary(let diaryID): - return .run { send in - let detailDiaryResponse = try await coachingService.detailDiaryAPI(diaryID: diaryID) - await send(.setDetailDiary(response: detailDiaryResponse)) + case .detailDiaryAPI(let ID): + Task { + do { + state.detailDiaryResponse = try await coachingService.detailDiaryAPI(diaryID: ID) + } catch _ { +// state.toastMessage = "일단 에러" + } + } + case .coachingButton(let ID): + Task { + do { + state.hiddenIndex += 1 + state.coachingResponse = CoachingsResponse.empty + state.hiddenIndex += 1 + } catch _ { +// state.toastMessage = "일단 에러" + } } - - // MARK: setter - case .setDetailDiary(let response): - state.detailDiaryResponse = response - return .none } } - } + + diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingView.swift index 4fc91907..62dcf03e 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/CoachingView.swift @@ -6,20 +6,21 @@ // import SwiftUI -import ComposableArchitecture +import LottieUI struct CoachingView: View { - let store: StoreOf + @StateObject var store: CoachingStore var body: some View { - WithViewStore(store, observe: {$0}) { viewStore in + + if store.state.hiddenIndex != 1 { HStack() { Spacer() Button(action: { let homeVC = HomeViewController() - homeVC.handlePostDiaryAPI(with: viewStore.state.postDiarayResponse) + homeVC.handlePostDiaryAPI(with: store.state.diaryResponse) changeRootViewController(homeVC) }, label: { @@ -29,8 +30,13 @@ struct CoachingView: View { .padding(.trailing, 18) } .frame(height: 66) - - Button(action: {}) { + } + + // MARK: 일기 작성 완료 화면 + if store.state.hiddenIndex == 0 { + Button(action: { + store.send(action: .coachingButton(diaryID: store.state.diaryResponse.diaryID)) + }) { HStack { Image("icnCrownMono") .frame(width: 24, height: 24) @@ -56,20 +62,37 @@ struct CoachingView: View { .background(Color(UIColor.point)) .cornerRadius(5) - DiaryDetailView(diaryInformation: viewStore.binding( - get: { $0.detailDiaryResponse }, - send: { .setDetailDiary(response: $0)} - ) - ) - .onAppear { - viewStore.send(.getDetailDiary(diaryID: viewStore.state.postDiarayResponse.diaryID)) - } + DiaryDetailView(diaryInformation: $store.state.detailDiaryResponse) + .onAppear { + store.send(action: .detailDiaryAPI(diaryID: store.state.diaryResponse.diaryID)) + } Spacer() + + // MARK: 로티 화면 + } else if store.state.hiddenIndex == 1 { + VStack { + LottieView("smeemLoading") + .loopMode(.loop) + .frame(width: screenWidth, height: 164, alignment: .center) + + Text("AI 코치가 내 일기를 분석하고 있어요\n잠시만 기다려주세요") + .font(Font.custom("Pretendard", size: 16)) + .multilineTextAlignment(.center) + .foregroundColor(.black) + } + + // MARK: 첨삭 화면 + } else { + CoachingCompleteView(diaryText: $store.state.detailDiaryResponse.content, + coachingResponse: $store.state.coachingResponse) } + +// SmeemErrorToastView(type: $store.state.toastMessage) + SmemeToastView(type: $store.state.toastMessgaea) } } #Preview { - CoachingView(store: Store(initialState: CoachingStore.State(postDiarayResponse: PostDiaryResponse.empty), reducer: { CoachingStore() })) + CoachingView(store: CoachingStore(diaryResponse: PostDiaryResponse.empty)) } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/SmemeToastView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/SmemeToastView.swift new file mode 100644 index 00000000..4384245c --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/SmemeToastView.swift @@ -0,0 +1,96 @@ +// +// ToastView.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/19/24. +// + +import SwiftUI + +struct SmeemErrorToastView: View { + @Binding var type: SmeemError? + @State private var opacity: Double = 0.0 + + var body: some View { + if let error = type { + ZStack { + Spacer() + VStack { + HStack(spacing: 14) { + Image("icnToastError") + .frame(width: 24, height: 24) + + VStack(alignment: .leading, spacing: 3) { + Text(error.displayText) + .font(Font.custom("Pretendard", size: 14).weight(.bold)) + .foregroundColor(.white) + + Text("재접속하거나 나중에 다시 시도해 주세요.") + .font(Font.custom("Pretendard", size: 12)) + .foregroundColor(.white) + } + } + .frame(width: screenWidth-36, height: 70) + .background(Color(UIColor.toastBackground)) + .cornerRadius(6) + .padding(.bottom, 20) + .opacity(opacity) + .transition(.opacity) + .onAppear { + + withAnimation(.easeIn(duration: 0.6)) { + opacity = 1.0 + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + withAnimation(.easeOut(duration: 0.6)) { + opacity = 0.0 + type = nil + } + } + } + } + } + } + } +} + +struct SmemeToastView: View { + @Binding var type: SmeemToast? + @State private var opacity: Double = 0.0 + + var body: some View { + if let toast = type { + ZStack { + Spacer() + HStack { + Text(toast.displayText) + .font(Font.custom("Pretendard", size: 14).weight(.medium)) + .foregroundColor(.white) + .padding(.vertical, 17) + .padding(.leading, 18) + Spacer() + } + .frame(width: screenWidth-36, height: 50) + .background(Color(UIColor.toastBackground)) + .cornerRadius(6) + .padding(.bottom, 20) + .opacity(opacity) + .transition(.opacity) + .onAppear { + + withAnimation(.easeIn(duration: 0.6)) { + opacity = 1.0 + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + withAnimation(.easeOut(duration: 0.6)) { + opacity = 0.0 + type = nil + } + } + } + } + } + } +} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/Store.swift b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/Store.swift new file mode 100644 index 00000000..b1d8052d --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/DiaryComplete/Store.swift @@ -0,0 +1,16 @@ +// +// ViewModel.swift +// Smeem-iOS +// +// Created by 황찬미 on 11/18/24. +// + +import Foundation + +protocol Store { + associatedtype Action + associatedtype State + + var state: State { get } + func send(action: Action) +}