Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/#62] 퀴즈 풀기 화면 수정 #63

Merged
merged 10 commits into from
Nov 21, 2023
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
41 changes: 40 additions & 1 deletion Targets/D3N/Sources/Domain/News/Entity/NewsEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,45 @@ public struct NewsEntity: Equatable {
self.mediaCompanyName = mediaCompanyName

//FIXME: 풀었던 뉴스 아이디 저장 로직 내부 구현
// self.isAlreadySolved = LocalStorageRepository.loadAlreadySolvedNewsIds().contains(id)
// self.isAlreadySolved = LocalStorageRepository.loadAlreadySolvedNewsIds().contains(id)
}
}

public extension NewsEntity {
static let mock = NewsEntity(
id: 11605391,
field: .society,
type: .koreanNews,
title: "강원도 임금근로자 2명 중 1명 ‘비정규직’…역대 최고치",
summary: "[앵커] 강원도 내 임금근로자 2명 가운데 한 명은 비정규직이라는 분석이 나왔습니다. 강원도 내 비정규직 비율이 계속 늘어 처음으로 50%를 넘긴 건데요. 경기침체가 길어지는 만큼, 좋은 일자리 찾기가 더 어려워질거란...",
url: "https://n.news.naver.com/mnews/article/056/0011605391?sid=101",
mediaCompanyId: "056",
mediaCompanyLogo: "https://mimgnews.pstatic.net/image/upload/office_logo/056/2021/07/15/logo_056_57_20210715101841.png",
mediaCompanyName: "KBS"
)

static let mocks = [
NewsEntity(
id: 5622923,
field: .society,
type: .koreanNews,
title: "[사설]미적대는 의대 입학정원 수요조사 발표, 이유 뭔가",
summary: "정부가 의과대학 입학정원 수요 조사 결과 발표를 두 차례나 연기하며 주저하는 모습을 보이고 있다. 보건복지부는 의대 정원 수요 조사 결과를 지난 13일 발표하겠다더니 돌연 연기한 데 이어 늦어도 17일까지는...",
url: "https://n.news.naver.com/mnews/article/018/0005622923?sid=110",
mediaCompanyId: "018",
mediaCompanyLogo: "https://mimgnews.pstatic.net/image/upload/office_logo/018/2018/08/08/logo_018_57_20180808174308.png",
mediaCompanyName: "이데일리"
),
NewsEntity(
id: 11605391,
field: .society,
type: .koreanNews,
title: "강원도 임금근로자 2명 중 1명 ‘비정규직’…역대 최고치",
summary: "[앵커] 강원도 내 임금근로자 2명 가운데 한 명은 비정규직이라는 분석이 나왔습니다. 강원도 내 비정규직 비율이 계속 늘어 처음으로 50%를 넘긴 건데요. 경기침체가 길어지는 만큼, 좋은 일자리 찾기가 더 어려워질거란...",
url: "https://n.news.naver.com/mnews/article/056/0011605391?sid=101",
mediaCompanyId: "056",
mediaCompanyLogo: "https://mimgnews.pstatic.net/image/upload/office_logo/056/2021/07/15/logo_056_57_20210715101841.png",
mediaCompanyName: "KBS"
)
]
}
4 changes: 0 additions & 4 deletions Targets/D3N/Sources/Domain/News/NewsClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ import Foundation
import ComposableArchitecture
import Moya

public enum NewsError: Error, Equatable {
case no
}

