From 8fbbb44427b85a028b15d7352fbc5d359283be9c Mon Sep 17 00:00:00 2001 From: omj0722 Date: Tue, 15 Apr 2025 12:56:34 +0900 Subject: [PATCH] =?UTF-8?q?[#361]=20=EA=B2=8C=EC=8B=9C=ED=8C=90=20UI=20?= =?UTF-8?q?=EC=97=85=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 | 14 +++- .../questionNoti.colorset/Contents.json | 20 +++++ .../Qapple/SourceCode/App/AppDelegate.swift | 2 +- .../Feature/1.MainFlow/MainFlowFeature.swift | 11 +++ .../3-1.BulletinBoard/BulletinBoardCell.swift | 12 +-- .../BulletinBoardFeature.swift | 18 ++++- .../3-1.BulletinBoard/BulletinBoardView.swift | 76 +++++++++++++++++++ .../UIComponent/QPAcademyDayCounter.swift | 30 ++++---- 8 files changed, 154 insertions(+), 29 deletions(-) create mode 100644 Qapple/Qapple/Resource/Color/Colors.xcassets/Grayscale/questionNoti.colorset/Contents.json diff --git a/Qapple/Qapple.xcodeproj/project.pbxproj b/Qapple/Qapple.xcodeproj/project.pbxproj index 8a9ed6f6..e18952ee 100644 --- a/Qapple/Qapple.xcodeproj/project.pbxproj +++ b/Qapple/Qapple.xcodeproj/project.pbxproj @@ -230,14 +230,17 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 7YZM4XMXXU; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = ""; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 18.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.thinkyside.QappleTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ""; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -249,14 +252,17 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 7YZM4XMXXU; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = ""; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 18.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.thinkyside.QappleTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ""; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/Qapple/Qapple/Resource/Color/Colors.xcassets/Grayscale/questionNoti.colorset/Contents.json b/Qapple/Qapple/Resource/Color/Colors.xcassets/Grayscale/questionNoti.colorset/Contents.json new file mode 100644 index 00000000..05b659e1 --- /dev/null +++ b/Qapple/Qapple/Resource/Color/Colors.xcassets/Grayscale/questionNoti.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x25", + "green" : "0x25", + "red" : "0x25" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Qapple/Qapple/SourceCode/App/AppDelegate.swift b/Qapple/Qapple/SourceCode/App/AppDelegate.swift index 14034138..2d0f5575 100644 --- a/Qapple/Qapple/SourceCode/App/AppDelegate.swift +++ b/Qapple/Qapple/SourceCode/App/AppDelegate.swift @@ -29,7 +29,7 @@ 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) #endif diff --git a/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowFeature.swift b/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowFeature.swift index 4e7fd5f2..318943f2 100644 --- a/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowFeature.swift @@ -90,9 +90,20 @@ struct MainFlowFeature { state.path.append(.bulletinBoardPost(.init())) return .none + case let .bulletinBoardTab(.questionNotiTapped(question)): + state.path.append(.writeAnswer(.init(question: question))) + return .none + + case let .bulletinBoardTab(.popularAnswerTapped(question)): + if question.isAnswered { + state.path.append(.answerList(.init(question: question))) + } + return .none + case let .bulletinBoardTab(.sheet(.presented(.seeMore(.reportButtonTapped(dataType))))): state.path.append(.report(.init(dataType: dataType))) return .none + case let .profileTab(.editProfileButtonTapped(nickname)): state.path.append(.profileEdit(.init(nickname: nickname, defaultNickname: nickname))) return .none 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 e5f7c062..142f4afd 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 @@ -53,18 +53,18 @@ private struct NormalBoardCell: View { board: board, seeMore: seeMore ) - .padding(.horizontal, 16) + .padding(.top) + .padding(.horizontal) ContentView( board: board, like: like ) - .padding(.horizontal, 16) + .padding(.horizontal, 16) Divider() .padding(.top, 16) } - .padding(.top, 16) .background(Background.first) } } @@ -115,7 +115,7 @@ private struct HeaderView: View { .padding(.leading, 8) Text("\(board.createAt.timeAgo)") - .pretendard(.regular, 14) + .pretendard(.regular, 12) .foregroundStyle(TextLabel.sub4) .padding(.leading, 6) @@ -158,8 +158,8 @@ private struct ContentView: View { board: board, like: like ) - .padding(.top, 12) - .disabled(board.isReported) + .padding(.top, 12) + .disabled(board.isReported) } } } 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 fc555250..7a68c4da 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 @@ -15,6 +15,7 @@ struct BulletinBoardFeature { @Presents var sheet: Sheet.State? @Presents var alert: AlertState? var bulletinBoardList: [BulletinBoard] = [] + var todayQuestion: Question = .initialState var paginationInfo = QappleAPI.PaginationInfo(threshold: "", hasNext: false) var isLoading: Bool = false var isFirstLaunch = true @@ -24,7 +25,7 @@ struct BulletinBoardFeature { case onAppear case refresh case pagination - case bulletinBoardListResponse([BulletinBoard], QappleAPI.PaginationInfo) + case bulletinBoardListResponse(Question, [BulletinBoard], QappleAPI.PaginationInfo) case paginationResponse([BulletinBoard], QappleAPI.PaginationInfo) case academyDayCounterTapped @@ -40,6 +41,8 @@ struct BulletinBoardFeature { case networkingFailed(Error) case toggleLoading(Bool) case filterBlockedUser + case questionNotiTapped(Question) + case popularAnswerTapped(Question) case sheet(PresentationAction) case alert(PresentationAction) @@ -54,6 +57,7 @@ struct BulletinBoardFeature { } } + @Dependency(\.questionRepository.fetchMainQuestion) var fetchMainQuestion @Dependency(\.bulletinBoardRepository) var bulletinBoardRepository var body: some ReducerOf { @@ -63,8 +67,9 @@ struct BulletinBoardFeature { return .run { [isFirstLaunch = state.isFirstLaunch] send in if isFirstLaunch { await send(.toggleLoading(true), animation: .bouncy) } do { + let mainQuestion = try await fetchMainQuestion() let response = try await bulletinBoardRepository.fetchBulletinBoardList(nil) - await send(.bulletinBoardListResponse(response.0, response.1)) + await send(.bulletinBoardListResponse(mainQuestion, response.0, response.1)) } catch { await send(.networkingFailed(error)) } @@ -83,7 +88,8 @@ struct BulletinBoardFeature { await send(.toggleLoading(false), animation: .bouncy) } - case let .bulletinBoardListResponse(bulletinBoardList, paginationInfo): + case let .bulletinBoardListResponse(mainQuestion, bulletinBoardList, paginationInfo): + state.todayQuestion = mainQuestion state.isFirstLaunch = false state.bulletinBoardList = bulletinBoardList.filter(UserDefaults.filterBoardBlockedUser) state.paginationInfo = paginationInfo @@ -144,6 +150,12 @@ struct BulletinBoardFeature { case .postBoardButtonTapped: return .none + case .questionNotiTapped: + return .none + + case .popularAnswerTapped: + return .none + case let .seeMoreAction(board): state.sheet = .seeMore( .init( 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 7fe23d9a..7c57d84d 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 @@ -82,6 +82,77 @@ private struct BulletinBoardContentView: View { } } +// MARK: - QuestionNotificationView + +private struct QuestionNotificationView: View { + + let store: StoreOf + + var body: some View { + if !store.todayQuestion.isAnswered { + Button { + store.send(.questionNotiTapped(store.todayQuestion)) + } label: { + HStack(spacing: 0) { + Image("questionReady") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing, 6) + .padding(.leading, 18) + + Text("오늘의 질문이 도착했어요!") + .font(.pretendard(.semiBold, size: 15)) + .foregroundStyle(.white) + + Spacer() + } + .frame(width: 361, height: 47) + .background(RoundedRectangle(cornerRadius: 12) + .fill(.questionNoti) + .stroke(.button.opacity(0.17), lineWidth: 0.6)) // TODO: 그라데이션 + } + } else { + Button { + store.send(.popularAnswerTapped(store.todayQuestion)) + } label: { + HStack(spacing: 0) { + VStack(spacing: 0) { + Image("questionComplete") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing, 8) + .padding(.leading, 18) + + Spacer() + } + + + VStack(alignment: .leading, spacing: 0) { + Text("오늘의 인기 답변") + .font(.pretendard(.regular, size: 12)) + .foregroundStyle(TextLabel.sub4) + + Spacer() + + Text("프라이데이는 여자친구가 가지고 싶어요") // TODO: 인기 답변으로 + .font(.pretendard(.regular, size: 15)) + .foregroundStyle(.white) + } + + Spacer() + } + .padding(.vertical, 12) + .frame(width: 361, height: 67) + .background(RoundedRectangle(cornerRadius: 12) + .fill(.questionNoti) + .stroke(.button.opacity(0.17), lineWidth: 0.6)) // TODO: 그라데이션 + } + } + } +} + // MARK: - BulletionBoardListView private struct BulletionBoardListView: View { @@ -90,6 +161,11 @@ private struct BulletionBoardListView: View { var body: some View { ScrollView { + + QuestionNotificationView(store: store) + .padding(.horizontal) + .padding(.top, 2) + LazyVStack(spacing: 0) { ForEach(enumerated(store.bulletinBoardList), id: \.offset) { index, board in Button { diff --git a/Qapple/Qapple/SourceCode/UIComponent/QPAcademyDayCounter.swift b/Qapple/Qapple/SourceCode/UIComponent/QPAcademyDayCounter.swift index f9f32a2f..f7794f5b 100644 --- a/Qapple/Qapple/SourceCode/UIComponent/QPAcademyDayCounter.swift +++ b/Qapple/Qapple/SourceCode/UIComponent/QPAcademyDayCounter.swift @@ -26,7 +26,7 @@ struct QPAcademyDayCounter: View { } } } - .padding(.vertical, 16) + .padding(.vertical, 14) .padding(.horizontal, 20) .background(.second) .clipShape(RoundedRectangle(cornerRadius: 12)) @@ -44,21 +44,21 @@ private struct ContentView: View { VStack(spacing: 0) { Header(event: event, dayLeft: dayLeft, isCurrentEvent: true) - HStack { - Text(event.period.0.formatting(.mdKorean)) - Spacer() - Text(event.period.1.formatting(.mdKorean)) - } - .padding(.top, 16) - .padding(.horizontal, 2) - .foregroundStyle(.main).opacity(0.6) - .pretendard(.semiBold, 14) +// HStack { +// Text(event.period.0.formatting(.mdKorean)) +// Spacer() +// Text(event.period.1.formatting(.mdKorean)) +// } +// .padding(.top, 16) +// .padding(.horizontal, 2) +// .foregroundStyle(.main).opacity(0.6) +// .pretendard(.semiBold, 14) - ProgressBar( - event: event, - dayLeft: dayLeft - ) - .padding(.top, 8) +// ProgressBar( +// event: event, +// dayLeft: dayLeft +// ) +// .padding(.top, 8) } } }