Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Qapple/Qapple/Resource/Constant/Constant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ enum Constant {

extension Constant {
static let isSignIn = "isSignIn"
static let recentQuestionID = "recentQuestionID"
static let userRandomID = "userRandomID"
}
6 changes: 3 additions & 3 deletions Qapple/Qapple/SourceCode/Entity/QuestionState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ enum QuestionState: Codable {
/// 질문 및 답변 여부에 따라 변화하는 버튼 문자열
func buttonTitle(isAnswerd: Bool) -> String {
switch self {
case .creating: isAnswerd ? "다른 답변 둘러보기" : "이전 질문 답변하기"
case .ready: "질문에 답변하기"
case .complete: "다른 답변 둘러보기"
case .creating: isAnswerd ? "둘러보기" : "답변하기"
case .ready: "답변하기"
case .complete: "둘러보기"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ struct TodayQuestionFeature {
var timeRemainingForQuestion: TimeInterval = 0
var isLoading = true
var isFirstLaunch = true
var isNewQuestion = false
@Presents var sheet: Sheet.State?
@Presents var alert: AlertState<Action.Alert>?
@Shared(.appStorage(Constant.recentQuestionID)) var recentQuestionID = 0
Comment on lines +22 to +25
Copy link
Contributor Author

@thinkySide thinkySide Apr 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isNewQuestion: 새로운 질문이 등록되었는지 여부를 확인
recentQuestionID: 가장 최근에 등록됐었던 질문의 ID

recentQuestionID의 경우 AppStorge에 저장함으로써 N번째 진입 시 SmallHeaderView를 보여줄 수 있도록 구현했습니다.

}

enum Action {
Expand Down Expand Up @@ -79,6 +81,14 @@ struct TodayQuestionFeature {
case let .mainQuestionResponse(mainQuestion):
state.isFirstLaunch = false
state.todayQuestion = mainQuestion

if mainQuestion.id != state.recentQuestionID {
state.isNewQuestion = true
state.$recentQuestionID.withLock { $0 = mainQuestion.id }
} else {
state.isNewQuestion = false
}
Comment on lines +85 to +90
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메인 질문을 받아올 때 저장되어있던 Question ID값과 비교 후 새롭게 등록된 질문인지 판단 + 업데이트합니다.


if isQuestionLiveTime {
state.questionState = mainQuestion.isAnswered ? .complete : .ready
return .none
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@ struct TodayQuestionView: View {
@Bindable var store: StoreOf<TodayQuestionFeature>

var body: some View {
ZStack {
Color.second.ignoresSafeArea()

ScrollView {
VStack(spacing: 0) {
HeaderView(store: store)
QuestionButton(store: store)
AnswerPreviewList(store: store)
ScrollView {
VStack(spacing: 0) {
if store.isNewQuestion {
LargeHeaderView(store: store)
LargeQuestionButton(store: store)
} else {
SmallHeaderView(store: store)
}
AnswerPreviewList(store: store)
}
Comment on lines +16 to 25
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

새롭게 등록됐으면 LargeHeader, 이미 봤던 질문이라면 SmallHeader

.scrollIndicators(.hidden)
}
.background(.second)
.scrollIndicators(.hidden)
.onAppear {
store.send(.onAppear)
}
Expand All @@ -36,20 +37,98 @@ struct TodayQuestionView: View {
}
.loadingIndicator(isLoading: store.isLoading)
.alert($store.scope(state: \.alert, action: \.alert))
.sheet(item: $store.scope(
state: \.sheet,
action: \.sheet)
) { store in
.sheet(item: $store.scope(state: \.sheet, action: \.sheet)) { store in
switch store.case {
case let .seeMore(store): SeeMoreSheet(store: store)
}
}
}
}

// MARK: - HeaderView
// MARK: - SmallHeaderView

private struct SmallHeaderView: View {

let store: StoreOf<TodayQuestionFeature>

var body: some View {
ZStack {
Color.first.ignoresSafeArea()

HStack(spacing: 8) {
Image(store.questionState.graphicImage)
.resizable()
.scaledToFill()
.frame(width: 40, height: 40)

Text(store.questionState.mainTitle)
.font(.pretendard(.semiBold, size: 18))
.foregroundStyle(.wh)
.tracking(-1)

Spacer()

if store.questionState == .creating {
QuestionTimer()
} else {
AnsweringButton()
}
}
.padding(.horizontal, 24)
.frame(maxWidth: .infinity)
.frame(height: 90)
.background(.second)
.cornerRadius(32, corners: [.bottomLeft, .bottomRight])
}
}

/// 질문 타이머
private func QuestionTimer() -> some View {
Text(store.timeRemainingForQuestion.timerFormat)
.font(.pretendard(.bold, size: 22))
.foregroundStyle(LinearGradient.timer)
.monospacedDigit()
.kerning(-2)
}

/// 답변하기 버튼
private func AnsweringButton() -> some View {
Button {
store.send(.questionButtonTapped(store.todayQuestion))
} label: {
Text(title)
.font(.pretendard(.semiBold, size: 15))
.frame(width: 84)
.padding(.vertical, 10)
.foregroundStyle(.main)
.background(backgroundColor)
.clipShape(RoundedRectangle(cornerRadius: 20))
}
.opacity(store.isLoading ? 0 : 1)
.buttonStyle(ScalableButtonStyle())
}

/// 버튼 제목
private var title: String {
store.questionState.buttonTitle(
isAnswerd: store.todayQuestion.isAnswered
)
}

/// 버튼 배경 색상
private var backgroundColor: Color {
switch store.questionState {
case .creating:
store.todayQuestion.isAnswered ? .secondaryButton : .button
case .ready: .button
case .complete: .secondaryButton
}
}
}

// MARK: - LargeHeaderView

private struct HeaderView: View {
private struct LargeHeaderView: View {

@State private var offsetY: CGFloat = 0

Expand Down Expand Up @@ -93,9 +172,9 @@ private struct HeaderView: View {
}
}

// MARK: - QuestionButton
// MARK: - LargeQuestionButton

private struct QuestionButton: View {
private struct LargeQuestionButton: View {

let store: StoreOf<TodayQuestionFeature>

Expand Down Expand Up @@ -141,7 +220,6 @@ private struct QuestionButton: View {
}
}
}

// MARK: - AnswerPreviewList

private struct AnswerPreviewList: View {
Expand All @@ -165,7 +243,7 @@ private struct AnswerPreviewList: View {
if store.answerPreviewList.isEmpty {
Spacer()

Text("아직 답변이 달리지않았어요\n첫 답변을 달아보세요!")
Text(" 답변을 달아보세요!")
.font(.pretendard(.semiBold, size: 14))
.foregroundStyle(.sub4)
.multilineTextAlignment(.center)
Expand Down Expand Up @@ -221,6 +299,12 @@ private struct AnswerPreviewList: View {
state: .normal,
seeMoreAction: {
store.send(.seeMoreAnswerButtonTapped(answer))
},
likeAction: {

},
commentAction: {

}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ private struct AnswerList: View {
state: .normal,
seeMoreAction: {
store.send(.seeMoreAction(answer))
},
likeAction: {

},
commentAction: {

}
)
.configurePagination(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ private struct MyAnswerList: View {
state: .written,
seeMoreAction:{
store.send(.seeMoreAction(answer))
},
likeAction: {

},
commentAction: {

}
)
.configurePagination(
Expand Down
Loading