struct NewsClient {
var fetchNewsList: (Int, Int) async -> Result<[NewsEntity], D3NAPIError>
var fetchQuizList: (Int) async -> Result<[QuizEntity], D3NAPIError>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// SubmitQuizListRequest.swift
// D3N
//
// Created by 송영모 on 11/20/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

public struct SubmitQuizListRequestElement: Codable {
let quizId, selectedAnswer: Int

public init(quizId: Int, selectedAnswer: Int) {
self.quizId = quizId
self.selectedAnswer = selectedAnswer
}
}

public typealias SubmitQuizListRequestDTO = [SubmitQuizListRequestElement]
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// SubmitQuizListResponse.swift
// D3N
//
// Created by 송영모 on 11/20/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

public struct SubmitQuizListResponseElement: Codable {
let quizId: Int
}

public typealias SubmitQuizListResponseDTO = [SubmitQuizListResponseElement]
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ public struct QuizEntity: Equatable {
let answer: Int
let reason: String

let secondTime: Int = 70

Comment on lines +18 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

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

여기서 secondTime = 70이라는 변수의 의미가 무엇인가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

임시로 넣은 값입니다!

var userAnswer: Int? = nil
}
9 changes: 9 additions & 0 deletions Targets/D3N/Sources/Domain/Quiz/Model/QuizModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//
// QuizModel.swift
// D3N
//
// Created by 송영모 on 11/20/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation
55 changes: 55 additions & 0 deletions Targets/D3N/Sources/Domain/Quiz/QuizClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// QuizClient.swift
// D3N
//
// Created by 송영모 on 11/20/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

import ComposableArchitecture
import Moya

struct QuizClient {
var fetch: (Int) async -> Result<[QuizEntity], D3NAPIError>
var submit: ([QuizEntity]) async -> Result<[Int], D3NAPIError>
}

extension QuizClient: TestDependencyKey {
static let previewValue = Self(
fetch: { _ in .failure(.none) },
submit: { _ in .failure(.none) }
)

static let testValue = Self(
fetch: unimplemented("\(Self.self).fetch"),
submit: unimplemented("\(Self.self).submit")
)
}

extension DependencyValues {
var quizClient: QuizClient {
get { self[QuizClient.self] }
set { self[QuizClient.self] = newValue }
}
}

extension QuizClient: DependencyKey {
static let liveValue = QuizClient(
fetch: { newsId in
let target: TargetType = NewsService.fetchQuizList(newsId: newsId)
let response: Result<FetchQuizListResponseDTO, D3NAPIError> = await D3NAPIkProvider.reqeust(target: target)

return response.map { $0.map { $0.toEntity() } }
},
submit: { quizs in
let target: TargetType = QuizService.submit(quizs: quizs)
let response: Result<SubmitQuizListResponseDTO, D3NAPIError> = await D3NAPIkProvider.reqeust(target: target)

return response.map {
$0.map { $0.quizId }
}
}
)
}
52 changes: 52 additions & 0 deletions Targets/D3N/Sources/Domain/Quiz/QuizService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// QuizService.swift
// D3N
//
// Created by 송영모 on 11/20/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

import Moya

public enum QuizService {
case fetch(newsId: Int)
case submit(quizs: [QuizEntity])
}

extension QuizService: TargetType {
public var baseURL: URL { URL(string: Environment.baseURL)! }

public var path: String {
switch self {
case .fetch: return "quiz/list"
case .submit: return "quiz/list/submit"
}
}

public var method: Moya.Method {
switch self {
case .fetch: return .get
case .submit: return .post
}
}

public var task: Task {
switch self {
case let .fetch(newsId: id):
return .requestParameters(parameters: ["newsId": id], encoding: URLEncoding.queryString)
case let .submit(quizs):
let dto: SubmitQuizListRequestDTO = quizs.compactMap {
if let userAnswer = $0.userAnswer {
return .init(quizId: $0.id, selectedAnswer: userAnswer)
} else {
return nil
}
}
return .requestJSONEncodable(dto)
}
}

public var headers: [String: String]? { nil }
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ struct OnboardingBirthView: View {

Spacer()

D3NSubmitButton(activeTitle: "선택 완료", inactiveTitle: "생일을 선택해주세요", isActive: viewStore.birthDate != nil) { _ in
D3NSubmitButton(
activeTitle: "선택 완료",
inactiveTitle: "생일을 선택해주세요",
isActive: viewStore.birthDate != nil
) {
viewStore.send(.comfirmButtonTapped)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct OnboardingNewsFieldView: View {
activeTitle: "이제 끝이에요!",
inactiveTitle: "선택해주세요",
isActive: !viewStore.state.newsFields.isEmpty
) { _ in
) {
viewStore.send(.submitButtonTapped)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public struct OnboardingNicknameStore: Reducer {
return .none

case .confirmButtonTapped:
if !state.nickname.isEmpty {
if state.isConfirmButtonActive {
state.focus = nil
return .send(.delegate(.submit(state.nickname)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,8 @@ struct OnboardingNicknameView: View {
activeTitle: "다음으로",
inactiveTitle: "닉네임을 입력해주세요",
isActive: viewStore.state.isConfirmButtonActive,
action: { isActive in
if isActive {
viewStore.send(.confirmButtonTapped)
}
action: {
viewStore.send(.confirmButtonTapped)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,41 @@ import ComposableArchitecture

public struct QuizListItemCellStore: Reducer {
public struct State: Equatable, Identifiable {
public var id: UUID
public var quizEntity: QuizEntity
public var id: Int

var choiceListItems: IdentifiedArrayOf<ChoiceListItemCellStore.State> = []
var question: String
var choices: [String]
var answer: Int
var reason: String
var userAnswer: Int?

public init(
id: UUID = .init(),
quizEntity: QuizEntity
init(
id: Int = .init(),
question: String,
choices: [String],
answer: Int,
reason: String,
userAnswer: Int? = nil
) {
self.id = id
self.quizEntity = quizEntity
self.choiceListItems = .init(
uniqueElements: quizEntity.choiceList.map { choice in
return .init(choice: choice)
}
)
self.question = question
self.choices = choices
self.answer = answer
self.reason = reason
self.userAnswer = userAnswer
}
}

public enum Action: Equatable {
case onAppear

case tapped
case answered(Int)
case submitButtonTappped

case delegate(Delegate)
case choiceListItems(id: ChoiceListItemCellStore.State.ID, action: ChoiceListItemCellStore.Action)

public enum Delegate: Equatable {
case userAnswered(Int)
case submit(Int)
}
}

Expand All @@ -50,25 +56,23 @@ public struct QuizListItemCellStore: Reducer {
case .onAppear:
return .none

case let .choiceListItems(id: id, action: .delegate(action)):
switch action {
case .tapped:
for id in state.choiceListItems.ids {
state.choiceListItems[id: id]?.isSelected = false
}
state.choiceListItems[id: id]?.isSelected = true
if let index = state.choiceListItems.index(id: id) {
return .send(.delegate(.userAnswered(index)))
}
return .none
case let .answered(userAnswer):
if state.userAnswer == userAnswer {
state.userAnswer = nil
} else {
state.userAnswer = userAnswer
}
return .none

case .submitButtonTappped:
if let userAnswer = state.userAnswer {
return .send(.delegate(.submit(userAnswer)))
}
return .none

default:
return .none
}
}
.forEach(\.choiceListItems, action: /Action.choiceListItems(id:action:)) {
ChoiceListItemCellStore()
}
}
}
Loading