From 6562dd9a475e2f200efb571eaaf9839d09d5a9b0 Mon Sep 17 00:00:00 2001 From: thinkyside Date: Thu, 22 May 2025 16:37:57 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[#370]=20AnswerCell=20=ED=84=B0=EC=B9=98=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Repository/MemberRepository.swift | 24 +++++++++----- .../2-2.TodayQuestion/TodayQuestionView.swift | 32 +++++++++++-------- .../2-6.AnswerList/AnswerListView.swift | 32 +++++++++++-------- .../3-1.BulletinBoard/BulletinBoardView.swift | 23 +++++++------ .../5-3.MyAnswerList/MyAnswerListView.swift | 32 +++++++++++-------- .../SourceCode/UIComponent/QPAnswerCell.swift | 1 + .../SourceCode/Utility/Pagination+.swift | 2 +- 7 files changed, 83 insertions(+), 63 deletions(-) diff --git a/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift b/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift index 2f6cf8ec..99cb180e 100644 --- a/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift +++ b/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift @@ -31,14 +31,22 @@ extension MemberRepository: DependencyKey { static let liveValue = Self( signIn: { code in let deviceToken = try keychainService.fetchData(.deviceToken) - let response = try await MemberAPI.signIn( - code: code, - deviceToken: deviceToken, - server: repositoryService.server - ) - try keychainService.createData(.accessToken, response.accessToken ?? "") - try keychainService.createData(.refreshToken, response.refreshToken ?? "") - return response.isMember + + // print("code: \(code)") + // print("deviceToken: \(deviceToken)") + + try keychainService.createData(.accessToken, "") + + return true + +// let response = try await MemberAPI.signIn( +// code: code, +// deviceToken: deviceToken, +// server: repositoryService.server +// ) +// try keychainService.createData(.accessToken, response.accessToken ?? "") +// try keychainService.createData(.refreshToken, response.refreshToken ?? "") +// return response.isMember }, signUp: { email, nickname in let refreshToken = try keychainService.fetchData(.refreshToken) diff --git a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionView.swift b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionView.swift index e1e0662e..7cf11819 100644 --- a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionView.swift +++ b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionView.swift @@ -300,20 +300,24 @@ private struct AnswerPreviewList: View { VStack(spacing: 0) { ForEach(enumerated(store.answerPreviewList), id: \.element.id) { index, answer in - QPAnswerCell( - answer: answer, - index: index, - state: .normal, - seeMoreAction: { - store.send(.seeMoreAnswerButtonTapped(answer)) - }, - likeAction: { - store.send(.likeAnswerButtonTapped(answer)) - }, - commentAction: { - store.send(.answerCommentButtonTapped(answer)) - } - ) + Button { + store.send(.answerCommentButtonTapped(answer)) + } label: { + QPAnswerCell( + answer: answer, + index: index, + state: .normal, + seeMoreAction: { + store.send(.seeMoreAnswerButtonTapped(answer)) + }, + likeAction: { + store.send(.likeAnswerButtonTapped(answer)) + }, + commentAction: { + store.send(.answerCommentButtonTapped(answer)) + } + ) + } } } } diff --git a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-6.AnswerList/AnswerListView.swift b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-6.AnswerList/AnswerListView.swift index 84040b00..68d0b4c7 100644 --- a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-6.AnswerList/AnswerListView.swift +++ b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-6.AnswerList/AnswerListView.swift @@ -138,20 +138,24 @@ private struct AnswerList: View { ForEach(enumerated(store.answerList), id: \.element.id) { index, answer in - QPAnswerCell( - answer: answer, - index: index, - state: .normal, - seeMoreAction: { - store.send(.seeMoreAction(answer)) - }, - likeAction: { - store.send(.likeAnswerButtonTapped(answer)) - }, - commentAction: { - store.send(.answerCommentButtonTapped(answer)) - } - ) + Button { + store.send(.answerCommentButtonTapped(answer)) + } label: { + QPAnswerCell( + answer: answer, + index: index, + state: .normal, + seeMoreAction: { + store.send(.seeMoreAction(answer)) + }, + likeAction: { + store.send(.likeAnswerButtonTapped(answer)) + }, + commentAction: { + store.send(.answerCommentButtonTapped(answer)) + } + ) + } .configurePagination( store.answerList, currentIndex: index, diff --git a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardView.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardView.swift index 7b7f100f..fd95629b 100644 --- a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardView.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardView.swift @@ -21,7 +21,7 @@ struct BulletinBoardView: View { BulletinBoardContentView(store: store) NewBoardPostButton(store: store) - .padding(.bottom, 20) + .padding(.bottom, 16) } .background(.first) .onChange(of: scenePhase) { _, newPhase in @@ -70,17 +70,8 @@ private struct BulletinBoardContentView: View { } ) - Button { - store.send(.academyDayCounterTapped) - } label: { - QPAcademyDayCounter(event: store.event) - .padding(.top, 8) - .padding(.horizontal, 16) - } - .buttonStyle(ScalableButtonStyle()) - BulletionBoardListView(store: store) - .padding(.top, 20) + .padding(.top, 8) Spacer() .frame(height: 2) @@ -96,6 +87,14 @@ private struct BulletionBoardListView: View { var body: some View { ScrollView { + Button { + store.send(.academyDayCounterTapped) + } label: { + QPAcademyDayCounter(event: store.event) + .padding(.horizontal, 16) + } + .buttonStyle(ScalableButtonStyle()) + Group { switch store.state.popularAnswerStatus { case .todayQuestion: @@ -113,7 +112,7 @@ private struct BulletionBoardListView: View { } } .padding(.horizontal) - .padding(.top, 2) + .padding(.top, 12) LazyVStack(spacing: 0) { ForEach(enumerated(store.bulletinBoardList), id: \.offset) { index, board in diff --git a/Qapple/Qapple/SourceCode/Feature/5.Profile/5-3.MyAnswerList/MyAnswerListView.swift b/Qapple/Qapple/SourceCode/Feature/5.Profile/5-3.MyAnswerList/MyAnswerListView.swift index ee498522..90882a20 100644 --- a/Qapple/Qapple/SourceCode/Feature/5.Profile/5-3.MyAnswerList/MyAnswerListView.swift +++ b/Qapple/Qapple/SourceCode/Feature/5.Profile/5-3.MyAnswerList/MyAnswerListView.swift @@ -64,20 +64,24 @@ private struct MyAnswerList: View { LazyVStack(spacing: 0) { ForEach(enumerated(store.myAnswerList), id: \.element.id) { index, answer in - QPAnswerCell( - answer: answer, - index: index, - state: .written, - seeMoreAction:{ - store.send(.seeMoreAction(answer)) - }, - likeAction: { - store.send(.likeAnswerButtonTapped(answer)) - }, - commentAction: { - store.send(.commentButtonTapped(answer)) - } - ) + Button { + store.send(.commentButtonTapped(answer)) + } label: { + QPAnswerCell( + answer: answer, + index: index, + state: .written, + seeMoreAction:{ + store.send(.seeMoreAction(answer)) + }, + likeAction: { + store.send(.likeAnswerButtonTapped(answer)) + }, + commentAction: { + store.send(.commentButtonTapped(answer)) + } + ) + } .configurePagination( store.myAnswerList, currentIndex: index, diff --git a/Qapple/Qapple/SourceCode/UIComponent/QPAnswerCell.swift b/Qapple/Qapple/SourceCode/UIComponent/QPAnswerCell.swift index 0e875155..9c4c9f33 100644 --- a/Qapple/Qapple/SourceCode/UIComponent/QPAnswerCell.swift +++ b/Qapple/Qapple/SourceCode/UIComponent/QPAnswerCell.swift @@ -140,6 +140,7 @@ private struct NormalCell: View { Text(answer.content) .pretendard(.medium, 16) + .multilineTextAlignment(.leading) .foregroundStyle(.main) .padding(.top, 2) } diff --git a/Qapple/Qapple/SourceCode/Utility/Pagination+.swift b/Qapple/Qapple/SourceCode/Utility/Pagination+.swift index d22954b9..daebacfa 100644 --- a/Qapple/Qapple/SourceCode/Utility/Pagination+.swift +++ b/Qapple/Qapple/SourceCode/Utility/Pagination+.swift @@ -16,7 +16,7 @@ struct ConfigurePagination: ViewModifier { func body(content: Content) -> some View { content.onAppear { - if currentIndex == list.endIndex - 5 && hasNext { + if currentIndex == list.endIndex - 10 && hasNext { pagination() } } From 0caa32978b1a2afeabfe56e56685a2839cdeca23 Mon Sep 17 00:00:00 2001 From: thinkyside Date: Thu, 22 May 2025 17:16:10 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[#370]=20=EC=95=B1=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EC=96=B4=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20Alert=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Qapple/Qapple.xcodeproj/project.pbxproj | 4 +-- .../Data/Repository/MemberRepository.swift | 4 +-- .../Data/Service/VersionService.swift | 11 +++--- .../0-1.SignUpFlow/SignUpFlowFeature.swift | 36 ++++++++++++++++--- .../0-1.SignUpFlow/SignUpView.swift | 1 + .../Utility/NetworkErrorAlert+.swift | 3 +- 6 files changed, 46 insertions(+), 13 deletions(-) diff --git a/Qapple/Qapple.xcodeproj/project.pbxproj b/Qapple/Qapple.xcodeproj/project.pbxproj index a9d72c93..069a7016 100644 --- a/Qapple/Qapple.xcodeproj/project.pbxproj +++ b/Qapple/Qapple.xcodeproj/project.pbxproj @@ -425,7 +425,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.3; + MARKETING_VERSION = 2.1.4; PRODUCT_BUNDLE_IDENTIFIER = "com.qapple.Apple-Developer-Academy-POSTECH"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -472,7 +472,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.3; + MARKETING_VERSION = 2.1.4; PRODUCT_BUNDLE_IDENTIFIER = "com.qapple.Apple-Developer-Academy-POSTECH"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift b/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift index 99cb180e..11fbda32 100644 --- a/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift +++ b/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift @@ -32,8 +32,8 @@ extension MemberRepository: DependencyKey { signIn: { code in let deviceToken = try keychainService.fetchData(.deviceToken) - // print("code: \(code)") - // print("deviceToken: \(deviceToken)") + print("code: \(code)") + print("deviceToken: \(deviceToken)") try keychainService.createData(.accessToken, "") diff --git a/Qapple/Qapple/SourceCode/Data/Service/VersionService.swift b/Qapple/Qapple/SourceCode/Data/Service/VersionService.swift index 47909768..6f78ac17 100644 --- a/Qapple/Qapple/SourceCode/Data/Service/VersionService.swift +++ b/Qapple/Qapple/SourceCode/Data/Service/VersionService.swift @@ -15,12 +15,12 @@ struct VersionService { } /// 현재 버전이 최신 버전인지 확인 - static func isRecentVersion() async -> Result { + static func isRecentVersion() async throws -> Bool { guard let recentVersion = await appStoreAppVersion() else { - return .failure(VersionError.networkError) + throw VersionError.networkError } - return .success(recentVersion == deviceAppVersion) + return recentVersion == deviceAppVersion } /// 앱스토어 내 최신 버전 @@ -55,7 +55,10 @@ struct VersionService { /// 앱스토어를 Open합니다. static func openAppStore() { - guard let url = URL(string: appStoreOpenUrlString) else { return } + guard let url = URL(string: appStoreOpenUrlString) else { + print("URL 생성 실패: \(appStoreOpenUrlString)") + return + } if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } diff --git a/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift b/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift index e15e2547..742ef513 100644 --- a/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift @@ -21,13 +21,16 @@ struct SignUpFlowFeature { enum Action { case onAppear + case updateVersion case autoLoginResponse case socialLogin(SocialLoginFeature.Action) case networkingFailed(Error) case path(StackActionOf) case alert(PresentationAction) - enum Alert: Equatable {} + enum Alert: Equatable { + case navigateToAppStore + } } @Dependency(\.appleLoginService) var appleLoginService @@ -42,13 +45,22 @@ struct SignUpFlowFeature { guard state.isFirstLaunch else { return .none } return .run { send in do { - try await appleLoginService.autoLogin() - await send(.autoLoginResponse) + let isRecentVersion = try await VersionService.isRecentVersion() + if isRecentVersion { + try await appleLoginService.autoLogin() + await send(.autoLoginResponse) + } else { + await send(.updateVersion) + } } catch { - await send(.networkingFailed(error)) + print(error) } } + case .updateVersion: + state.alert = .requiredUpdate + return .none + case .autoLoginResponse: state.isFirstLaunch = false state.$isSignIn.withLock { $0 = true } @@ -97,6 +109,10 @@ struct SignUpFlowFeature { return .none } + case .alert(.presented(.navigateToAppStore)): + VersionService.openAppStore() + return .none + case .alert: return .none @@ -122,3 +138,15 @@ extension SignUpFlowFeature { case signUpComplete(SignUpCompleteFeature) } } + +// MARK: - Alert + +extension AlertState where Action == SignUpFlowFeature.Action.Alert { + static let requiredUpdate = AlertState { + TextState("원활한 서비스 이용을 위해 캐플 앱 업데이트가 필요해요!") + } actions: { + ButtonState(role: .none, action: .navigateToAppStore) { + TextState("앱스토어 이동") + } + } +} diff --git a/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpView.swift b/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpView.swift index 5dd23985..cfc7a268 100644 --- a/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpView.swift +++ b/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpView.swift @@ -27,6 +27,7 @@ struct SignUpFlowView: View { .onAppear { store.send(.onAppear) } + .alert($store.scope(state: \.alert, action: \.alert)) } } diff --git a/Qapple/Qapple/SourceCode/Utility/NetworkErrorAlert+.swift b/Qapple/Qapple/SourceCode/Utility/NetworkErrorAlert+.swift index 755de3a4..d0738f0f 100644 --- a/Qapple/Qapple/SourceCode/Utility/NetworkErrorAlert+.swift +++ b/Qapple/Qapple/SourceCode/Utility/NetworkErrorAlert+.swift @@ -11,7 +11,8 @@ extension AlertState { /// 네트워킹 실패 기본 Alert static func failedNetworking(with error: Error) -> Self { - Self { + print(error) + return Self { TextState("네트워크 상태가 불안정해요") } actions: { ButtonState(role: .cancel) { From 5fa7225504b2066341f00ac14059be40cbfb49ed Mon Sep 17 00:00:00 2001 From: thinkyside Date: Thu, 22 May 2025 17:33:30 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[#370]=20=EB=8B=B5=EB=B3=80=20=EB=82=B4=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=20=EC=8B=A0=EA=B3=A0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repository/AnswerCommentRepository.swift | 3 +- .../Data/Repository/ReportRepository.swift | 8 +++- .../Data/Service/UserDefaultsService.swift | 37 +++++++++++++++++++ .../Feature/8.Report/ReportFeature.swift | 7 ++-- 4 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 Qapple/Qapple/SourceCode/Data/Service/UserDefaultsService.swift diff --git a/Qapple/Qapple/SourceCode/Data/Repository/AnswerCommentRepository.swift b/Qapple/Qapple/SourceCode/Data/Repository/AnswerCommentRepository.swift index 32380987..13f9a29e 100644 --- a/Qapple/Qapple/SourceCode/Data/Repository/AnswerCommentRepository.swift +++ b/Qapple/Qapple/SourceCode/Data/Repository/AnswerCommentRepository.swift @@ -49,7 +49,8 @@ extension AnswerCommentRepository: DependencyKey { ) } - return list + // TODO: 추후 신고된 아이디 로직 수정 필요 + return list.filter { !UserDefaultsService.reportedAnswerCommentIds.contains($0.id) } }, createAnswerComment: { answerId, content in let _ = try await RepositoryService.shared.request { server, accessToken in diff --git a/Qapple/Qapple/SourceCode/Data/Repository/ReportRepository.swift b/Qapple/Qapple/SourceCode/Data/Repository/ReportRepository.swift index 70edbd6c..e5718d12 100644 --- a/Qapple/Qapple/SourceCode/Data/Repository/ReportRepository.swift +++ b/Qapple/Qapple/SourceCode/Data/Repository/ReportRepository.swift @@ -12,7 +12,8 @@ import Foundation struct ReportRepository { var reportAnswer: (_ answerId: Int, _ reportType: ReportType) async throws -> Void var reportBoard: (_ boardId: Int, _ reportType: ReportType) async throws -> Void - var reportComment: (_ commentId: Int, _ reportType: ReportType) async throws -> Void + var reportBoardComment: (_ commentId: Int, _ reportType: ReportType) async throws -> Void + var reportAnswerComment:(_ commentId: Int, _ reportType: ReportType) async throws -> Void } // MARK: - DependencyKey @@ -40,7 +41,7 @@ extension ReportRepository: DependencyKey { ) } }, - reportComment: { commentId, reportType in + reportBoardComment: { commentId, reportType in let _ = try await RepositoryService.shared.request { server, accessToken in try await ReportAPI.reportBoardComment( boardCommentId: commentId, @@ -49,6 +50,9 @@ extension ReportRepository: DependencyKey { accessToken: accessToken ) } + }, reportAnswerComment: { commentId, reportType in + // TODO: 추후 API 교체 필요 + UserDefaultsService.reportedAnswerCommentIds.append(commentId) } ) } diff --git a/Qapple/Qapple/SourceCode/Data/Service/UserDefaultsService.swift b/Qapple/Qapple/SourceCode/Data/Service/UserDefaultsService.swift new file mode 100644 index 00000000..737dd255 --- /dev/null +++ b/Qapple/Qapple/SourceCode/Data/Service/UserDefaultsService.swift @@ -0,0 +1,37 @@ +// +// UserDefaultsService.swift +// Qapple +// +// Created by 김민준 on 5/22/25. +// + +import Foundation + +/// UserDefaults 관리 객체 +struct UserDefaultsService { + + @UserDefault(key: "reportedAnswerCommentIds", defaultValue: []) + static var reportedAnswerCommentIds: [Int] +} + +// MARK: - propertyWrapper + +@propertyWrapper +struct UserDefault { + + let key: String + let defaultValue: T + + init(key: String, defaultValue: T) { + self.key = key + self.defaultValue = defaultValue + } + + var wrappedValue: T { + get { + UserDefaults.standard.object(forKey: key) as? T ?? defaultValue + } set { + UserDefaults.standard.set(newValue, forKey: key) + } + } +} diff --git a/Qapple/Qapple/SourceCode/Feature/8.Report/ReportFeature.swift b/Qapple/Qapple/SourceCode/Feature/8.Report/ReportFeature.swift index a8496161..4d2917be 100644 --- a/Qapple/Qapple/SourceCode/Feature/8.Report/ReportFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/8.Report/ReportFeature.swift @@ -58,11 +58,10 @@ struct ReportFeature { try await reportRepository.reportBoard(board.id, reportType) case let .comment(comment): - try await reportRepository.reportComment(comment.id, reportType) - case let .answerComment(comment): - // TODO: 답변 댓글 신고 구현 - break + try await reportRepository.reportBoardComment(comment.id, reportType) + case let .answerComment(comment): + try await reportRepository.reportAnswerComment(comment.id, reportType) } await send(.completionReport) } catch { From 375969ccb3d498f64958ecea04f5cabed9c3a381 Mon Sep 17 00:00:00 2001 From: thinkyside Date: Thu, 22 May 2025 19:45:16 +0900 Subject: [PATCH 4/8] =?UTF-8?q?[#370]=20=EC=9D=B8=EA=B8=B0=20=EB=8B=B5?= =?UTF-8?q?=EB=B3=80=20=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Qapple/Qapple.xcodeproj/project.pbxproj | 1 + .../Qapple/SourceCode/App/AppDelegate.swift | 4 +- .../Data/Repository/AnswerRepository.swift | 200 +++++++++--------- .../2-6.AnswerList/AnswerListFeature.swift | 16 +- .../BulletinBoardFeature.swift | 11 +- 5 files changed, 115 insertions(+), 117 deletions(-) diff --git a/Qapple/Qapple.xcodeproj/project.pbxproj b/Qapple/Qapple.xcodeproj/project.pbxproj index 069a7016..a04c4e7a 100644 --- a/Qapple/Qapple.xcodeproj/project.pbxproj +++ b/Qapple/Qapple.xcodeproj/project.pbxproj @@ -402,6 +402,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Qapple/QappleBox/Qapple.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2505142036; diff --git a/Qapple/Qapple/SourceCode/App/AppDelegate.swift b/Qapple/Qapple/SourceCode/App/AppDelegate.swift index 14034138..76524bb6 100644 --- a/Qapple/Qapple/SourceCode/App/AppDelegate.swift +++ b/Qapple/Qapple/SourceCode/App/AppDelegate.swift @@ -29,9 +29,9 @@ extension AppDelegate { didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool { #if DEBUG - RepositoryService.shared.configureServer(to: .production) + RepositoryService.shared.configureServer(to: .test) #else - RepositoryService.shared.configureServer(to: .production) + RepositoryService.shared.configureServer(to: .test) #endif setupPushNotification(application) setupFirebase() diff --git a/Qapple/Qapple/SourceCode/Data/Repository/AnswerRepository.swift b/Qapple/Qapple/SourceCode/Data/Repository/AnswerRepository.swift index 1ae9b409..343a2160 100644 --- a/Qapple/Qapple/SourceCode/Data/Repository/AnswerRepository.swift +++ b/Qapple/Qapple/SourceCode/Data/Repository/AnswerRepository.swift @@ -17,7 +17,8 @@ struct AnswerRepository { QappleAPI.TotalCount, QappleAPI.PaginationInfo ) - var fetchPopularAnswer: (_ question: Question?) async throws -> (Answer?, Question?, Bool) + var fetchPopularAnswerOfMainQuestion: () async throws -> (Answer?, Question?, Bool) + var fetchPopularAnswer: (_ question: Question) async throws -> (Answer?, Question?, Bool) var postAnswer: (_ questionId: Int, _ answer: String) async throws -> Void var deleteAnswer: (_ answerId: Int) async throws -> Void var likeAnswer: (_ questionId: Int) async throws -> Void @@ -118,113 +119,44 @@ extension AnswerRepository: DependencyKey { ) return (answerList, response.total, paginationInfo) }, - fetchPopularAnswer: { question in + fetchPopularAnswerOfMainQuestion: { let currentHour = Calendar.current.component(.hour, from: .now) if currentHour > 12 && currentHour < 19 { return (nil, nil, false) } - let response = try await RepositoryService.shared.request { server, accessToken in - try await QuestionAPI.fetchQuestionList( - threshold: nil, - pageSize: 1, - server: server, - accessToken: accessToken - ) + let mainQuestion = try await RepositoryService.shared.request { server, accessToken in + try await QuestionAPI.fetchMainQuestion(server: server, accessToken: accessToken) } - guard let questionContent = response.content.first else { return (nil, nil, true) } - - let currentQuestion = question ?? Question( - id: questionContent.questionId, - content: questionContent.content, - publishedDate: questionContent.livedAt?.ISO8601ToDate(.yearMonthDateTime) ?? .now, - isAnswered: questionContent.isAnswered, - isLived: questionContent.questionStatus == ("LIVE") + let question = Question( + id: mainQuestion.questionId, + content: mainQuestion.content, + publishedDate: .now, + isAnswered: mainQuestion.isAnswered, + isLived: mainQuestion.questionStatus == ("LIVE") ) - var popularAnswer: Answer = .init( - id: -1, - writerId: -1, - content: "", - authorNickname: "", - authorGeneration: "", - publishedDate: .init(timeIntervalSince1970: 0), - isReported: false, - isMine: false, - isLiked: false, - isResignMember: false, - commentCount: 0, - heartCount: 0 - ) - - var hasNext = true - var threshold: Int? + if let popularAnswer = try await getPopularQuestion(from: question) { + return (popularAnswer, question, true) + } else { + return (nil, question, true) + } + }, + fetchPopularAnswer: { question in + let mainQuestion = try await RepositoryService.shared.request { server, accessToken in + try await QuestionAPI.fetchMainQuestion(server: server, accessToken: accessToken) + } - while hasNext { - let answersOfQuestion = try await RepositoryService.shared.request { server, accessToken in - try await AnswerAPI.fetchListOfQuestion( - questionId: Int(currentQuestion.id), - threshold: threshold, - pageSize: 30, - server: server, - accessToken: accessToken - ) - } - - if answersOfQuestion.content.isEmpty { - return (nil, nil, true) - } - - for answer in answersOfQuestion.content { - if answer.isReported { continue } - - let sum = answer.commentCount + answer.heartCount - let popularSum = popularAnswer.heartCount + popularAnswer.commentCount - - if sum > popularSum { - popularAnswer = .init( - id: answer.answerId, - writerId: answer.writerId, - content: answer.content, - authorNickname: answer.nickname, - authorGeneration: answer.writerGeneration, - publishedDate: answer.writeAt.ISO8601ToDate(.yearMonthDateTimeMilliseconds), - isReported: false, - isMine: answer.isMine, - isLiked: answer.isLiked, - isResignMember: answer.nickname == "알 수 없음", - commentCount: answer.commentCount, - heartCount: answer.heartCount - ) - } else if sum == popularSum { - let popularAnswerDate = popularAnswer.publishedDate - let answerDate = answer.writeAt.ISO8601ToDate(.yearMonthDateTimeMilliseconds) - - if answerDate > popularAnswerDate { - popularAnswer = .init( - id: answer.answerId, - writerId: answer.writerId, - content: answer.content, - authorNickname: answer.nickname, - authorGeneration: answer.writerGeneration, - publishedDate: answer.writeAt.ISO8601ToDate(.yearMonthDateTimeMilliseconds), - isReported: false, - isMine: answer.isMine, - isLiked: answer.isLiked, - isResignMember: answer.nickname == "알 수 없음", - commentCount: answer.commentCount, - heartCount: answer.heartCount - ) - } - } - } - - hasNext = answersOfQuestion.hasNext - threshold = Int(answersOfQuestion.threshold) + if question.id == mainQuestion.questionId { + return (nil, nil, false) } - return (popularAnswer, currentQuestion, false) + if let popularAnswer = try await getPopularQuestion(from: question) { + return (popularAnswer, question, true) + } else { + return (nil, question, true) + } }, postAnswer: { questionId, answer in let response = try await RepositoryService.shared.request { server, accessToken in @@ -282,6 +214,9 @@ extension AnswerRepository: DependencyKey { fetchAnswerListOfQuestion: { _, _ in (stubAnswerList, 25, .init(threshold: "", hasNext: false)) }, + fetchPopularAnswerOfMainQuestion: { + return (Answer.initialState, Question.initialState, true) + }, fetchPopularAnswer: { _ in let question = Question(id: 0, content: "", publishedDate: .now, isAnswered: false, isLived: true) @@ -302,6 +237,79 @@ extension DependencyValues { } } +// MARK: - Helper + +extension AnswerRepository { + + /// 질문에 따른 인기 답변을 계산합니다. + private static func getPopularQuestion(from question: Question) async throws -> Answer? { + var popularAnswer = Answer.initialState + var hasNext = true + var threshold: Int? + + while hasNext { + let answersOfQuestion = try await RepositoryService.shared.request { server, accessToken in + try await AnswerAPI.fetchListOfQuestion( + questionId: Int(question.id), + threshold: threshold, + pageSize: 30, + server: server, + accessToken: accessToken + ) + } + + if answersOfQuestion.content.isEmpty { + return nil + } + + for (index, answer) in answersOfQuestion.content.enumerated() { + guard index > 0 else { + popularAnswer = toEntity(answer) + continue + } + + if answer.isReported { continue } + + let sum = answer.commentCount + answer.heartCount + let popularSum = popularAnswer.heartCount + popularAnswer.commentCount + + if sum > popularSum { + popularAnswer = toEntity(answer) + } else if sum == popularSum { + let popularAnswerDate = popularAnswer.publishedDate + let answerDate = answer.writeAt.ISO8601ToDate(.yearMonthDateTimeMilliseconds) + + if answerDate < popularAnswerDate { + popularAnswer = toEntity(answer) + } + } + } + + hasNext = answersOfQuestion.hasNext + threshold = Int(answersOfQuestion.threshold) + } + + return popularAnswer + } + + private static func toEntity(_ dto: AnswerListOfQuestion.Content) -> Answer { + .init( + id: dto.answerId, + writerId: dto.writerId, + content: dto.content, + authorNickname: dto.nickname, + authorGeneration: dto.writerGeneration, + publishedDate: dto.writeAt.ISO8601ToDate(.yearMonthDateTimeMilliseconds), + isReported: false, + isMine: dto.isMine, + isLiked: dto.isLiked, + isResignMember: dto.nickname == "알 수 없음", + commentCount: dto.commentCount, + heartCount: dto.heartCount + ) + } +} + // MARK: - Stub extension AnswerRepository { diff --git a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-6.AnswerList/AnswerListFeature.swift b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-6.AnswerList/AnswerListFeature.swift index 0f809c60..642b9f01 100644 --- a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-6.AnswerList/AnswerListFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-6.AnswerList/AnswerListFeature.swift @@ -60,20 +60,10 @@ struct AnswerListFeature { let response = try await answerRepository.fetchAnswerListOfQuestion( question.id, nil ) - let currentHour = Calendar.current.component(.hour, from: .now) - if question.isLived, !(currentHour > 12 && currentHour < 19){ - if !(currentHour > 12 && currentHour < 19) { - let response = try await answerRepository.fetchPopularAnswer(question) - if let answer = response.0 { - await send(.fetchPopularAnswer(answer)) - } - } - } else { - let response = try await answerRepository.fetchPopularAnswer(question) - if let answer = response.0 { - await send(.fetchPopularAnswer(answer)) - } + let (answer, _, isEmpty) = try await answerRepository.fetchPopularAnswer(question) + if let popularAnswer = answer { + await send(.fetchPopularAnswer(popularAnswer)) } await send( diff --git a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift index 80d4f179..5940e1ee 100644 --- a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift @@ -64,7 +64,7 @@ struct BulletinBoardFeature { @Dependency(\.questionRepository.fetchMainQuestion) var fetchMainQuestion @Dependency(\.bulletinBoardRepository) var bulletinBoardRepository - @Dependency(\.answerRepository.fetchPopularAnswer) var fetchPopularAnswer + @Dependency(\.answerRepository) var answerRepository var body: some ReducerOf { Reduce { state, action in @@ -78,7 +78,7 @@ struct BulletinBoardFeature { do { let mainQuestion = try await fetchMainQuestion() let response = try await bulletinBoardRepository.fetchBulletinBoardList(nil) - let popularAnswer = try await fetchPopularAnswer(nil) + let popularAnswer = try await answerRepository.fetchPopularAnswerOfMainQuestion() await send(.fetchPopularAnswer(popularAnswer)) await send(.bulletinBoardListResponse(mainQuestion, response.0, response.1)) } catch { @@ -152,13 +152,12 @@ struct BulletinBoardFeature { } return .none - case let .fetchPopularAnswer(result): - if let answer = result.0, let question = result.1 { + case let .fetchPopularAnswer((answer, question, isEmpty)): + if let answer = answer, let question = question { state.popularAnswerStatus = .popularAnswer(answer, question) } else { - state.popularAnswerStatus = result.2 ? .none : .todayQuestion + state.popularAnswerStatus = isEmpty ? .none : .todayQuestion } - return .none case .searchButtonTapped: From 2d4a5860c43b46bceb2b777afe39d883c7085cf6 Mon Sep 17 00:00:00 2001 From: thinkyside Date: Tue, 17 Jun 2025 13:57:37 +0900 Subject: [PATCH 5/8] =?UTF-8?q?[#370]=20=EB=8B=B5=EB=B3=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=ED=9B=84=20=EA=B2=8C=EC=8B=9C=ED=8C=90=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EB=B2=84=ED=8A=BC=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Qapple/Qapple.xcodeproj/project.pbxproj | 8 +++---- .../Qapple/SourceCode/App/AppDelegate.swift | 4 ++-- .../Data/Repository/AnswerRepository.swift | 13 +++++----- .../Data/Repository/MemberRepository.swift | 24 +++++++------------ .../0-1.SignUpFlow/SignUpFlowFeature.swift | 17 +++++++------ .../3-1.BulletinBoard/BulletinBoardCell.swift | 16 ++++++++----- .../BulletinBoardFeature.swift | 8 +++---- .../SourceCode/UIComponent/QPAnswerCell.swift | 14 +++++++---- .../SourceCode/Utility/Pagination+.swift | 2 +- 9 files changed, 55 insertions(+), 51 deletions(-) diff --git a/Qapple/Qapple.xcodeproj/project.pbxproj b/Qapple/Qapple.xcodeproj/project.pbxproj index a04c4e7a..78fb6633 100644 --- a/Qapple/Qapple.xcodeproj/project.pbxproj +++ b/Qapple/Qapple.xcodeproj/project.pbxproj @@ -403,7 +403,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Qapple/QappleBox/Qapple.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2505142036; DEVELOPMENT_ASSET_PATHS = "\"Qapple/Resource/Preview Content\""; @@ -430,7 +430,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.qapple.Apple-Developer-Academy-POSTECH"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Qapple+Distribution"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = DevelopmentQapple2025; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; @@ -450,7 +450,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Qapple/QappleBox/Qapple.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2505142036; DEVELOPMENT_ASSET_PATHS = "\"Qapple/Resource/Preview Content\""; @@ -477,7 +477,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.qapple.Apple-Developer-Academy-POSTECH"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Qapple+Distribution"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = DevelopmentQapple2025; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; diff --git a/Qapple/Qapple/SourceCode/App/AppDelegate.swift b/Qapple/Qapple/SourceCode/App/AppDelegate.swift index 76524bb6..14034138 100644 --- a/Qapple/Qapple/SourceCode/App/AppDelegate.swift +++ b/Qapple/Qapple/SourceCode/App/AppDelegate.swift @@ -29,9 +29,9 @@ extension AppDelegate { didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool { #if DEBUG - RepositoryService.shared.configureServer(to: .test) + RepositoryService.shared.configureServer(to: .production) #else - RepositoryService.shared.configureServer(to: .test) + RepositoryService.shared.configureServer(to: .production) #endif setupPushNotification(application) setupFirebase() diff --git a/Qapple/Qapple/SourceCode/Data/Repository/AnswerRepository.swift b/Qapple/Qapple/SourceCode/Data/Repository/AnswerRepository.swift index 343a2160..0d23980f 100644 --- a/Qapple/Qapple/SourceCode/Data/Repository/AnswerRepository.swift +++ b/Qapple/Qapple/SourceCode/Data/Repository/AnswerRepository.swift @@ -17,7 +17,7 @@ struct AnswerRepository { QappleAPI.TotalCount, QappleAPI.PaginationInfo ) - var fetchPopularAnswerOfMainQuestion: () async throws -> (Answer?, Question?, Bool) + var fetchPopularAnswerOfMainQuestion: () async throws -> (Answer?, Question, Bool) var fetchPopularAnswer: (_ question: Question) async throws -> (Answer?, Question?, Bool) var postAnswer: (_ questionId: Int, _ answer: String) async throws -> Void var deleteAnswer: (_ answerId: Int) async throws -> Void @@ -117,14 +117,10 @@ extension AnswerRepository: DependencyKey { threshold: response.threshold, hasNext: response.hasNext ) + return (answerList, response.total, paginationInfo) }, fetchPopularAnswerOfMainQuestion: { - let currentHour = Calendar.current.component(.hour, from: .now) - if currentHour > 12 && currentHour < 19 { - return (nil, nil, false) - } - let mainQuestion = try await RepositoryService.shared.request { server, accessToken in try await QuestionAPI.fetchMainQuestion(server: server, accessToken: accessToken) } @@ -137,6 +133,11 @@ extension AnswerRepository: DependencyKey { isLived: mainQuestion.questionStatus == ("LIVE") ) + let currentHour = Calendar.current.component(.hour, from: .now) + if currentHour > 12 && currentHour < 19 { + return (nil, question, false) + } + if let popularAnswer = try await getPopularQuestion(from: question) { return (popularAnswer, question, true) } else { diff --git a/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift b/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift index 11fbda32..2f6cf8ec 100644 --- a/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift +++ b/Qapple/Qapple/SourceCode/Data/Repository/MemberRepository.swift @@ -31,22 +31,14 @@ extension MemberRepository: DependencyKey { static let liveValue = Self( signIn: { code in let deviceToken = try keychainService.fetchData(.deviceToken) - - print("code: \(code)") - print("deviceToken: \(deviceToken)") - - try keychainService.createData(.accessToken, "") - - return true - -// let response = try await MemberAPI.signIn( -// code: code, -// deviceToken: deviceToken, -// server: repositoryService.server -// ) -// try keychainService.createData(.accessToken, response.accessToken ?? "") -// try keychainService.createData(.refreshToken, response.refreshToken ?? "") -// return response.isMember + let response = try await MemberAPI.signIn( + code: code, + deviceToken: deviceToken, + server: repositoryService.server + ) + try keychainService.createData(.accessToken, response.accessToken ?? "") + try keychainService.createData(.refreshToken, response.refreshToken ?? "") + return response.isMember }, signUp: { email, nickname in let refreshToken = try keychainService.fetchData(.refreshToken) diff --git a/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift b/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift index 742ef513..8d460330 100644 --- a/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift @@ -45,13 +45,16 @@ struct SignUpFlowFeature { guard state.isFirstLaunch else { return .none } return .run { send in do { - let isRecentVersion = try await VersionService.isRecentVersion() - if isRecentVersion { - try await appleLoginService.autoLogin() - await send(.autoLoginResponse) - } else { - await send(.updateVersion) - } + try await appleLoginService.autoLogin() + await send(.autoLoginResponse) + +// let isRecentVersion = try await VersionService.isRecentVersion() +// if isRecentVersion { +// try await appleLoginService.autoLogin() +// await send(.autoLoginResponse) +// } else { +// await send(.updateVersion) +// } } catch { print(error) } diff --git a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardCell.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardCell.swift index 8fac5022..3bd87217 100644 --- a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardCell.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardCell.swift @@ -181,9 +181,11 @@ private struct RemoteView: View { HStack(spacing: 4) { Image(board.isLiked ? .heartActive : .heart) - Text("\(board.heartCount)") - .pretendard(.regular, 13) - .foregroundStyle(TextLabel.sub3) + if board.heartCount > 0 { + Text("\(board.heartCount)") + .pretendard(.regular, 13) + .foregroundStyle(TextLabel.sub3) + } } } @@ -193,9 +195,11 @@ private struct RemoteView: View { .frame(width: 15, height: 14) .foregroundStyle(TextLabel.sub3) - Text("\(board.commentCount)") - .pretendard(.regular, 13) - .foregroundStyle(TextLabel.sub3) + if board.commentCount > 0 { + Text("\(board.commentCount)") + .pretendard(.regular, 13) + .foregroundStyle(TextLabel.sub3) + } } } } diff --git a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift index 5940e1ee..4b141481 100644 --- a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift @@ -47,7 +47,7 @@ struct BulletinBoardFeature { case questionNotiTapped(Question) case popularAnswerTapped(Question) - case fetchPopularAnswer((Answer?, Question?, Bool)) + case fetchPopularAnswer((Answer?, Question, Bool)) case sheet(PresentationAction) case alert(PresentationAction) @@ -153,10 +153,10 @@ struct BulletinBoardFeature { return .none case let .fetchPopularAnswer((answer, question, isEmpty)): - if let answer = answer, let question = question { - state.popularAnswerStatus = .popularAnswer(answer, question) + if let popularAnswer = answer { + state.popularAnswerStatus = .popularAnswer(popularAnswer, question) } else { - state.popularAnswerStatus = isEmpty ? .none : .todayQuestion + state.popularAnswerStatus = question.isAnswered || isEmpty ? .none : .todayQuestion } return .none diff --git a/Qapple/Qapple/SourceCode/UIComponent/QPAnswerCell.swift b/Qapple/Qapple/SourceCode/UIComponent/QPAnswerCell.swift index 9c4c9f33..f56d8701 100644 --- a/Qapple/Qapple/SourceCode/UIComponent/QPAnswerCell.swift +++ b/Qapple/Qapple/SourceCode/UIComponent/QPAnswerCell.swift @@ -160,9 +160,11 @@ private struct NormalCell: View { .resizable() .frame(width: 18, height: 18) - Text("\(answer.heartCount)") - .pretendard(.regular, 13) - .foregroundStyle(.sub3) + if answer.heartCount > 0 { + Text("\(answer.heartCount)") + .pretendard(.regular, 13) + .foregroundStyle(.sub3) + } } } .padding(.leading, 8) @@ -175,8 +177,10 @@ private struct NormalCell: View { .resizable() .frame(width: 15, height: 14) - Text("\(answer.commentCount)") - .pretendard(.regular, 13) + if answer.commentCount > 0 { + Text("\(answer.commentCount)") + .pretendard(.regular, 13) + } } .foregroundStyle(.sub3) } diff --git a/Qapple/Qapple/SourceCode/Utility/Pagination+.swift b/Qapple/Qapple/SourceCode/Utility/Pagination+.swift index daebacfa..bd6fcd5a 100644 --- a/Qapple/Qapple/SourceCode/Utility/Pagination+.swift +++ b/Qapple/Qapple/SourceCode/Utility/Pagination+.swift @@ -16,7 +16,7 @@ struct ConfigurePagination: ViewModifier { func body(content: Content) -> some View { content.onAppear { - if currentIndex == list.endIndex - 10 && hasNext { + if currentIndex == list.endIndex - 1 && hasNext { pagination() } } From fd9c348ce83bfad21617675d749b7dd0d5d65b5c Mon Sep 17 00:00:00 2001 From: thinkyside Date: Sat, 21 Jun 2025 09:42:56 +0900 Subject: [PATCH 6/8] =?UTF-8?q?[#370]=20AnswerCommentList=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Repository/AnswerCommentRepository.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Qapple/Qapple/SourceCode/Data/Repository/AnswerCommentRepository.swift b/Qapple/Qapple/SourceCode/Data/Repository/AnswerCommentRepository.swift index 13f9a29e..ae180984 100644 --- a/Qapple/Qapple/SourceCode/Data/Repository/AnswerCommentRepository.swift +++ b/Qapple/Qapple/SourceCode/Data/Repository/AnswerCommentRepository.swift @@ -33,7 +33,7 @@ extension AnswerCommentRepository: DependencyKey { ) } - let list = response.answerCommentInfos.map { + let list = response.content.map { AnswerComment( id: $0.answerCommentId, writeId: $0.writerId, @@ -41,8 +41,8 @@ extension AnswerCommentRepository: DependencyKey { writerGeneration: "", content: $0.content, heartCount: $0.heartCount, - isLiked: false, - isMine: false, + isLiked: $0.isLiked, + isMine: $0.isMine, isReport: false, createdAt: $0.createdAt.ISO8601ToDate(.yearMonthDateTimeMilliseconds), anonymityId: -2 From 4cf29d25bfd5ea936ae35a3e6e49c84619206984 Mon Sep 17 00:00:00 2001 From: thinkyside Date: Sat, 21 Jun 2025 10:31:40 +0900 Subject: [PATCH 7/8] =?UTF-8?q?[#370]=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0-1.SignUpFlow/SignUpFlowFeature.swift | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift b/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift index 8d460330..d93cfe46 100644 --- a/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/0.SignUpFlow/0-1.SignUpFlow/SignUpFlowFeature.swift @@ -46,15 +46,14 @@ struct SignUpFlowFeature { return .run { send in do { try await appleLoginService.autoLogin() - await send(.autoLoginResponse) - -// let isRecentVersion = try await VersionService.isRecentVersion() -// if isRecentVersion { -// try await appleLoginService.autoLogin() -// await send(.autoLoginResponse) -// } else { -// await send(.updateVersion) -// } + await send(.autoLoginResponse) + let isRecentVersion = try await VersionService.isRecentVersion() + if isRecentVersion { + try await appleLoginService.autoLogin() + await send(.autoLoginResponse) + } else { + await send(.updateVersion) + } } catch { print(error) } From 8e174ad28578f07c9ee0b951281473fba82cc697 Mon Sep 17 00:00:00 2001 From: thinkyside Date: Tue, 1 Jul 2025 19:20:41 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[#370]=202.1.4=20=EB=B0=B0=ED=8F=AC=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Qapple/Qapple.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Qapple/Qapple.xcodeproj/project.pbxproj b/Qapple/Qapple.xcodeproj/project.pbxproj index 78fb6633..a04c4e7a 100644 --- a/Qapple/Qapple.xcodeproj/project.pbxproj +++ b/Qapple/Qapple.xcodeproj/project.pbxproj @@ -403,7 +403,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Qapple/QappleBox/Qapple.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2505142036; DEVELOPMENT_ASSET_PATHS = "\"Qapple/Resource/Preview Content\""; @@ -430,7 +430,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.qapple.Apple-Developer-Academy-POSTECH"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = DevelopmentQapple2025; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Qapple+Distribution"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; @@ -450,7 +450,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Qapple/QappleBox/Qapple.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 2505142036; DEVELOPMENT_ASSET_PATHS = "\"Qapple/Resource/Preview Content\""; @@ -477,7 +477,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.qapple.Apple-Developer-Academy-POSTECH"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = DevelopmentQapple2025; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Qapple+Distribution"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;