From f2c81df3b79be5a3a385e80acdc5e56e979c7a1e Mon Sep 17 00:00:00 2001 From: Roy Date: Wed, 28 Oct 2026 22:34:36 +0900 Subject: [PATCH 01/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=A4=91?= =?UTF-8?q?=EA=B0=84=20=ED=88=AC=ED=91=9C=20API=EC=99=80=20=EB=B6=84?= =?UTF-8?q?=EA=B8=B0=20=EC=9E=AC=EC=83=9D=20=ED=9D=90=EB=A6=84=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서버 중간 투표 계약과 클래스 생성 요청 키를 반영하고, 투표 복구·결과 매핑·오디오 분기 테스트를 추가한다. --- .../Tests/Sources/ClassCoordinatorTests.swift | 8 + .../Model/DTO/BattleScenarioDataDTO.swift | 6 +- .../Model/Mapper/BattleScenarioDataDTO+.swift | 15 +- .../Tests/Sources/BattleDomainTests.swift | 22 + .../AIFeedback/ClassAIPersonalFeedback.swift | 3 + .../Entity/Battle/ClassBattleSession.swift | 5 +- .../Entity/Error/ClassBattleError.swift | 10 + .../Sources/Entity/Error/ClassError.swift | 2 + .../Result/ClassResultMidVoteCheckpoint.swift | 15 + .../Result/ClassResultMidVoteOption.swift | 18 + .../Entity/Result/ClassResultStats.swift | 9 + .../Sources/Entity/Room/ClassCreation.swift | 9 +- .../Entity/Vote/ClassMidVoteChoice.swift | 15 + .../Entity/Vote/ClassVoteHistory.swift | 3 + .../Battle/ClassBattleInterface.swift | 1 + .../MockClassRepository+Features.swift | 8 + .../Model/DTO/ClassAIFeedbackDTO.swift | 1 + .../Model/DTO/ClassBattleSessionDTO.swift | 1 + .../Sources/Model/DTO/ClassResultDTO.swift | 16 + .../Sources/Model/DTO/ClassVoteDTO.swift | 7 + .../Model/Mapper/ClassAIFeedbackDTO+.swift | 3 + .../Model/Mapper/ClassBattleSessionDTO+.swift | 3 +- .../Model/Mapper/ClassResultDTO+.swift | 24 + .../Sources/Model/Mapper/ClassVoteDTO+.swift | 11 + .../Sources/Repository/ClassErrorMapper.swift | 1 + .../ClassRepositoryImpl+Battle.swift | 19 + .../UseCase/ClassUseCaseImpl+Battle.swift | 8 + .../Sources/ClassBattleAPIContractTests.swift | 9 +- .../Sources/ClassErrorMappingTests.swift | 1 + .../Sources/ClassRecommendationTests.swift | 4 +- .../Sources/ClassResultAPIContractTests.swift | 35 ++ .../Sources/Scenario/RecommendedPathKey.swift | 2 + .../Scenario/ScenarioInteractiveOption.swift | 8 +- .../Sources/BattleScenarioTimelineTests.swift | 5 + .../Model/ClassBattleTranscriptLine.swift | 15 +- .../Reducer/ClassBattleFlowFeature.swift | 358 ++++++++++++++- .../View/ClassBattleFlowView.swift | 58 ++- .../Reducer/ClassSettingFeature.swift | 23 +- .../Tests/Sources/ClassAIVoteTests.swift | 8 + .../Tests/Sources/ClassBattleFlowTests.swift | 416 +++++++++++++++++- .../Tests/Sources/ClassSettingTests.swift | 55 +++ .../Class/Tests/Sources/MyClassTests.swift | 25 ++ .../Sources/Class/Battle/ClassBattleAPI.swift | 2 + .../Class/Battle/ClassBattleService.swift | 6 +- .../Battle/Request/ClassBattleRequests.swift | 5 + .../Room/Request/ClassroomRequests.swift | 2 + 46 files changed, 1229 insertions(+), 51 deletions(-) create mode 100644 Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultMidVoteCheckpoint.swift create mode 100644 Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultMidVoteOption.swift create mode 100644 Projects/Domain/ClassDomain/Interface/Sources/Entity/Vote/ClassMidVoteChoice.swift diff --git a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift index dae174ab..743454df 100644 --- a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift +++ b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift @@ -2777,6 +2777,14 @@ private struct SlowBattleUseCase: ClassBattleInterface { try await fallback.submitPreVote(classID: classID, optionID: optionID) } + func submitMidVote( + classID: Int, + checkpointID: Int, + optionID: Int + ) async throws -> ClassVoteReceipt { + try await fallback.submitMidVote(classID: classID, checkpointID: checkpointID, optionID: optionID) + } + func completeTTS(classID _: Int) async throws { await tracker.markStarted() do { diff --git a/Projects/Domain/BattleDomain/Sources/Model/DTO/BattleScenarioDataDTO.swift b/Projects/Domain/BattleDomain/Sources/Model/DTO/BattleScenarioDataDTO.swift index 09f251cc..478ab7ac 100644 --- a/Projects/Domain/BattleDomain/Sources/Model/DTO/BattleScenarioDataDTO.swift +++ b/Projects/Domain/BattleDomain/Sources/Model/DTO/BattleScenarioDataDTO.swift @@ -78,6 +78,8 @@ public struct ScenarioScriptDTO: Decodable { } public struct ScenarioInteractiveOptionDTO: Decodable { - public let label: String - public let nextNodeId: Int + public let optionId: Int? + public let label: String? + public let title: String? + public let nextNodeId: Int? } diff --git a/Projects/Domain/BattleDomain/Sources/Model/Mapper/BattleScenarioDataDTO+.swift b/Projects/Domain/BattleDomain/Sources/Model/Mapper/BattleScenarioDataDTO+.swift index 07840d0c..da96e80b 100644 --- a/Projects/Domain/BattleDomain/Sources/Model/Mapper/BattleScenarioDataDTO+.swift +++ b/Projects/Domain/BattleDomain/Sources/Model/Mapper/BattleScenarioDataDTO+.swift @@ -50,7 +50,7 @@ public extension ScenarioNodeDTO { audioDuration: audioDuration, autoNextNodeId: autoNextNodeId, scripts: scripts.map { $0.toDomain() }, - interactiveOptions: (interactiveOptions ?? []).map { $0.toDomain() } + interactiveOptions: (interactiveOptions ?? []).compactMap { $0.toDomain() } ) } } @@ -68,7 +68,16 @@ public extension ScenarioScriptDTO { } public extension ScenarioInteractiveOptionDTO { - func toDomain() -> ScenarioInteractiveOption { - ScenarioInteractiveOption(label: label, nextNodeId: nextNodeId) + func toDomain() -> ScenarioInteractiveOption? { + guard let nextNodeId else { return nil } + let displayLabel = label?.trimmingCharacters(in: .whitespacesAndNewlines) + let displayTitle = title?.trimmingCharacters(in: .whitespacesAndNewlines) + let resolvedLabel = (displayLabel?.isEmpty == false ? displayLabel : nil) ?? displayTitle ?? "선택지" + return ScenarioInteractiveOption( + label: resolvedLabel, + nextNodeId: nextNodeId, + optionID: optionId, + title: displayTitle + ) } } diff --git a/Projects/Domain/BattleDomain/Tests/Sources/BattleDomainTests.swift b/Projects/Domain/BattleDomain/Tests/Sources/BattleDomainTests.swift index 7323e98f..4fac9844 100644 --- a/Projects/Domain/BattleDomain/Tests/Sources/BattleDomainTests.swift +++ b/Projects/Domain/BattleDomain/Tests/Sources/BattleDomainTests.swift @@ -6,6 +6,7 @@ // @testable import BattleDomain +import Foundation import Testing struct BattleDomainTests { @@ -13,4 +14,25 @@ struct BattleDomainTests { func battleDomainExample() { #expect(true) } + + @Test + func interactiveOptionPreservesServerID() throws { + let json = Data(#"{"optionId":9001,"label":"유죄","nextNodeId":802}"#.utf8) + let option = try #require(JSONDecoder().decode(ScenarioInteractiveOptionDTO.self, from: json).toDomain()) + + #expect(option.optionID == 9001) + #expect(option.label == "유죄") + #expect(option.nextNodeId == 802) + } + + @Test + func interactiveOptionFallsBackToTitleAndSkipsMissingNextNode() throws { + let titleOnly = Data(#"{"optionId":9001,"title":"피고인의 사정을 듣는다","nextNodeId":802}"#.utf8) + let option = try #require(JSONDecoder().decode(ScenarioInteractiveOptionDTO.self, from: titleOnly).toDomain()) + #expect(option.label == "피고인의 사정을 듣는다") + #expect(option.title == "피고인의 사정을 듣는다") + + let missingNext = Data(#"{"optionId":9002,"label":"유죄","title":"유죄를 선택한다"}"#.utf8) + #expect(try JSONDecoder().decode(ScenarioInteractiveOptionDTO.self, from: missingNext).toDomain() == nil) + } } diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/AIFeedback/ClassAIPersonalFeedback.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/AIFeedback/ClassAIPersonalFeedback.swift index ec3b1e2e..dbf211d4 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Entity/AIFeedback/ClassAIPersonalFeedback.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/AIFeedback/ClassAIPersonalFeedback.swift @@ -15,6 +15,7 @@ public struct ClassAIPersonalFeedback: Equatable, Sendable { public let coverage: String? public let excludedCount: Int public let preOption: ClassAIFeedbackVoteChoice? + public let midVotes: [ClassMidVoteChoice] public let postOption: ClassAIFeedbackVoteChoice? public let perspectiveCount: Int public let commentCount: Int @@ -37,6 +38,7 @@ public struct ClassAIPersonalFeedback: Equatable, Sendable { coverage: String? = nil, excludedCount: Int = 0, preOption: ClassAIFeedbackVoteChoice? = nil, + midVotes: [ClassMidVoteChoice] = [], postOption: ClassAIFeedbackVoteChoice? = nil, perspectiveCount: Int = 0, commentCount: Int = 0 @@ -50,6 +52,7 @@ public struct ClassAIPersonalFeedback: Equatable, Sendable { self.coverage = coverage self.excludedCount = excludedCount self.preOption = preOption + self.midVotes = midVotes self.postOption = postOption self.perspectiveCount = perspectiveCount self.commentCount = commentCount diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Battle/ClassBattleSession.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Battle/ClassBattleSession.swift index d8d7951f..255b82dd 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Battle/ClassBattleSession.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Battle/ClassBattleSession.swift @@ -9,6 +9,7 @@ public struct ClassBattleSession: Equatable, Sendable { public let canParticipate: Bool public let participationCompleted: Bool public let battle: ClassBattleDetail + public let midVoteEnabled: Bool public init( classID: Int, @@ -18,7 +19,8 @@ public struct ClassBattleSession: Equatable, Sendable { commentRequired: Bool, canParticipate: Bool, participationCompleted: Bool, - battle: ClassBattleDetail + battle: ClassBattleDetail, + midVoteEnabled: Bool = false ) { self.classID = classID self.myRole = myRole @@ -28,5 +30,6 @@ public struct ClassBattleSession: Equatable, Sendable { self.canParticipate = canParticipate self.participationCompleted = participationCompleted self.battle = battle + self.midVoteEnabled = midVoteEnabled } } diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassBattleError.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassBattleError.swift index 19730900..df3cbeb8 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassBattleError.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassBattleError.swift @@ -8,6 +8,11 @@ public enum ClassBattleError: LocalizedError, Equatable, Sendable { case preVoteLocked case invalidOption case ttsUnavailable + case ttsIncomplete + case midVoteUnavailable + case invalidMidVoteOption + case midVoteLocked + case midVoteRequired case voteNotFound case preVoteRequired case alreadySubmitted @@ -26,6 +31,11 @@ public enum ClassBattleError: LocalizedError, Equatable, Sendable { case .preVoteLocked: return "청취 또는 토론을 시작한 뒤에는 사전투표를 바꿀 수 없어요." case .invalidOption: return "이 배틀의 선택지가 아니에요." case .ttsUnavailable: return "이 클래스에서는 TTS 청취를 사용할 수 없어요." + case .ttsIncomplete: return "TTS 청취를 완료한 뒤 투표해 주세요." + case .midVoteUnavailable: return "이 배틀에서는 중간투표를 사용할 수 없어요." + case .invalidMidVoteOption: return "이 선택 지점의 선택지가 아니에요." + case .midVoteLocked: return "사후투표를 마친 뒤에는 중간투표를 바꿀 수 없어요." + case .midVoteRequired: return "선택 지점의 투표를 완료해 주세요." case .voteNotFound: return "투표 내역을 찾을 수 없어요." case .preVoteRequired: return "먼저 사전투표를 완료해 주세요." case .alreadySubmitted: return "사후투표는 다시 제출할 수 없어요." diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassError.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassError.swift index 1fcd2ac1..e0845fd1 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassError.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassError.swift @@ -30,6 +30,7 @@ public enum ClassError: LocalizedError, Equatable, Sendable { case topicVersionNotFound case topicDraftUsed case inviteCodeFailed + case requestKeyConflict case network(String) case unknown(String) @@ -61,6 +62,7 @@ public enum ClassError: LocalizedError, Equatable, Sendable { case .topicVersionNotFound: return "존재하지 않는 주제 버전입니다." case .topicDraftUsed: return "이 주제 초안으로 이미 만든 클래스가 있습니다." case .inviteCodeFailed: return "초대코드 생성에 실패했습니다. 다시 시도해 주세요." + case .requestKeyConflict: return "클래스 생성 요청이 충돌했어요. 입력 내용을 확인하고 다시 시도해 주세요." case let .network(message), let .unknown(message): return message } } diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultMidVoteCheckpoint.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultMidVoteCheckpoint.swift new file mode 100644 index 00000000..9d0bbefe --- /dev/null +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultMidVoteCheckpoint.swift @@ -0,0 +1,15 @@ +public struct ClassResultMidVoteCheckpoint: Equatable, Identifiable, Sendable { + public let id: Int + public let voteCount: Int + public let options: [ClassResultMidVoteOption] + + public init( + id: Int, + voteCount: Int, + options: [ClassResultMidVoteOption] + ) { + self.id = id + self.voteCount = voteCount + self.options = options + } +} diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultMidVoteOption.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultMidVoteOption.swift new file mode 100644 index 00000000..c569823b --- /dev/null +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultMidVoteOption.swift @@ -0,0 +1,18 @@ +public struct ClassResultMidVoteOption: Equatable, Identifiable, Sendable { + public let id: Int + public let label: String + public let voteCount: Int + public let ratio: Double + + public init( + id: Int, + label: String, + voteCount: Int, + ratio: Double + ) { + self.id = id + self.label = label + self.voteCount = voteCount + self.ratio = ratio + } +} diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultStats.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultStats.swift index c21bb17a..d6391f51 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultStats.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassResultStats.swift @@ -5,6 +5,9 @@ public struct ClassResultStats: Equatable, Sendable { public let commentWriterCount: Int public let commentRate: Double public let postVoteCount: Int + public let midVoteEnabled: Bool + public let midVoteCount: Int + public let midVoteCheckpoints: [ClassResultMidVoteCheckpoint] public let options: [ClassResultOption] public init( @@ -14,6 +17,9 @@ public struct ClassResultStats: Equatable, Sendable { commentWriterCount: Int, commentRate: Double, postVoteCount: Int, + midVoteEnabled: Bool = false, + midVoteCount: Int = 0, + midVoteCheckpoints: [ClassResultMidVoteCheckpoint] = [], options: [ClassResultOption] ) { self.activeMemberCount = activeMemberCount @@ -22,6 +28,9 @@ public struct ClassResultStats: Equatable, Sendable { self.commentWriterCount = commentWriterCount self.commentRate = commentRate self.postVoteCount = postVoteCount + self.midVoteEnabled = midVoteEnabled + self.midVoteCount = midVoteCount + self.midVoteCheckpoints = midVoteCheckpoints self.options = options } } diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Room/ClassCreation.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Room/ClassCreation.swift index c026d7e0..4932f71e 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Room/ClassCreation.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Room/ClassCreation.swift @@ -16,6 +16,7 @@ public struct ClassCreation: Equatable, Sendable { public var requiresComment: Bool public var ownerParticipates: Bool public var ownerDisplayName: String? + public var requestKey: String? public init( name: String, @@ -24,7 +25,8 @@ public struct ClassCreation: Equatable, Sendable { allowsAnonymousOpinion: Bool, requiresComment: Bool, ownerParticipates: Bool = false, - ownerDisplayName: String? = nil + ownerDisplayName: String? = nil, + requestKey: String? = nil ) { self.name = name self.deadline = deadline @@ -34,6 +36,7 @@ public struct ClassCreation: Equatable, Sendable { self.requiresComment = requiresComment self.ownerParticipates = ownerParticipates self.ownerDisplayName = ownerDisplayName + self.requestKey = requestKey } public init( @@ -43,7 +46,8 @@ public struct ClassCreation: Equatable, Sendable { allowsAnonymousOpinion: Bool, requiresComment: Bool, ownerParticipates: Bool = false, - ownerDisplayName: String? = nil + ownerDisplayName: String? = nil, + requestKey: String? = nil ) { self.name = name self.deadline = deadline @@ -53,5 +57,6 @@ public struct ClassCreation: Equatable, Sendable { self.requiresComment = requiresComment self.ownerParticipates = ownerParticipates self.ownerDisplayName = ownerDisplayName + self.requestKey = requestKey } } diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Vote/ClassMidVoteChoice.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Vote/ClassMidVoteChoice.swift new file mode 100644 index 00000000..18dba431 --- /dev/null +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Vote/ClassMidVoteChoice.swift @@ -0,0 +1,15 @@ +public struct ClassMidVoteChoice: Equatable, Sendable { + public let checkpointID: Int + public let optionID: Int + public let label: String + + public init( + checkpointID: Int, + optionID: Int, + label: String + ) { + self.checkpointID = checkpointID + self.optionID = optionID + self.label = label + } +} diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Vote/ClassVoteHistory.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Vote/ClassVoteHistory.swift index 9130be62..aed8ca44 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Vote/ClassVoteHistory.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Vote/ClassVoteHistory.swift @@ -1,6 +1,7 @@ public struct ClassVoteHistory: Equatable, Sendable { public let battleTitle: String public let preVote: ClassVoteChoice + public let midVotes: [ClassMidVoteChoice] public let postVote: ClassVoteChoice? public let status: ClassBattleStep public let opinionChanged: Bool? @@ -8,12 +9,14 @@ public struct ClassVoteHistory: Equatable, Sendable { public init( battleTitle: String, preVote: ClassVoteChoice, + midVotes: [ClassMidVoteChoice] = [], postVote: ClassVoteChoice?, status: ClassBattleStep, opinionChanged: Bool? ) { self.battleTitle = battleTitle self.preVote = preVote + self.midVotes = midVotes self.postVote = postVote self.status = status self.opinionChanged = opinionChanged diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Battle/ClassBattleInterface.swift b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Battle/ClassBattleInterface.swift index c78b435e..88e927eb 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Battle/ClassBattleInterface.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Battle/ClassBattleInterface.swift @@ -11,6 +11,7 @@ import Foundation public protocol ClassBattleInterface: Sendable { func fetchBattle(classID: Int) async throws -> ClassBattleSession func submitPreVote(classID: Int, optionID: Int) async throws -> ClassVoteReceipt + func submitMidVote(classID: Int, checkpointID: Int, optionID: Int) async throws -> ClassVoteReceipt func completeTTS(classID: Int) async throws func submitPostVote(classID: Int, optionID: Int) async throws -> ClassVoteReceipt func fetchMyVotes(classID: Int) async throws -> ClassVoteHistory diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Default/MockClassRepository+Features.swift b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Default/MockClassRepository+Features.swift index ee0404ce..506c3a73 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Default/MockClassRepository+Features.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Default/MockClassRepository+Features.swift @@ -9,6 +9,14 @@ public extension MockClassRepository { throw ClassError.unknown("모의 클래스 기능이 설정되지 않았습니다: submitPreVote") } + public func submitMidVote( + classID: Int, + checkpointID: Int, + optionID: Int + ) async throws -> ClassVoteReceipt { + throw ClassError.unknown("모의 클래스 기능이 설정되지 않았습니다: submitMidVote") + } + public func completeTTS(classID: Int) async throws { throw ClassError.unknown("모의 클래스 기능이 설정되지 않았습니다: completeTTS") } diff --git a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassAIFeedbackDTO.swift b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassAIFeedbackDTO.swift index 40bce229..d21d0003 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassAIFeedbackDTO.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassAIFeedbackDTO.swift @@ -37,6 +37,7 @@ struct ClassPersonalReportDTO: Decodable, Sendable { let state: ClassAIGenerationState let feedback: Feedback? let preOption: VoteOption? + let midVotes: [ClassMidVoteDTO]? let postOption: VoteOption? let perspectiveCount: Int? let commentCount: Int? diff --git a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassBattleSessionDTO.swift b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassBattleSessionDTO.swift index c6ed6220..92d5d8ab 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassBattleSessionDTO.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassBattleSessionDTO.swift @@ -39,5 +39,6 @@ struct ClassBattleSessionDTO: Decodable, Sendable { let commentRequired: Bool let canParticipate: Bool let participationCompleted: Bool + let midVoteEnabled: Bool? let battle: Battle } diff --git a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassResultDTO.swift b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassResultDTO.swift index e311e01a..fe3fd56d 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassResultDTO.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassResultDTO.swift @@ -4,6 +4,19 @@ import PickeCoreUtility struct ClassResultDTO: Decodable, Sendable { struct Stats: Decodable, Sendable { + struct MidVoteCheckpoint: Decodable, Sendable { + struct Option: Decodable, Sendable { + let optionId: Int + let label: String + let voteCount: Int + let ratio: Double + } + + let checkpointId: Int + let voteCount: Int + let options: [Option] + } + struct Option: Decodable, Sendable { let optionId: Int let title: String @@ -19,6 +32,9 @@ struct ClassResultDTO: Decodable, Sendable { let commentWriterCount: Int let commentRate: Double let postVoteCount: Int + let midVoteEnabled: Bool? + let midVoteCount: Int? + let midVoteCheckpoints: [MidVoteCheckpoint]? let options: [Option] } diff --git a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassVoteDTO.swift b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassVoteDTO.swift index 5f209386..ba24085e 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassVoteDTO.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassVoteDTO.swift @@ -7,6 +7,12 @@ struct ClassVoteReceiptDTO: Decodable, Sendable { let status: String } +struct ClassMidVoteDTO: Decodable, Sendable { + let checkpointId: Int + let optionId: Int + let label: String +} + struct ClassVoteHistoryDTO: Decodable, Sendable { struct Choice: Decodable, Sendable { let optionId: Int @@ -17,6 +23,7 @@ struct ClassVoteHistoryDTO: Decodable, Sendable { let battleTitle: String let preVote: Choice + let midVotes: [ClassMidVoteDTO]? let postVote: Choice? let status: String let opinionChanged: Bool? diff --git a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassAIFeedbackDTO+.swift b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassAIFeedbackDTO+.swift index 51494e86..120b44c6 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassAIFeedbackDTO+.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassAIFeedbackDTO+.swift @@ -21,6 +21,7 @@ extension ClassPersonalReportDTO.Feedback.Evidence { extension ClassPersonalReportDTO.Feedback { func toDomain( preOption: ClassPersonalReportDTO.VoteOption?, + midVotes: [ClassMidVoteDTO], postOption: ClassPersonalReportDTO.VoteOption?, perspectiveCount: Int, commentCount: Int @@ -38,6 +39,7 @@ extension ClassPersonalReportDTO.Feedback { coverage: coverage, excludedCount: excludedCount ?? 0, preOption: preOption?.toDomain(), + midVotes: midVotes.map { $0.toDomain() }, postOption: postOption?.toDomain(), perspectiveCount: perspectiveCount, commentCount: commentCount @@ -52,6 +54,7 @@ extension ClassPersonalReportDTO { guard let feedback else { throw ClassAIFeedbackError.generationFailed } return feedback.toDomain( preOption: preOption, + midVotes: midVotes ?? [], postOption: postOption, perspectiveCount: perspectiveCount ?? 0, commentCount: commentCount ?? 0 diff --git a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassBattleSessionDTO+.swift b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassBattleSessionDTO+.swift index da1592e7..fb9ba900 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassBattleSessionDTO+.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassBattleSessionDTO+.swift @@ -55,7 +55,8 @@ extension ClassBattleSessionDTO { commentRequired: commentRequired, canParticipate: canParticipate, participationCompleted: participationCompleted, - battle: try battle.toDomain() + battle: try battle.toDomain(), + midVoteEnabled: midVoteEnabled ?? false ) } } diff --git a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassResultDTO+.swift b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassResultDTO+.swift index 6ed67760..b63d2679 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassResultDTO+.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassResultDTO+.swift @@ -14,6 +14,27 @@ extension ClassResultDTO.Stats.Option { } } +extension ClassResultDTO.Stats.MidVoteCheckpoint.Option { + func toDomain() -> ClassResultMidVoteOption { + ClassResultMidVoteOption( + id: optionId, + label: label, + voteCount: voteCount, + ratio: ratio + ) + } +} + +extension ClassResultDTO.Stats.MidVoteCheckpoint { + func toDomain() -> ClassResultMidVoteCheckpoint { + ClassResultMidVoteCheckpoint( + id: checkpointId, + voteCount: voteCount, + options: options.map { $0.toDomain() } + ) + } +} + extension ClassResultDTO.Stats { func toDomain() -> ClassResultStats { ClassResultStats( @@ -23,6 +44,9 @@ extension ClassResultDTO.Stats { commentWriterCount: commentWriterCount, commentRate: commentRate, postVoteCount: postVoteCount, + midVoteEnabled: midVoteEnabled ?? false, + midVoteCount: midVoteCount ?? 0, + midVoteCheckpoints: midVoteCheckpoints?.map { $0.toDomain() } ?? [], options: options.map { $0.toDomain() } ) } diff --git a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassVoteDTO+.swift b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassVoteDTO+.swift index 14d33d7a..5d077754 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassVoteDTO+.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassVoteDTO+.swift @@ -17,6 +17,16 @@ extension ClassVoteHistoryDTO.Choice { } } +extension ClassMidVoteDTO { + func toDomain() -> ClassMidVoteChoice { + ClassMidVoteChoice( + checkpointID: checkpointId, + optionID: optionId, + label: label + ) + } +} + extension ClassVoteHistoryDTO { func toDomain() throws -> ClassVoteHistory { guard let status = ClassBattleStep(rawValue: status) else { @@ -25,6 +35,7 @@ extension ClassVoteHistoryDTO { return ClassVoteHistory( battleTitle: battleTitle, preVote: preVote.toDomain(), + midVotes: midVotes?.map { $0.toDomain() } ?? [], postVote: postVote?.toDomain(), status: status, opinionChanged: opinionChanged diff --git a/Projects/Domain/ClassDomain/Sources/Repository/ClassErrorMapper.swift b/Projects/Domain/ClassDomain/Sources/Repository/ClassErrorMapper.swift index 195a9429..92326fd0 100644 --- a/Projects/Domain/ClassDomain/Sources/Repository/ClassErrorMapper.swift +++ b/Projects/Domain/ClassDomain/Sources/Repository/ClassErrorMapper.swift @@ -31,6 +31,7 @@ enum ClassErrorMapper { case "CLASS_404": return .notFound case "CLASS_409_FULL": return .full case "CLASS_409_DELETED": return .deleted + case "CLASS_409_REQUEST_KEY": return .requestKeyConflict case "CLASS_409_OWNER": return .ownClass case "CLASS_403_REMOVED": return .removed case "CLASS_403_TICKET": return .ticketRequired diff --git a/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Battle.swift b/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Battle.swift index cbd1ae18..91e012c7 100644 --- a/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Battle.swift +++ b/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Battle.swift @@ -40,6 +40,20 @@ extension ClassRepositoryImpl { } catch { throw Self.battleError(error) } } + public func submitMidVote( + classID: Int, + checkpointID: Int, + optionID: Int + ) async throws -> ClassVoteReceipt { + do { + let payload = try await client.send( + ClassBattleService.midVote(classID: classID, checkpointID: checkpointID, optionID: optionID), + as: ClassVoteReceiptDTO.self + ) + return try payload.toDomain() + } catch { throw Self.battleError(error) } + } + public func submitPostVote( classID: Int, optionID: Int @@ -125,6 +139,11 @@ extension ClassRepositoryImpl { case "CLASS_409_PRE_VOTE_LOCKED": return .preVoteLocked case "BATTLE_OPT_404": return .invalidOption case "CLASS_409_TTS": return .ttsUnavailable + case "CLASS_409_TTS_INCOMPLETE": return .ttsIncomplete + case "CLASS_409_MID_VOTE": return .midVoteUnavailable + case "CLASS_404_MID_VOTE_OPTION": return .invalidMidVoteOption + case "CLASS_409_MID_VOTE_LOCKED": return .midVoteLocked + case "CLASS_409_MID_VOTE_REQUIRED": return .midVoteRequired case "VOTE_404": return .voteNotFound case "VOTE_409_PRE": return .preVoteRequired case "VOTE_409_SUB": return .alreadySubmitted diff --git a/Projects/Domain/ClassDomain/Sources/UseCase/ClassUseCaseImpl+Battle.swift b/Projects/Domain/ClassDomain/Sources/UseCase/ClassUseCaseImpl+Battle.swift index 9434363c..80b9c3cd 100644 --- a/Projects/Domain/ClassDomain/Sources/UseCase/ClassUseCaseImpl+Battle.swift +++ b/Projects/Domain/ClassDomain/Sources/UseCase/ClassUseCaseImpl+Battle.swift @@ -17,6 +17,14 @@ extension ClassUseCaseImpl { try await repository.submitPreVote(classID: classID, optionID: optionID) } + public func submitMidVote( + classID: Int, + checkpointID: Int, + optionID: Int + ) async throws -> ClassVoteReceipt { + try await repository.submitMidVote(classID: classID, checkpointID: checkpointID, optionID: optionID) + } + public func completeTTS(classID: Int) async throws { try await repository.completeTTS(classID: classID) } diff --git a/Projects/Domain/ClassDomain/Tests/Sources/ClassBattleAPIContractTests.swift b/Projects/Domain/ClassDomain/Tests/Sources/ClassBattleAPIContractTests.swift index 16779be3..9fa50632 100644 --- a/Projects/Domain/ClassDomain/Tests/Sources/ClassBattleAPIContractTests.swift +++ b/Projects/Domain/ClassDomain/Tests/Sources/ClassBattleAPIContractTests.swift @@ -10,6 +10,7 @@ struct ClassBattleAPIContractTests { func 클래스_배틀_경로와_투표_의견_요청_본문이_명세와_일치한다() throws { let detail = ClassBattleService.detail(classID: 5) let preVote = ClassBattleService.preVote(classID: 5, optionID: 31) + let midVote = ClassBattleService.midVote(classID: 5, checkpointID: 801, optionID: 9001) let postVote = ClassBattleService.postVote(classID: 5, optionID: 32) let publish = ClassBattleService.publishPerspective(classID: 5, content: "의견") let perspectives = ClassBattleService.perspectives( @@ -24,6 +25,8 @@ struct ClassBattleAPIContractTests { #expect(detail.method == .get) #expect(try preVote.url().path == "/api/v1/classes/5/battle/votes/pre") #expect(preVote.method == .post) + #expect(try midVote.url().path == "/api/v1/classes/5/battle/votes/mid") + #expect(midVote.method == .post) #expect(try postVote.url().path == "/api/v1/classes/5/battle/votes/post") #expect(postVote.method == .post) #expect(try ClassBattleService.ttsComplete(classID: 5).url().path == "/api/v1/classes/5/battle/votes/tts-complete") @@ -35,6 +38,7 @@ struct ClassBattleAPIContractTests { #expect(try ClassBattleService.myPerspectives(classID: 5).url().path == "/api/v1/classes/5/battle/perspectives/me") guard let preVoteParameters = preVote.parameters, + let midVoteParameters = midVote.parameters, let postVoteParameters = postVote.parameters, let publishParameters = publish.parameters, let perspectiveParameters = perspectives.parameters @@ -44,10 +48,12 @@ struct ClassBattleAPIContractTests { } let encoder = JSONEncoder() let preVoteJSON = try JSONSerialization.jsonObject(with: encoder.encode(preVoteParameters)) as? [String: Int] + let midVoteJSON = try JSONSerialization.jsonObject(with: encoder.encode(midVoteParameters)) as? [String: Int] let postVoteJSON = try JSONSerialization.jsonObject(with: encoder.encode(postVoteParameters)) as? [String: Int] let publishJSON = try JSONSerialization.jsonObject(with: encoder.encode(publishParameters)) as? [String: String] let perspectiveJSON = try JSONSerialization.jsonObject(with: encoder.encode(perspectiveParameters)) as? [String: Any] #expect(preVoteJSON == ["optionId": 31]) + #expect(midVoteJSON == ["checkpointId": 801, "optionId": 9001]) #expect(postVoteJSON == ["optionId": 32]) #expect(publishJSON == ["content": "의견"]) #expect(perspectiveJSON?["cursor"] as? String == "next") @@ -86,7 +92,7 @@ struct ClassBattleAPIContractTests { func 클래스_배틀_응답은_클래스_권한과_배틀_진행을_함께_복원한다() throws { let json = """ {"classId":5,"myRole":"MEMBER","closeAt":"2026-12-31T23:59:59","closed":false, - "commentRequired":true,"canParticipate":true,"participationCompleted":false, + "commentRequired":true,"canParticipate":true,"participationCompleted":false,"midVoteEnabled":true, "battle":{"battleInfo":{"battleId":9,"title":"AI가 쓴 글도 창작일까?", "options":[{"optionId":31,"label":"A","title":"창작이다"}]}, "userVoteStatus":"PRE_VOTE","currentStep":"PRE_VOTE"}} @@ -96,6 +102,7 @@ struct ClassBattleAPIContractTests { #expect(session.classID == 5) #expect(session.myRole == .member) #expect(session.canParticipate) + #expect(session.midVoteEnabled) #expect(session.battle.currentStep == .preVote) #expect(session.battle.options.first?.id == 31) } diff --git a/Projects/Domain/ClassDomain/Tests/Sources/ClassErrorMappingTests.swift b/Projects/Domain/ClassDomain/Tests/Sources/ClassErrorMappingTests.swift index 1502babc..f774a29b 100644 --- a/Projects/Domain/ClassDomain/Tests/Sources/ClassErrorMappingTests.swift +++ b/Projects/Domain/ClassDomain/Tests/Sources/ClassErrorMappingTests.swift @@ -13,6 +13,7 @@ struct ClassErrorMappingTests { ("CLASS_404", .notFound), ("CLASS_409_FULL", .full), ("CLASS_409_DELETED", .deleted), + ("CLASS_409_REQUEST_KEY", .requestKeyConflict), ("CLASS_409_OWNER", .ownClass), ("CLASS_403_REMOVED", .removed), ("CLASS_403_TICKET", .ticketRequired), diff --git a/Projects/Domain/ClassDomain/Tests/Sources/ClassRecommendationTests.swift b/Projects/Domain/ClassDomain/Tests/Sources/ClassRecommendationTests.swift index b9b4a243..f93bbff8 100644 --- a/Projects/Domain/ClassDomain/Tests/Sources/ClassRecommendationTests.swift +++ b/Projects/Domain/ClassDomain/Tests/Sources/ClassRecommendationTests.swift @@ -102,7 +102,8 @@ struct ClassRecommendationTests { battleId: 12, allowsAnonymousOpinion: false, requiresComment: true, - ownerParticipates: false + ownerParticipates: false, + requestKey: "3f2b8c1e-7d4a-4f6b-9a0e-2c5d8e1f4a7b" ) let request = ClassroomService.create(creation) guard let body = request.parameters else { @@ -120,6 +121,7 @@ struct ClassRecommendationTests { #expect(fields["deadline"] as? String == "2026-12-31T23:59:59") #expect((fields["commentRequired"] as? Bool) == true) #expect((fields["ownerParticipates"] as? Bool) == false) + #expect(fields["requestKey"] as? String == "3f2b8c1e-7d4a-4f6b-9a0e-2c5d8e1f4a7b") #expect(fields["allowsAnonymousOpinion"] == nil) } diff --git a/Projects/Domain/ClassDomain/Tests/Sources/ClassResultAPIContractTests.swift b/Projects/Domain/ClassDomain/Tests/Sources/ClassResultAPIContractTests.swift index 53e31e9c..b9d987a4 100644 --- a/Projects/Domain/ClassDomain/Tests/Sources/ClassResultAPIContractTests.swift +++ b/Projects/Domain/ClassDomain/Tests/Sources/ClassResultAPIContractTests.swift @@ -44,6 +44,11 @@ struct ClassResultAPIContractTests { "stats": { "activeMemberCount": 12, "completedCount": 10, "participationRate": 83.3, "commentWriterCount": 9, "commentRate": 75.0, "postVoteCount": 11, + "midVoteEnabled": true, "midVoteCount": 10, + "midVoteCheckpoints": [{ + "checkpointId": 801, "voteCount": 10, + "options": [{"optionId": 9002, "label": "무죄", "voteCount": 6, "ratio": 60.0}] + }], "options": [ { "optionId": 31, "title": "창작이다", "preVoteCount": 8, "postVoteCount": 6, "postVoteRatio": 54.5 } @@ -59,6 +64,11 @@ struct ClassResultAPIContractTests { #expect(result.stats.completedCount == 10) #expect(result.stats.participationRate == 83.3) #expect(result.stats.options.first?.id == 31) + #expect(result.stats.midVoteEnabled) + #expect(result.stats.midVoteCount == 10) + #expect(result.stats.midVoteCheckpoints.first?.id == 801) + #expect(result.stats.midVoteCheckpoints.first?.options.first?.id == 9002) + #expect(result.stats.midVoteCheckpoints.first?.options.first?.ratio == 60.0) #expect(result.myReportState == nil) #expect(result.insightState == "SUCCEEDED") } @@ -84,6 +94,7 @@ struct ClassResultAPIContractTests { { "state": "SUCCEEDED", "preOption": { "optionId": 31, "title": "창작이다", "stance": "처음 의견" }, + "midVotes": [{"checkpointId":801,"optionId":9002,"label":"무죄"}], "postOption": { "optionId": 32, "title": "창작이 아니다", "stance": "바뀐 의견" }, "perspectiveCount": 1, "commentCount": 2, @@ -106,11 +117,35 @@ struct ClassResultAPIContractTests { #expect(feedback.evidence.first?.content == "삭제되지 않은 원문") #expect(feedback.evidence.first?.quote == "짧은 인용") #expect(feedback.preOption?.id == 31) + #expect(feedback.midVotes == [ClassMidVoteChoice(checkpointID: 801, optionID: 9002, label: "무죄")]) #expect(feedback.postOption?.title == "창작이 아니다") #expect(feedback.perspectiveCount == 1) #expect(feedback.commentCount == 2) } + @Test + func 투표_이력의_중간_선택을_보존한다() throws { + let json = """ + {"battleTitle":"Picke(TTS) 배틀","preVote":{"optionId":31,"label":"A","title":"창작이다"}, + "midVotes":[{"checkpointId":801,"optionId":9002,"label":"무죄"}], + "postVote":null,"status":"PRE_VOTE","opinionChanged":null} + """ + + let history = try JSONDecoder().decode(ClassVoteHistoryDTO.self, from: Data(json.utf8)).toDomain() + #expect(history.midVotes == [ClassMidVoteChoice(checkpointID: 801, optionID: 9002, label: "무죄")]) + } + + @Test + func 중간_투표가_없는_이전_응답도_빈_선택으로_읽는다() throws { + let json = """ + {"battleTitle":"일반 배틀","preVote":{"optionId":31,"label":"A","title":"창작이다"}, + "postVote":null,"status":"PRE_VOTE","opinionChanged":null} + """ + + let history = try JSONDecoder().decode(ClassVoteHistoryDTO.self, from: Data(json.utf8)).toDomain() + #expect(history.midVotes.isEmpty) + } + @Test func 운영자_인사이트의_근거와_대표_의견을_보존한다() throws { let json = """ diff --git a/Projects/Domain/HomeDomain/Interface/Sources/Scenario/RecommendedPathKey.swift b/Projects/Domain/HomeDomain/Interface/Sources/Scenario/RecommendedPathKey.swift index d71f39ee..a7fb6cfc 100644 --- a/Projects/Domain/HomeDomain/Interface/Sources/Scenario/RecommendedPathKey.swift +++ b/Projects/Domain/HomeDomain/Interface/Sources/Scenario/RecommendedPathKey.swift @@ -9,6 +9,8 @@ import Foundation public enum RecommendedPathKey: String, Equatable, Hashable, CaseIterable { case common = "COMMON" + case pathA = "PATH_A" + case pathB = "PATH_B" case unknown public init(rawValue: String) { diff --git a/Projects/Domain/HomeDomain/Interface/Sources/Scenario/ScenarioInteractiveOption.swift b/Projects/Domain/HomeDomain/Interface/Sources/Scenario/ScenarioInteractiveOption.swift index 73182d79..25f8aa2b 100644 --- a/Projects/Domain/HomeDomain/Interface/Sources/Scenario/ScenarioInteractiveOption.swift +++ b/Projects/Domain/HomeDomain/Interface/Sources/Scenario/ScenarioInteractiveOption.swift @@ -8,16 +8,22 @@ import Foundation public struct ScenarioInteractiveOption: Equatable, Hashable, Identifiable { + public let optionID: Int? public let label: String + public let title: String? public let nextNodeId: Int public var id: String { "\(label)-\(nextNodeId)" } public init( label: String, - nextNodeId: Int + nextNodeId: Int, + optionID: Int? = nil, + title: String? = nil ) { + self.optionID = optionID self.label = label + self.title = title self.nextNodeId = nextNodeId } } diff --git a/Projects/Domain/HomeDomain/Tests/Sources/BattleScenarioTimelineTests.swift b/Projects/Domain/HomeDomain/Tests/Sources/BattleScenarioTimelineTests.swift index 8ec83931..d65d7d6f 100644 --- a/Projects/Domain/HomeDomain/Tests/Sources/BattleScenarioTimelineTests.swift +++ b/Projects/Domain/HomeDomain/Tests/Sources/BattleScenarioTimelineTests.swift @@ -9,6 +9,11 @@ import HomeDomainInterface import Testing struct BattleScenarioTimelineTests { + @Test + func 추천_오디오_경로의_분기_키를_인식한다() { + #expect(RecommendedPathKey(rawValue: "PATH_A") == .pathA) + #expect(RecommendedPathKey(rawValue: "PATH_B") == .pathB) + } @Test func linearContentReachesClosingWithoutExplicitLink() { let scenario = makeScenario() diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassBattleTranscriptLine.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassBattleTranscriptLine.swift index 28a35abc..a7a1ad34 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassBattleTranscriptLine.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassBattleTranscriptLine.swift @@ -28,7 +28,10 @@ public struct ClassBattleTranscriptLine: Equatable, Identifiable, Sendable { } public extension ClassBattleTranscriptLine { - static func from(scenario: BattleScenario) -> [ClassBattleTranscriptLine] { + static func from( + scenario: BattleScenario, + selectedPaths: [Int: Int] = [:] + ) -> [ClassBattleTranscriptLine] { var transcript: [ClassBattleTranscriptLine] = [] var visibleNodeID = scenario.startNodeId var visitedNodeIDs = Set() @@ -81,7 +84,15 @@ public extension ClassBattleTranscriptLine { )) } } - guard let nextNodeID = scenario.nextNodeId(for: node) else { break } + let nextNodeID: Int? + if !node.interactiveOptions.isEmpty { + nextNodeID = node.interactiveOptions.first { + $0.nextNodeId == selectedPaths[node.nodeId] + }?.nextNodeId + } else { + nextNodeID = scenario.nextNodeId(for: node) + } + guard let nextNodeID else { break } visibleNodeID = nextNodeID } return transcript.sorted { diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift index 7d0ec44b..1dcb5db0 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift @@ -59,6 +59,16 @@ public struct ClassBattleFlowFeature { public var audioDuration: TimeInterval = 0 public var playbackRate: Float = 1 public var transcript: [ClassBattleTranscriptLine] = [] + public var scenario: BattleScenario? + public var currentNodeID: Int? + public var pendingCheckpointID: Int? + public var selectedMidOptionID: Int? + public var selectedPaths: [Int: Int] = [:] + public var loadedAudioURL: URL? + public var midVoteRequestID: UUID? + public var pendingSeekTime: TimeInterval? + public var needsMidVoteRecovery = false + public var midVoteDisabledLocally = false public var selectedPerspectiveOptionID: Int? public var perspectiveSort: String = "popular" @@ -72,11 +82,15 @@ public struct ClassBattleFlowFeature { guard viewState == .loaded, canParticipate, submitRequestID == nil else { return false } guard let step else { return false } switch step { - case .none, .postVote: return selectedOptionID != nil + case .none: return selectedOptionID != nil + case .postVote: + return selectedOptionID != nil + && (!needsMidVoteRecovery || (hasListened && pendingCheckpointID == nil)) case .preVote: guard selectedOptionID != nil else { return false } - return selectedOptionID != voteHistory?.preVote.optionID || hasListened - case .ttsListening: return hasListened + return (!isPreVoteLocked && selectedOptionID != voteHistory?.preVote.optionID) || hasListened + case .ttsListening: + return hasListened && pendingCheckpointID == nil && hasAnsweredCurrentPath case .completed: return !hasSubmittedInCurrentSession && hasPublishedPerspective == false @@ -84,12 +98,18 @@ public struct ClassBattleFlowFeature { } } + public var isPreVoteLocked: Bool { + session?.midVoteEnabled == true + && (!(voteHistory?.midVotes.isEmpty ?? true) || !selectedPaths.isEmpty) + } + public var buttonTitle: String { guard let step else { return "불러오는 중" } switch step { case .none: return "사전 투표하기" case .preVote: - return selectedOptionID != voteHistory?.preVote.optionID ? "사전투표 변경하기" : "청취 완료" + return !isPreVoteLocked && selectedOptionID != voteHistory?.preVote.optionID + ? "사전투표 변경하기" : "청취 완료" case .ttsListening: return "청취 완료" case .postVote: return "최종 투표하기" case .completed: @@ -150,6 +170,7 @@ public struct ClassBattleFlowFeature { public var isAudioStep: Bool { step == .some(.preVote) || step == .some(.ttsListening) + || (step == .some(.postVote) && needsMidVoteRecovery) } public var audioProgress: Double { @@ -169,6 +190,38 @@ public struct ClassBattleFlowFeature { transcript.filter { $0.startTime <= currentTime } } + public var midVoteOptions: [ScenarioInteractiveOption] { + guard let pendingCheckpointID else { return [] } + return scenario?.nodes.first { $0.nodeId == pendingCheckpointID }?.interactiveOptions ?? [] + } + + public var currentNode: ScenarioNode? { + guard let scenario else { return nil } + return scenario.nodes.first { $0.nodeId == (currentNodeID ?? scenario.startNodeId) } + } + + public var canConfirmMidVote: Bool { + pendingCheckpointID != nil && selectedMidOptionID != nil && midVoteRequestID == nil + } + + public var hasAnsweredCurrentPath: Bool { + guard session?.midVoteEnabled == true, let scenario else { return true } + var nodeID: Int? = scenario.startNodeId + var visited = Set() + while let id = nodeID, visited.insert(id).inserted, + let node = scenario.nodes.first(where: { $0.nodeId == id }) { + if !node.interactiveOptions.isEmpty { + guard let chosenNextID = selectedPaths[id], + node.interactiveOptions.contains(where: { $0.nextNodeId == chosenNextID }) + else { return false } + nodeID = chosenNextID + } else { + nodeID = scenario.nextNodeId(for: node) + } + } + return true + } + public var visibleTranscriptRows: [ClassBattleTranscriptRow] { let lines = visibleTranscript return lines.enumerated().map { index, line in @@ -248,6 +301,8 @@ public struct ClassBattleFlowFeature { case scrub(TimeInterval) case replayTapped case playbackRateSelected(Float) + case midVoteOptionTapped(Int) + case confirmMidVoteTapped case perspectiveOptionTapped(Int?) case perspectiveSortTapped(String) case perspectiveTapped(Int) @@ -258,7 +313,8 @@ public struct ClassBattleFlowFeature { case progressResponse(UUID, Result) case submissionResponse(UUID, ClassBattleStep, Result) case audioURLResponse(UUID, Result) - case transcriptLoaded(UUID, [ClassBattleTranscriptLine]) + case scenarioLoaded(UUID, BattleScenario) + case midVoteResponse(UUID, Int, Int, Result) case audioLoaded(UUID, Bool, TimeInterval) case playerTimeUpdated(UUID, TimeInterval) case perspectivePageResponse(UUID, Result) @@ -284,6 +340,7 @@ public struct ClassBattleFlowFeature { case voteStats(UUID) case perspectiveLike(UUID) case myPerspective(UUID) + case midVote(UUID) } @Dependency(\.uuid) private var uuid @@ -354,6 +411,7 @@ extension ClassBattleFlowFeature { state.pendingPerspectiveLikeResults = [:] state.voteStatsRequestID = nil state.myPerspectiveRequestID = nil + state.midVoteRequestID = nil state.isLoadingPerspectives = false state.isPlaying = false return stopAudio(instanceID: state.instanceID) @@ -384,6 +442,7 @@ extension ClassBattleFlowFeature { guard state.canParticipate, let step = state.step, [.none, .preVote, .postVote].contains(step), + !(step == .preVote && state.isPreVoteLocked), state.options.contains(where: { $0.id == optionID }), state.submitRequestID == nil else { return .none } @@ -411,6 +470,7 @@ extension ClassBattleFlowFeature { guard let classID = state.room?.id else { return .none } let optionID = state.selectedOptionID let preVoteOptionID = state.voteHistory?.preVote.optionID + let isPreVoteLocked = state.isPreVoteLocked let draft = state.draft.trimmingCharacters(in: .whitespacesAndNewlines) return .run { [useCase = classUseCase] send in let result = await Result { @@ -419,7 +479,7 @@ extension ClassBattleFlowFeature { guard let optionID else { throw ClassBattleError.invalidOption } _ = try await useCase.submitPreVote(classID: classID, optionID: optionID) case .preVote: - if optionID != preVoteOptionID { + if !isPreVoteLocked && optionID != preVoteOptionID { guard let optionID else { throw ClassBattleError.invalidOption } _ = try await useCase.submitPreVote(classID: classID, optionID: optionID) } else { @@ -439,26 +499,78 @@ extension ClassBattleFlowFeature { await send(.inner(.submissionResponse(requestID, step, result))) } .cancellable(id: CancelID.submit(state.instanceID), cancelInFlight: true) + case let .midVoteOptionTapped(nextNodeID): + guard state.pendingCheckpointID != nil, + state.midVoteOptions.contains(where: { $0.nextNodeId == nextNodeID }), + state.midVoteRequestID == nil else { return .none } + state.selectedMidOptionID = nextNodeID + state.errorMessage = nil + return .none + case .confirmMidVoteTapped: + guard state.canConfirmMidVote, + let checkpointID = state.pendingCheckpointID, + let nextNodeID = state.selectedMidOptionID, + let option = state.midVoteOptions.first(where: { $0.nextNodeId == nextNodeID }), + let classID = state.room?.id else { return .none } + guard let session = state.session else { return .none } + if !session.midVoteEnabled || state.midVoteDisabledLocally { + return advanceAfterChoice(state: &state, checkpointID: checkpointID, option: option) + } + guard let optionID = option.optionID else { + state.errorMessage = ClassBattleError.invalidResponse.message + return .none + } + let requestID = uuid() + state.midVoteRequestID = requestID + state.errorMessage = nil + return .run { [useCase = classUseCase] send in + let result = await Result { + _ = try await useCase.submitMidVote( + classID: classID, + checkpointID: checkpointID, + optionID: optionID + ) + return true + }.mapError(Self.mapError) + guard !Task.isCancelled else { return } + await send(.inner(.midVoteResponse(requestID, checkpointID, optionID, result))) + } + .cancellable(id: CancelID.midVote(state.instanceID), cancelInFlight: true) case .playbackTapped: - guard state.isAudioStep, state.isAudioReady else { return .none } + guard state.isAudioStep, state.isAudioReady, + state.pendingCheckpointID == nil else { return .none } state.isPlaying.toggle() return .run { [player = audioPlayer, shouldPlay = state.isPlaying] _ in if shouldPlay { await player.play() } else { await player.pause() } } .cancellable(id: CancelID.playback(state.instanceID), cancelInFlight: true) case let .seekBy(delta): - guard state.isAudioStep, state.isAudioReady else { return .none } - let target = min(state.audioDuration, max(0, state.currentTime + delta)) + guard state.isAudioStep, state.isAudioReady, + state.pendingCheckpointID == nil else { return .none } + let checkpointBoundary = state.currentNode.flatMap { node in + node.interactiveOptions.isEmpty ? nil : state.scenario?.nodeEndTime(for: node) + } + let target = min( + state.audioDuration, + min(checkpointBoundary ?? .infinity, max(0, state.currentTime + delta)) + ) state.currentTime = target + state.pendingSeekTime = target return .run { [player = audioPlayer] _ in await player.seek(to: target) } case let .scrub(time): - guard state.isAudioStep, state.isAudioReady, state.hasListened else { return .none } + guard state.isAudioStep, state.isAudioReady, state.hasListened, + state.pendingCheckpointID == nil else { return .none } let target = min(state.audioDuration, max(0, time)) state.currentTime = target + state.pendingSeekTime = target return .run { [player = audioPlayer] _ in await player.seek(to: target) } case .replayTapped: guard state.isAudioStep, state.isAudioReady, state.hasListened else { return .none } state.currentTime = 0 + state.currentNodeID = state.scenario?.startNodeId + state.pendingCheckpointID = nil + state.selectedMidOptionID = nil + state.pendingSeekTime = 0 state.isPlaying = true return .run { [player = audioPlayer] _ in await player.seek(to: 0) @@ -536,6 +648,17 @@ extension ClassBattleFlowFeature { state.viewState = .loaded state.session = progress.session state.voteHistory = progress.voteHistory + if progress.session.midVoteEnabled { + state.selectedPaths = state.scenario.map { + Self.selectedPaths(from: progress.voteHistory, scenario: $0) + } ?? [:] + if let scenario = state.scenario { + state.transcript = ClassBattleTranscriptLine.from( + scenario: scenario, + selectedPaths: state.selectedPaths + ) + } + } if progress.session.battle.currentStep == .completed { if state.hasSubmittedInCurrentSession || (progress.session.commentRequired && progress.session.participationCompleted) { @@ -577,6 +700,7 @@ extension ClassBattleFlowFeature { case .success: state.draft = "" state.hasAdvancedInCurrentSession = true + if submittedStep == .postVote { state.needsMidVoteRecovery = false } if submittedStep == .completed { state.hasSubmittedInCurrentSession = true state.hasPublishedPerspective = true @@ -584,12 +708,35 @@ extension ClassBattleFlowFeature { return loadProgress(state: &state) case let .failure(error): state.errorMessage = error.message + if submittedStep == .postVote, error == .midVoteRequired { + state.needsMidVoteRecovery = true + state.hasListened = false + state.currentNodeID = state.scenario?.startNodeId + state.pendingCheckpointID = nil + state.selectedMidOptionID = nil + state.currentTime = 0 + state.pendingSeekTime = 0 + if state.isAudioReady { + state.isPlaying = true + return .merge( + .run { [player = audioPlayer] _ in + await player.seek(to: 0) + await player.play() + }, + observePlayerTime(instanceID: state.instanceID) + ) + } + if let battleID = state.session?.battle.id { + return loadAudio(state: &state, battleID: battleID) + } + } return .none } case let .audioURLResponse(requestID, result): guard state.audioRequestID == requestID, state.isAudioStep else { return .none } switch result { case let .success(url): + state.loadedAudioURL = url return .run { [player = audioPlayer] send in let loaded = await player.load(url: url) let duration = loaded ? await player.duration() : 0 @@ -602,6 +749,47 @@ extension ClassBattleFlowFeature { state.audioRequestID = nil return .none } + case let .scenarioLoaded(requestID, scenario): + guard state.audioRequestID == requestID, state.isAudioStep else { return .none } + state.scenario = scenario + state.currentNodeID = scenario.startNodeId + state.pendingCheckpointID = nil + state.selectedMidOptionID = nil + if state.session?.midVoteEnabled == true { + state.selectedPaths = Self.selectedPaths(from: state.voteHistory, scenario: scenario) + } + state.transcript = ClassBattleTranscriptLine.from( + scenario: scenario, + selectedPaths: state.selectedPaths + ) + return .none + case let .midVoteResponse(requestID, checkpointID, optionID, result): + guard state.midVoteRequestID == requestID, + state.pendingCheckpointID == checkpointID, + let option = state.midVoteOptions.first(where: { $0.optionID == optionID }) + else { return .none } + state.midVoteRequestID = nil + switch result { + case .success: + if state.step == .some(.preVote) { + state.selectedOptionID = state.voteHistory?.preVote.optionID + } + return advanceAfterChoice(state: &state, checkpointID: checkpointID, option: option) + case let .failure(error): + state.errorMessage = error.message + if error == .midVoteUnavailable { + let effect = advanceAfterChoice(state: &state, checkpointID: checkpointID, option: option) + state.midVoteDisabledLocally = true + state.errorMessage = error.message + return effect + } + if error == .midVoteLocked || error == .invalidMidVoteOption || error == .voteNotFound { + state.pendingCheckpointID = nil + state.isAudioReady = false + return .merge(stopPlayback(instanceID: state.instanceID), loadProgress(state: &state)) + } + return .none + } case let .audioLoaded(requestID, loaded, duration): guard state.audioRequestID == requestID, state.isAudioStep else { return .none } state.audioRequestID = nil @@ -612,20 +800,28 @@ extension ClassBattleFlowFeature { state.isAudioReady = true state.audioDuration = duration state.isPlaying = true + let seekTime = state.pendingSeekTime return .merge( - .run { [player = audioPlayer] _ in await player.play() } + .run { [player = audioPlayer] _ in + if let seekTime { await player.seek(to: seekTime) } + await player.play() + } .cancellable(id: CancelID.playback(state.instanceID), cancelInFlight: true), observePlayerTime(instanceID: state.instanceID) ) - case let .transcriptLoaded(requestID, transcript): - guard state.audioRequestID == requestID, state.isAudioStep else { return .none } - state.transcript = transcript - return .none case let .playerTimeUpdated(instanceID, time): guard state.instanceID == instanceID, state.isAudioStep, - state.isAudioReady else { return .none } + state.isAudioReady, state.pendingCheckpointID == nil else { return .none } + if let pendingSeekTime = state.pendingSeekTime { + guard abs(time - pendingSeekTime) < 1 else { return .none } + state.pendingSeekTime = nil + } state.currentTime = min(state.audioDuration, max(0, time)) - guard time >= max(0, state.audioDuration - 0.3) else { return .none } + if let effect = advanceNodeIfNeeded(state: &state, time: time) { + return effect + } + guard state.pendingCheckpointID == nil, + time >= max(0, state.audioDuration - 0.3) else { return .none } state.hasListened = true guard state.isPlaying else { return .none } state.isPlaying = false @@ -764,22 +960,28 @@ extension ClassBattleFlowFeature { let requestID = uuid() state.audioRequestID = requestID state.errorMessage = nil - return .run { [useCase = battleUseCase] send in + return .run { [ + useCase = battleUseCase, + history = state.voteHistory, + priorPaths = state.selectedPaths + ] send in let result = await Result { let scenario = try await useCase.fetchScenario(battleId: battleID) - guard let rawURL = scenario.audios[scenario.recommendedPathKey.rawValue] + let paths = Self.selectedPaths(from: history, scenario: scenario) + let chosenNodeID = (paths.isEmpty ? priorPaths : paths).values.first + let pathKey = chosenNodeID.flatMap { Self.branchPathKey(from: $0, scenario: scenario) } + guard let rawURL = pathKey.flatMap({ scenario.audios[$0] }) + ?? scenario.audios[scenario.recommendedPathKey.rawValue] ?? scenario.audios.values.first, let url = URL(string: rawURL), ["http", "https"].contains(url.scheme?.lowercased() ?? "") else { throw ClassBattleError.invalidResponse } - return (url, ClassBattleTranscriptLine.from(scenario: scenario)) + return (url, scenario) }.mapError(Self.mapError) guard !Task.isCancelled else { return } switch result { - case let .success((url, transcript)): - if !transcript.isEmpty { - await send(.inner(.transcriptLoaded(requestID, transcript))) - } + case let .success((url, scenario)): + await send(.inner(.scenarioLoaded(requestID, scenario))) await send(.inner(.audioURLResponse(requestID, .success(url)))) case let .failure(error): await send(.inner(.audioURLResponse(requestID, .failure(error)))) @@ -788,6 +990,113 @@ extension ClassBattleFlowFeature { .cancellable(id: CancelID.audio(state.instanceID), cancelInFlight: true) } + private func advanceNodeIfNeeded( + state: inout State, + time: TimeInterval + ) -> Effect? { + guard let scenario = state.scenario, state.pendingCheckpointID == nil else { return nil } + var visited = Set() + while let node = state.currentNode, visited.insert(node.nodeId).inserted, + time >= scenario.nodeEndTime(for: node) { + if !node.interactiveOptions.isEmpty { + if let selectedNextID = state.selectedPaths[node.nodeId], + let selectedOption = node.interactiveOptions.first(where: { $0.nextNodeId == selectedNextID }) { + return advanceAfterChoice(state: &state, checkpointID: node.nodeId, option: selectedOption) + } + state.pendingCheckpointID = node.nodeId + state.selectedMidOptionID = state.selectedPaths[node.nodeId] + state.currentTime = scenario.nodeEndTime(for: node) + state.isPlaying = false + return .run { [player = audioPlayer] _ in await player.pause() } + } + guard let nextNodeID = scenario.nextNodeId(for: node), + scenario.nodes.contains(where: { $0.nodeId == nextNodeID }) + else { return nil } + state.currentNodeID = nextNodeID + } + return nil + } + + private func advanceAfterChoice( + state: inout State, + checkpointID: Int, + option: ScenarioInteractiveOption + ) -> Effect { + guard let scenario = state.scenario else { return .none } + let pathKey = Self.branchPathKey(from: option.nextNodeId, scenario: scenario) + let branchURL: URL? + if let pathKey, let rawURL = scenario.audios[pathKey] { + branchURL = URL(string: rawURL) + } else if pathKey != nil, + scenario.audios["PATH_A"] != nil || scenario.audios["PATH_B"] != nil { + state.errorMessage = ClassBattleError.invalidResponse.message + return .none + } else { + branchURL = state.loadedAudioURL + } + state.selectedPaths[checkpointID] = option.nextNodeId + state.transcript = ClassBattleTranscriptLine.from( + scenario: scenario, + selectedPaths: state.selectedPaths + ) + state.currentNodeID = option.nextNodeId + state.pendingCheckpointID = nil + state.selectedMidOptionID = nil + state.errorMessage = nil + let targetTime = scenario.nodeStartTime(for: option.nextNodeId) + state.currentTime = targetTime + state.pendingSeekTime = targetTime + state.isPlaying = true + if let branchURL, branchURL != state.loadedAudioURL { + let requestID = uuid() + state.audioRequestID = requestID + state.loadedAudioURL = branchURL + state.isAudioReady = false + state.isPlaying = false + return .run { [player = audioPlayer] send in + let loaded = await player.load(url: branchURL) + let duration = loaded ? await player.duration() : 0 + guard !Task.isCancelled else { return } + await send(.inner(.audioLoaded(requestID, loaded, duration))) + } + .cancellable(id: CancelID.audio(state.instanceID), cancelInFlight: true) + } + return .run { [player = audioPlayer] _ in + await player.seek(to: targetTime) + await player.play() + } + } + + private static func selectedPaths( + from history: ClassVoteHistory?, + scenario: BattleScenario + ) -> [Int: Int] { + Dictionary( + history?.midVotes.compactMap { choice in + scenario.nodes.first(where: { $0.nodeId == choice.checkpointID })? + .interactiveOptions.first(where: { $0.optionID == choice.optionID }) + .map { (choice.checkpointID, $0.nextNodeId) } + } ?? [], + uniquingKeysWith: { _, newest in newest } + ) + } + + private static func branchPathKey( + from firstNodeID: Int, + scenario: BattleScenario + ) -> String? { + var nodeID: Int? = firstNodeID + var visited = Set() + while let id = nodeID, visited.insert(id).inserted, + let node = scenario.nodes.first(where: { $0.nodeId == id }) { + let name = node.nodeName.uppercased() + if name.contains("_A") { return "PATH_A" } + if name.contains("_B") { return "PATH_B" } + nodeID = node.autoNextNodeId + } + return nil + } + private func observePlayerTime(instanceID: UUID) -> Effect { .run { [player = audioPlayer] send in for await time in player.currentTimes() { @@ -802,6 +1111,7 @@ extension ClassBattleFlowFeature { .merge( .cancel(id: CancelID.load(instanceID)), .cancel(id: CancelID.submit(instanceID)), + .cancel(id: CancelID.midVote(instanceID)), .cancel(id: CancelID.perspectives(instanceID)), .cancel(id: CancelID.perspectiveLike(instanceID)), .cancel(id: CancelID.myPerspective(instanceID)), diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift index 299b339d..24a7b991 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift @@ -22,7 +22,10 @@ public struct ClassBattleFlowView: View { case .error: errorScreen() case .loaded: switch store.step { - case .some(.none), .some(.postVote): voteScreen() + case .some(.none): voteScreen() + case .some(.postVote): + if store.needsMidVoteRecovery { listeningScreen() } + else { voteScreen() } case .some(.preVote): listeningScreen() case .some(.ttsListening): listeningScreen() case .some(.completed): @@ -356,6 +359,9 @@ private extension ClassBattleFlowView { } } } + if store.pendingCheckpointID != nil { + midVoteChoiceSection() + } audioFooter() } .screenBackground() @@ -365,6 +371,50 @@ private extension ClassBattleFlowView { } } + @ViewBuilder + func midVoteChoiceSection() -> some View { + VStack(alignment: .leading, spacing: 12) { + Text("선택의 시간") + .pretendardFont(.headingMedium) + .foregroundStyle(.neutral900) + ForEach(store.midVoteOptions) { option in + Button { + send(.midVoteOptionTapped(option.nextNodeId)) + } label: { + VStack(alignment: .leading, spacing: 4) { + if let title = option.title, !title.isEmpty, title != option.label { + Text(option.label) + .pretendardFont(.labelSmall) + .foregroundStyle(.neutral600) + } + Text(option.title.flatMap { $0.isEmpty ? nil : $0 } ?? option.label) + .pretendardFont(.bodyMedium) + .foregroundStyle(.neutral900) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(12) + .background( + store.selectedMidOptionID == option.nextNodeId ? .beige300 : .white, + in: RoundedRectangle(cornerRadius: 4) + ) + .overlay { + RoundedRectangle(cornerRadius: 4) + .strokeBorder( + store.selectedMidOptionID == option.nextNodeId ? .primary500 : .beige600, + lineWidth: 1 + ) + } + } + .buttonStyle(.plain) + } + Button("입장 선택하기") { send(.confirmMidVoteTapped) } + .ctaButtonStyle(.primary, size: .large, height: 52) + .disabled(!store.canConfirmMidVote) + } + .padding(16) + .background(.beige200) + } + @ViewBuilder func preVoteEditor() -> some View { VStack(alignment: .leading, spacing: 20) { @@ -470,7 +520,9 @@ private extension ClassBattleFlowView { .pretendardFont(.regular13) .foregroundStyle(.primary500) Spacer() - Button("다시 시도") { send(.retryAudioTapped) } + if store.pendingCheckpointID == nil { + Button("다시 시도") { send(.retryAudioTapped) } + } } } audioProgressBar() @@ -485,7 +537,7 @@ private extension ClassBattleFlowView { playbackRateTitle: store.playbackRateTitle, onPlaybackRateSelected: { send(.playbackRateSelected($0)) } ) - .disabled(!store.isAudioReady) + .disabled(!store.isAudioReady || store.pendingCheckpointID != nil) if store.hasListened { Button(store.buttonTitle) { send(.submitTapped) } .ctaButtonStyle(.primary, size: .large, height: 52) diff --git a/Projects/Feature/Class/Sources/ClassSetting/Reducer/ClassSettingFeature.swift b/Projects/Feature/Class/Sources/ClassSetting/Reducer/ClassSettingFeature.swift index a11a6f2d..f5a9cf76 100644 --- a/Projects/Feature/Class/Sources/ClassSetting/Reducer/ClassSettingFeature.swift +++ b/Projects/Feature/Class/Sources/ClassSetting/Reducer/ClassSettingFeature.swift @@ -7,6 +7,7 @@ import ClassDomainInterface import ComposableArchitecture +import CryptoKit import Foundation import PickeCoreUtility import PickeSharedUI @@ -95,7 +96,8 @@ public struct ClassSettingFeature { battleId: battle.id, allowsAnonymousOpinion: false, requiresComment: requiresComment, - ownerParticipates: ownerParticipates + ownerParticipates: ownerParticipates, + requestKey: creationRequestKey ) } guard let topicVersionID, aiQuestion != nil else { return nil } @@ -105,7 +107,8 @@ public struct ClassSettingFeature { topicVersionId: topicVersionID, allowsAnonymousOpinion: false, requiresComment: requiresComment, - ownerParticipates: ownerParticipates + ownerParticipates: ownerParticipates, + requestKey: creationRequestKey ) } @@ -138,10 +141,24 @@ public struct ClassSettingFeature { topicVersionId: topicVersionID, allowsAnonymousOpinion: false, requiresComment: requiresComment, - ownerParticipates: ownerParticipates + ownerParticipates: ownerParticipates, + requestKey: creationRequestKey ) } + private var creationRequestKey: String { + let fields = [ + instanceID.uuidString, + Data(trimmedName.utf8).base64EncodedString(), + battle.map { "battle:\($0.id)" } ?? "topic:\(topicVersionID.map { String($0) } ?? "nil")", + String((isDeadlineEnabled ? deadline : Date.distantFuture).timeIntervalSinceReferenceDate.bitPattern), + String(requiresComment), + String(ownerParticipates), + ] + let digest = SHA256.hash(data: Data(fields.joined(separator: "|").utf8)) + return digest.map { String(format: "%02x", $0) }.joined() + } + private var trimmedName: String { name.trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/Projects/Feature/Class/Tests/Sources/ClassAIVoteTests.swift b/Projects/Feature/Class/Tests/Sources/ClassAIVoteTests.swift index bc1a1011..34aa6eef 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassAIVoteTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassAIVoteTests.swift @@ -489,6 +489,14 @@ private struct ClassVoteUseCaseStub: ClassBattleInterface { return .init(voteID: 5, status: .preVote) } + func submitMidVote( + classID _: Int, + checkpointID _: Int, + optionID _: Int + ) async throws -> ClassVoteReceipt { + throw ClassBattleError.midVoteUnavailable + } + func completeTTS(classID _: Int) async throws { throw ClassBattleError.invalidResponse } diff --git a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift index 26ba2cb5..29a7f300 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift @@ -13,6 +13,377 @@ import Testing struct ClassBattleFlowTests { private let requestID = UUID(uuidString: "00000000-0000-0000-0000-000000000042")! + @Test + func 중간투표_선택_전에는_청취완료를_제출할_수_없다() { + var state = battleState(room: .mocks[0]) + state.session = session(step: .ttsListening, commentRequired: false) + state.viewState = .loaded + state.hasListened = true + state.pendingCheckpointID = 801 + #expect(!state.canSubmit) + state.pendingCheckpointID = nil + #expect(state.canSubmit) + } + + @Test + func 중간투표_후에는_사전투표를_변경할_수_없다() async { + var state = battleState(room: .mocks[0]) + state.session = session(step: .preVote, commentRequired: false, midVoteEnabled: true) + state.viewState = .loaded + state.selectedOptionID = 31 + state.voteHistory = ClassVoteHistory( + battleTitle: "토론", + preVote: .init(optionID: 31, label: "A", title: "찬성"), + postVote: nil, + status: .preVote, + opinionChanged: nil + ) + state.selectedPaths = [801: 802] + #expect(state.isPreVoteLocked) + #expect(!state.canSubmit) + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } + await store.send(.view(.optionTapped(32))) + #expect(store.state.selectedOptionID == 31) + } + + @Test + func 사후투표_중간투표_누락_복구는_오디오_시간_관찰을_재개한다() async { + let spy = ClassBattleFlowSpy() + var state = battleState(room: .mocks[0]) + state.session = session(step: .postVote, commentRequired: false) + state.viewState = .loaded + state.needsMidVoteRecovery = true + state.isAudioReady = true + state.submitRequestID = requestID + state.currentTime = 15 + state.hasListened = true + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: spy) + } + await store.send(.inner(.submissionResponse(requestID, .postVote, .failure(.midVoteRequired)))) { + $0.submitRequestID = nil + $0.errorMessage = ClassBattleError.midVoteRequired.message + $0.hasListened = false + $0.currentTime = 0 + $0.pendingSeekTime = 0 + $0.isPlaying = true + } + await store.finish() + #expect(await spy.timeSubscriptions == 1) + } + + @Test + func 중간투표_선택_경로의_대본만_노출한다() { + let scenario = BattleScenario( + battleId: 22, + title: "선택형 배틀", + philosophers: [], + isInteractive: true, + startNodeId: 801, + recommendedPathKey: .common, + audios: [:], + nodes: [ + ScenarioNode( + nodeId: 801, + nodeName: "선택", + audioDuration: 2, + autoNextNodeId: nil, + scripts: [ScenarioScript(scriptId: 1, startTimeMs: 0, speakerType: .a, speakerName: "A", text: "선택하세요.")], + interactiveOptions: [ + ScenarioInteractiveOption(label: "첫째", nextNodeId: 802, optionID: 9001), + ScenarioInteractiveOption(label: "둘째", nextNodeId: 803, optionID: 9002) + ] + ), + ScenarioNode( + nodeId: 802, nodeName: "첫째 경로", audioDuration: 2, + autoNextNodeId: nil, + scripts: [ScenarioScript(scriptId: 2, startTimeMs: 2_000, speakerType: .a, speakerName: "A", text: "첫째.")], + interactiveOptions: [] + ), + ScenarioNode( + nodeId: 803, nodeName: "둘째 경로", audioDuration: 2, + autoNextNodeId: nil, + scripts: [ScenarioScript(scriptId: 3, startTimeMs: 2_000, speakerType: .b, speakerName: "B", text: "둘째.")], + interactiveOptions: [] + ) + ] + ) + #expect(ClassBattleTranscriptLine.from(scenario: scenario).count == 1) + #expect(ClassBattleTranscriptLine.from(scenario: scenario, selectedPaths: [801: 803]) + .map(\.text) == ["선택하세요.", "둘째."]) + } + + @Test + func 이미_기록된_중간투표는_재생할_때_다시_제출하지_않는다() async { + let scenario = BattleScenario( + battleId: 22, + title: "선택형 배틀", + philosophers: [], + isInteractive: true, + startNodeId: 801, + recommendedPathKey: .common, + audios: [:], + nodes: [ + ScenarioNode( + nodeId: 801, nodeName: "선택", audioDuration: 2, + autoNextNodeId: nil, + scripts: [ScenarioScript(scriptId: 1, startTimeMs: 0, speakerType: .a, speakerName: "A", text: "선택")], + interactiveOptions: [ScenarioInteractiveOption(label: "첫째", nextNodeId: 802, optionID: 9001)] + ), + ScenarioNode( + nodeId: 802, nodeName: "첫째 경로", audioDuration: 2, + autoNextNodeId: nil, + scripts: [ScenarioScript(scriptId: 2, startTimeMs: 2_000, speakerType: .a, speakerName: "A", text: "첫째")], + interactiveOptions: [] + ) + ] + ) + var state = battleState(room: .mocks[0]) + state.session = session(step: .ttsListening, commentRequired: false) + state.viewState = .loaded + state.isAudioReady = true + state.isPlaying = true + state.audioDuration = 4 + state.scenario = scenario + state.currentNodeID = 801 + state.selectedPaths = [801: 802] + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: ClassBattleFlowSpy()) + } + + await store.send(.inner(.playerTimeUpdated(requestID, 2))) { + $0.currentTime = 2 + $0.currentNodeID = 802 + $0.transcript = ClassBattleTranscriptLine.from(scenario: scenario, selectedPaths: [801: 802]) + $0.pendingSeekTime = 2 + } + #expect(store.state.pendingCheckpointID == nil) + await store.finish() + } + + @Test + func 다른_분기를_선택하면_해당_경로의_오디오를_불러온다() async throws { + let commonURL = try #require(URL(string: "https://example.com/common.mp3")) + let branchURL = try #require(URL(string: "https://example.com/path-b.mp3")) + let scenario = BattleScenario( + battleId: 22, + title: "선택형 배틀", + philosophers: [], + isInteractive: true, + startNodeId: 801, + recommendedPathKey: .common, + audios: ["COMMON": commonURL.absoluteString, "PATH_B": branchURL.absoluteString], + nodes: [ + ScenarioNode( + nodeId: 801, nodeName: "COMMON", audioDuration: 2, + autoNextNodeId: nil, + scripts: [ScenarioScript(scriptId: 1, startTimeMs: 0, speakerType: .a, speakerName: "A", text: "선택")], + interactiveOptions: [ScenarioInteractiveOption(label: "둘째", nextNodeId: 803, optionID: 9002)] + ), + ScenarioNode( + nodeId: 803, nodeName: "NODE_B", audioDuration: 2, + autoNextNodeId: nil, + scripts: [ScenarioScript(scriptId: 2, startTimeMs: 2_000, speakerType: .b, speakerName: "B", text: "둘째")], + interactiveOptions: [] + ) + ] + ) + let spy = ClassBattleFlowSpy() + var state = battleState(room: .mocks[0]) + state.session = session(step: .ttsListening, commentRequired: false) + state.viewState = .loaded + state.scenario = scenario + state.currentNodeID = 801 + state.pendingCheckpointID = 801 + state.selectedMidOptionID = 803 + state.isAudioReady = true + state.loadedAudioURL = commonURL + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .constant(requestID) + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: spy) + } + + await store.send(.view(.confirmMidVoteTapped)) { + $0.selectedPaths = [801: 803] + $0.transcript = ClassBattleTranscriptLine.from(scenario: scenario, selectedPaths: [801: 803]) + $0.currentNodeID = 803 + $0.pendingCheckpointID = nil + $0.selectedMidOptionID = nil + $0.currentTime = 2 + $0.pendingSeekTime = 2 + $0.audioRequestID = requestID + $0.loadedAudioURL = branchURL + $0.isAudioReady = false + } + await store.receive(\.inner, .audioLoaded(requestID, false, 0)) { + $0.audioRequestID = nil + $0.errorMessage = "오디오를 재생할 수 없어요. 다시 시도해 주세요." + } + await store.finish() + #expect(await spy.loadedURLs == [branchURL]) + } + + @Test + func 서버에서_중간투표가_비활성화되면_로컬_선택으로_재생을_계속한다() async { + let scenario = BattleScenario( + battleId: 22, + title: "선택형 배틀", + philosophers: [], + isInteractive: true, + startNodeId: 801, + recommendedPathKey: .common, + audios: [:], + nodes: [ + ScenarioNode( + nodeId: 801, nodeName: "COMMON", audioDuration: 2, + autoNextNodeId: nil, scripts: [], + interactiveOptions: [ScenarioInteractiveOption(label: "다음", nextNodeId: 802, optionID: 9001)] + ), + ScenarioNode( + nodeId: 802, nodeName: "NEXT", audioDuration: 2, + autoNextNodeId: nil, scripts: [], interactiveOptions: [] + ) + ] + ) + var state = battleState(room: .mocks[0]) + state.session = session(step: .ttsListening, commentRequired: false, midVoteEnabled: true) + state.scenario = scenario + state.currentNodeID = 801 + state.pendingCheckpointID = 801 + state.selectedMidOptionID = 802 + state.midVoteRequestID = requestID + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: ClassBattleFlowSpy()) + } + await store.send(.inner(.midVoteResponse(requestID, 801, 9001, .failure(.midVoteUnavailable)))) { + $0.midVoteRequestID = nil + $0.selectedPaths = [801: 802] + $0.currentNodeID = 802 + $0.pendingCheckpointID = nil + $0.selectedMidOptionID = nil + $0.currentTime = 0 + $0.pendingSeekTime = 0 + $0.isPlaying = true + $0.midVoteDisabledLocally = true + $0.errorMessage = ClassBattleError.midVoteUnavailable.message + } + await store.finish() + } + + @Test + func 사후투표_복구_후에도_사용자가_고른_최종_선택을_제출한다() async { + let scenario = BattleScenario( + battleId: 22, + title: "선택형 배틀", + philosophers: [], + isInteractive: true, + startNodeId: 801, + recommendedPathKey: .common, + audios: [:], + nodes: [ + ScenarioNode( + nodeId: 801, nodeName: "COMMON", audioDuration: 2, + autoNextNodeId: nil, scripts: [], + interactiveOptions: [ScenarioInteractiveOption(label: "다음", nextNodeId: 802, optionID: 9001)] + ), + ScenarioNode( + nodeId: 802, nodeName: "NEXT", audioDuration: 2, + autoNextNodeId: nil, scripts: [], interactiveOptions: [] + ) + ] + ) + let spy = ClassBattleFlowSpy() + var state = battleState(room: .mocks[0]) + state.session = session(step: .postVote, commentRequired: false, midVoteEnabled: true) + state.viewState = .loaded + state.scenario = scenario + state.voteHistory = ClassVoteHistory( + battleTitle: "토론", + preVote: .init(optionID: 31, label: "A", title: "찬성"), + postVote: nil, + status: .postVote, + opinionChanged: nil + ) + state.selectedOptionID = 32 + state.needsMidVoteRecovery = true + state.hasListened = true + state.pendingCheckpointID = 801 + state.selectedMidOptionID = 802 + state.midVoteRequestID = requestID + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .constant(requestID) + $0.classBattleUseCase = ClassBattleFlowUseCaseStub(spy: spy) + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: spy) + } + await store.send(.inner(.midVoteResponse(requestID, 801, 9001, .success(true)))) { + $0.midVoteRequestID = nil + $0.selectedPaths = [801: 802] + $0.currentNodeID = 802 + $0.pendingCheckpointID = nil + $0.selectedMidOptionID = nil + $0.currentTime = 0 + $0.pendingSeekTime = 0 + $0.isPlaying = true + } + #expect(store.state.selectedOptionID == 32) + await store.send(.view(.submitTapped)) { $0.submitRequestID = requestID } + await store.receive(\.inner, .submissionResponse(requestID, .postVote, .failure(.invalidResponse))) { + $0.submitRequestID = nil + $0.errorMessage = ClassBattleError.invalidResponse.message + } + await store.finish() + #expect(await spy.postVoteOptionIDs == [32]) + } + + @Test + func 중간투표_확인은_체크포인트와_서버_선택지_ID를_제출한다() async { + let scenario = BattleScenario( + battleId: 22, + title: "선택형 배틀", + philosophers: [], + isInteractive: true, + startNodeId: 801, + recommendedPathKey: .common, + audios: [:], + nodes: [ + ScenarioNode( + nodeId: 801, nodeName: "COMMON", audioDuration: 2, + autoNextNodeId: nil, scripts: [], + interactiveOptions: [ScenarioInteractiveOption(label: "다음", nextNodeId: 802, optionID: 9001)] + ), + ScenarioNode( + nodeId: 802, nodeName: "NEXT", audioDuration: 2, + autoNextNodeId: nil, scripts: [], interactiveOptions: [] + ) + ] + ) + let spy = ClassBattleFlowSpy() + var state = battleState(room: .mocks[0]) + state.session = session(step: .ttsListening, commentRequired: false, midVoteEnabled: true) + state.scenario = scenario + state.currentNodeID = 801 + state.pendingCheckpointID = 801 + state.selectedMidOptionID = 802 + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .constant(requestID) + $0.classBattleUseCase = ClassBattleFlowUseCaseStub(spy: spy) + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: spy) + } + await store.send(.view(.confirmMidVoteTapped)) { $0.midVoteRequestID = requestID } + await store.receive(\.inner, .midVoteResponse(requestID, 801, 9001, .success(true))) { + $0.midVoteRequestID = nil + $0.selectedPaths = [801: 802] + $0.currentNodeID = 802 + $0.pendingCheckpointID = nil + $0.selectedMidOptionID = nil + $0.currentTime = 0 + $0.pendingSeekTime = 0 + $0.isPlaying = true + } + await store.finish() + #expect(await spy.midVoteArguments.map { "\($0.0):\($0.1):\($0.2)" } == ["\(ClassRoom.mocks[0].id):801:9001"]) + } + @Test func TTS_대본은_다음_노드_시작을_넘기지_않는다() { let scenario = BattleScenario( @@ -288,11 +659,15 @@ struct ClassBattleFlowTests { ClassBattleTranscriptLine(id: 2, startTime: 2, speakerName: "소크라테스", speakerImageURL: "https://example.com/b.png", text: "생각해요.", side: "B"), ClassBattleTranscriptLine(id: 3, startTime: 4, speakerName: "소크라테스", speakerImageURL: "https://example.com/b.png", text: "반대해요.", side: "B") ] - await store.receive(\.inner, .transcriptLoaded(requestID, expected)) { + await store.receive(\.inner, .scenarioLoaded(requestID, scenario)) { + $0.scenario = scenario + $0.currentNodeID = scenario.startNodeId $0.transcript = expected } let audioURL = try #require(URL(string: "https://example.com/class-battle.mp3")) - await store.receive(\.inner, .audioURLResponse(requestID, .success(audioURL))) + await store.receive(\.inner, .audioURLResponse(requestID, .success(audioURL))) { + $0.loadedAudioURL = audioURL + } await store.receive(\.inner, .audioLoaded(requestID, false, 0)) { $0.audioRequestID = nil $0.errorMessage = "오디오를 재생할 수 없어요. 다시 시도해 주세요." @@ -335,7 +710,16 @@ struct ClassBattleFlowTests { $0.audioRequestID = requestID } let audioURL = try #require(URL(string: "https://example.com/class-battle.mp3")) - await store.receive(\.inner, .audioURLResponse(requestID, .success(audioURL))) + let scenario = try await ClassBattleFlowScenarioStub(spy: ClassBattleFlowSpy()) + .fetchScenario(battleId: session.battle.id) + await store.receive(\.inner, .scenarioLoaded(requestID, scenario)) { + $0.scenario = scenario + $0.currentNodeID = scenario.startNodeId + $0.transcript = ClassBattleTranscriptLine.from(scenario: scenario) + } + await store.receive(\.inner, .audioURLResponse(requestID, .success(audioURL))) { + $0.loadedAudioURL = audioURL + } await store.receive(\.inner, .audioLoaded(requestID, false, 0)) { $0.audioRequestID = nil $0.errorMessage = "오디오를 재생할 수 없어요. 다시 시도해 주세요." @@ -344,6 +728,7 @@ struct ClassBattleFlowTests { $0.audioRequestID = requestID $0.errorMessage = nil } + await store.receive(\.inner, .scenarioLoaded(requestID, scenario)) await store.receive(\.inner, .audioURLResponse(requestID, .success(audioURL))) await store.receive(\.inner, .audioLoaded(requestID, false, 0)) { $0.audioRequestID = nil @@ -852,7 +1237,8 @@ struct ClassBattleFlowTests { private func session( step: ClassBattleStep, - commentRequired: Bool + commentRequired: Bool, + midVoteEnabled: Bool = false ) -> ClassBattleSession { ClassBattleSession( classID: 1, @@ -874,7 +1260,8 @@ struct ClassBattleFlowTests { participantsCount: 1, audioDuration: 30, options: [] - ) + ), + midVoteEnabled: midVoteEnabled ) } @@ -892,12 +1279,18 @@ private actor ClassBattleFlowSpy { private(set) var scenarioBattleIDs: [Int] = [] private(set) var loadedURLs: [URL] = [] private(set) var timeSubscriptions = 0 + private(set) var midVoteArguments: [(Int, Int, Int)] = [] + private(set) var postVoteOptionIDs: [Int] = [] func recordClass(_ id: Int) { classIDs.append(id) } func recordVoteHistory(_ id: Int) { voteHistoryClassIDs.append(id) } func recordScenario(_ id: Int) { scenarioBattleIDs.append(id) } func recordAudio(_ url: URL) { loadedURLs.append(url) } func recordTimeSubscription() { timeSubscriptions += 1 } + func recordMidVote(classID: Int, checkpointID: Int, optionID: Int) { + midVoteArguments.append((classID, checkpointID, optionID)) + } + func recordPostVote(optionID: Int) { postVoteOptionIDs.append(optionID) } } private struct ClassBattleFlowUseCaseStub: ClassBattleInterface { @@ -921,8 +1314,19 @@ private struct ClassBattleFlowUseCaseStub: ClassBattleInterface { } func submitPreVote(classID _: Int, optionID _: Int) async throws -> ClassVoteReceipt { throw ClassBattleError.invalidResponse } + func submitMidVote( + classID: Int, + checkpointID: Int, + optionID: Int + ) async throws -> ClassVoteReceipt { + await spy.recordMidVote(classID: classID, checkpointID: checkpointID, optionID: optionID) + return ClassVoteReceipt(voteID: 1, status: .ttsListening) + } func completeTTS(classID _: Int) async throws { throw ClassBattleError.invalidResponse } - func submitPostVote(classID _: Int, optionID _: Int) async throws -> ClassVoteReceipt { throw ClassBattleError.invalidResponse } + func submitPostVote(classID _: Int, optionID: Int) async throws -> ClassVoteReceipt { + await spy.recordPostVote(optionID: optionID) + throw ClassBattleError.invalidResponse + } func fetchVoteStats(classID _: Int) async throws -> ClassVoteStats { throw ClassBattleError.invalidResponse } func publishPerspective(classID _: Int, content _: String) async throws -> ClassPerspectiveReceipt { throw ClassBattleError.invalidResponse } func fetchPerspectives( diff --git a/Projects/Feature/Class/Tests/Sources/ClassSettingTests.swift b/Projects/Feature/Class/Tests/Sources/ClassSettingTests.swift index c8513f75..b6ca8b74 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassSettingTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassSettingTests.swift @@ -56,6 +56,61 @@ struct ClassSettingTests { #expect(ai.aiCreation?.ownerParticipates == true) } + @Test + func 생성_요청_키는_같은_내용의_재시도에_유지되고_내용을_바꾸면_바뀐다() throws { + let instanceID = try #require(UUID(uuidString: "3F2B8C1E-7D4A-4F6B-9A0E-2C5D8E1F4A7B")) + var state = ClassSettingFeature.State( + battle: battle, + deadline: deadline, + instanceID: instanceID + ) + state.name = "토론 클래스" + let firstKey = state.creation?.requestKey + state.name = "토론 클래스 " + + #expect(firstKey?.count == 64) + #expect(state.creation?.requestKey == firstKey) + + state.name = "다른 토론 클래스" + #expect(state.creation?.requestKey != firstKey) + state.name = "토론 클래스" + #expect(state.creation?.requestKey == firstKey) + + state.deadline = deadline.addingTimeInterval(60) + #expect(state.creation?.requestKey != firstKey) + state.deadline = deadline + state.isDeadlineEnabled = false + #expect(state.creation?.requestKey != firstKey) + state.isDeadlineEnabled = true + + state.requiresComment = true + #expect(state.creation?.requestKey != firstKey) + state.requiresComment = false + state.ownerParticipates = true + #expect(state.creation?.requestKey != firstKey) + state.ownerParticipates = false + #expect(state.creation?.requestKey == firstKey) + + var other = ClassSettingFeature.State(battle: battle, deadline: deadline) + other.name = state.name + #expect(other.creation?.requestKey != firstKey) + + state.aiQuestion = ClassAIQuestion(version: topicVersion) + state.topicVersionID = topicVersion.id + #expect(state.aiCreation?.requestKey != firstKey) + #expect(state.creation?.requestKey == firstKey) + + state.battle = nil + let aiKey = state.creation?.requestKey + #expect(aiKey == state.aiCreation?.requestKey) + #expect(aiKey != firstKey) + state.topicVersionID = topicVersion.id + 1 + #expect(state.creation?.requestKey != aiKey) + + state.battle = ClassBattleSummary.mocks[1] + #expect(state.creation?.requestKey != firstKey) + } + @Test func 클래스를_만들면_생성_델리게이트를_보낸다() async throws { var state = ClassSettingFeature.State(battle: battle, deadline: deadline) diff --git a/Projects/Feature/Class/Tests/Sources/MyClassTests.swift b/Projects/Feature/Class/Tests/Sources/MyClassTests.swift index 652e8d4c..167fcb53 100644 --- a/Projects/Feature/Class/Tests/Sources/MyClassTests.swift +++ b/Projects/Feature/Class/Tests/Sources/MyClassTests.swift @@ -13,6 +13,31 @@ import Testing @MainActor struct MyClassTests { + @Test + func 목록_500_실패는_빈_클래스로_표시하지_않고_재시도한다() async { + let store = TestStore(initialState: MyClassFeature.State()) { MyClassFeature() } withDependencies: { + $0.classRoomUseCase = MockClassRepository(rooms: [], availableRooms: []) + } + + await store.send(.inner(.roomsResponse(0, .failure(.network("HTTP 500"))))) { + $0.viewState = .error + $0.errorMessage = "HTTP 500" + } + #expect(store.state.rooms.isEmpty) + #expect(store.state.viewState == .error) + await store.send(.view(.retryTapped)) + await store.receive(\.async, .fetchRooms) { + $0.fetchGeneration = 1 + $0.isLoading = true + $0.errorMessage = nil + $0.viewState = .loading + } + await store.receive(\.inner, .roomsResponse(1, .success([]))) { + $0.isLoading = false + $0.viewState = .empty + } + } + @Test func 진행_상태로_필터링한다() async { var state = MyClassFeature.State() diff --git a/Projects/Service/API/Sources/Class/Battle/ClassBattleAPI.swift b/Projects/Service/API/Sources/Class/Battle/ClassBattleAPI.swift index 2aefadcf..f63af6c8 100644 --- a/Projects/Service/API/Sources/Class/Battle/ClassBattleAPI.swift +++ b/Projects/Service/API/Sources/Class/Battle/ClassBattleAPI.swift @@ -1,6 +1,7 @@ public enum ClassBattleAPI { case detail(classID: Int) case preVote(classID: Int) + case midVote(classID: Int) case ttsComplete(classID: Int) case postVote(classID: Int) case myVotes(classID: Int) @@ -12,6 +13,7 @@ public enum ClassBattleAPI { switch self { case let .detail(id): return "classes/\(id)/battle" case let .preVote(id): return "classes/\(id)/battle/votes/pre" + case let .midVote(id): return "classes/\(id)/battle/votes/mid" case let .ttsComplete(id): return "classes/\(id)/battle/votes/tts-complete" case let .postVote(id): return "classes/\(id)/battle/votes/post" case let .myVotes(id): return "classes/\(id)/battle/votes/me" diff --git a/Projects/Service/APIEndpoint/Sources/Class/Battle/ClassBattleService.swift b/Projects/Service/APIEndpoint/Sources/Class/Battle/ClassBattleService.swift index ef3c7455..4b1b5209 100644 --- a/Projects/Service/APIEndpoint/Sources/Class/Battle/ClassBattleService.swift +++ b/Projects/Service/APIEndpoint/Sources/Class/Battle/ClassBattleService.swift @@ -6,6 +6,7 @@ import PickeNetwork public enum ClassBattleService { case detail(classID: Int) case preVote(classID: Int, optionID: Int) + case midVote(classID: Int, checkpointID: Int, optionID: Int) case ttsComplete(classID: Int) case postVote(classID: Int, optionID: Int) case myVotes(classID: Int) @@ -22,6 +23,7 @@ extension ClassBattleService: PickeDataRequest { switch self { case let .detail(classID): return ClassBattleAPI.detail(classID: classID).description case let .preVote(classID, _): return ClassBattleAPI.preVote(classID: classID).description + case let .midVote(classID, _, _): return ClassBattleAPI.midVote(classID: classID).description case let .ttsComplete(classID): return ClassBattleAPI.ttsComplete(classID: classID).description case let .postVote(classID, _): return ClassBattleAPI.postVote(classID: classID).description case let .myVotes(classID): return ClassBattleAPI.myVotes(classID: classID).description @@ -35,7 +37,7 @@ extension ClassBattleService: PickeDataRequest { public var method: HTTPMethod { switch self { case .detail, .myVotes, .voteStats, .perspectives, .myPerspectives: return .get - case .preVote, .ttsComplete, .postVote, .publishPerspective: return .post + case .preVote, .midVote, .ttsComplete, .postVote, .publishPerspective: return .post } } @@ -43,6 +45,8 @@ extension ClassBattleService: PickeDataRequest { switch self { case let .preVote(_, optionID), let .postVote(_, optionID): return VoteBody(optionId: optionID) + case let .midVote(_, checkpointID, optionID): + return MidVoteBody(checkpointId: checkpointID, optionId: optionID) case let .publishPerspective(_, content): return PerspectiveBody(content: content) case let .perspectives(_, cursor, size, optionID, sort): diff --git a/Projects/Service/APIEndpoint/Sources/Class/Battle/Request/ClassBattleRequests.swift b/Projects/Service/APIEndpoint/Sources/Class/Battle/Request/ClassBattleRequests.swift index d46892c6..2c8bccf7 100644 --- a/Projects/Service/APIEndpoint/Sources/Class/Battle/Request/ClassBattleRequests.swift +++ b/Projects/Service/APIEndpoint/Sources/Class/Battle/Request/ClassBattleRequests.swift @@ -4,6 +4,11 @@ struct VoteBody: Encodable, Sendable { let optionId: Int } +struct MidVoteBody: Encodable, Sendable { + let checkpointId: Int + let optionId: Int +} + struct PerspectiveBody: Encodable, Sendable { let content: String } diff --git a/Projects/Service/APIEndpoint/Sources/Class/Room/Request/ClassroomRequests.swift b/Projects/Service/APIEndpoint/Sources/Class/Room/Request/ClassroomRequests.swift index 095b46d8..7e2d5446 100644 --- a/Projects/Service/APIEndpoint/Sources/Class/Room/Request/ClassroomRequests.swift +++ b/Projects/Service/APIEndpoint/Sources/Class/Room/Request/ClassroomRequests.swift @@ -10,6 +10,7 @@ struct CreateClassBody: Encodable, Sendable { let commentRequired: Bool let ownerParticipates: Bool let ownerDisplayName: String? + let requestKey: String? init(creation: ClassCreation) { name = creation.name @@ -21,6 +22,7 @@ struct CreateClassBody: Encodable, Sendable { commentRequired = creation.requiresComment ownerParticipates = creation.ownerParticipates ownerDisplayName = creation.ownerDisplayName + requestKey = creation.requestKey } } From adfe2351ac92b3c75b3b075b2741f4f150e7f4f6 Mon Sep 17 00:00:00 2001 From: Roy Date: Wed, 28 Oct 2026 23:03:57 +0900 Subject: [PATCH 02/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=8C=80=EC=83=81=EB=B3=84=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=ED=82=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 배틀과 AI 주제의 요청 키가 서로 충돌하지 않도록 실제 생성 대상 ID를 해시에 반영한다. --- .../ClassSetting/Reducer/ClassSettingFeature.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Projects/Feature/Class/Sources/ClassSetting/Reducer/ClassSettingFeature.swift b/Projects/Feature/Class/Sources/ClassSetting/Reducer/ClassSettingFeature.swift index f5a9cf76..6447e056 100644 --- a/Projects/Feature/Class/Sources/ClassSetting/Reducer/ClassSettingFeature.swift +++ b/Projects/Feature/Class/Sources/ClassSetting/Reducer/ClassSettingFeature.swift @@ -97,7 +97,7 @@ public struct ClassSettingFeature { allowsAnonymousOpinion: false, requiresComment: requiresComment, ownerParticipates: ownerParticipates, - requestKey: creationRequestKey + requestKey: creationRequestKey(subject: "battle:\(battle.id)") ) } guard let topicVersionID, aiQuestion != nil else { return nil } @@ -108,7 +108,7 @@ public struct ClassSettingFeature { allowsAnonymousOpinion: false, requiresComment: requiresComment, ownerParticipates: ownerParticipates, - requestKey: creationRequestKey + requestKey: creationRequestKey(subject: "topic:\(topicVersionID)") ) } @@ -142,15 +142,15 @@ public struct ClassSettingFeature { allowsAnonymousOpinion: false, requiresComment: requiresComment, ownerParticipates: ownerParticipates, - requestKey: creationRequestKey + requestKey: creationRequestKey(subject: "topic:\(topicVersionID)") ) } - private var creationRequestKey: String { + private func creationRequestKey(subject: String) -> String { let fields = [ instanceID.uuidString, Data(trimmedName.utf8).base64EncodedString(), - battle.map { "battle:\($0.id)" } ?? "topic:\(topicVersionID.map { String($0) } ?? "nil")", + subject, String((isDeadlineEnabled ? deadline : Date.distantFuture).timeIntervalSinceReferenceDate.bitPattern), String(requiresComment), String(ownerParticipates), From d99adc086f007b78fdedeaf9e1e6dfdc23a23488 Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 1 Nov 2026 19:28:12 +0900 Subject: [PATCH 03/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20API?= =?UTF-8?q?=EC=99=80=20=ED=99=88=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=A0=95?= =?UTF-8?q?=ED=95=A9=EC=84=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 클래스 결과 화면과 오류 처리, 홈 Best 댓글 및 빈 상태를 Android와 Figma 기준에 맞춘다. Figma 추출 플러그인과 검증 기록을 추가하고 관련 빌드 및 테스트 결과를 남긴다. --- .../Sources/ClassFlowVisualAuditTests.swift | 62 ++++++++++++ .../Sources/Monitor/PickeEventMonitor.swift | 16 +++- .../Sources/Entity/Error/ClassError.swift | 5 +- .../Repository/Room/ClassRoomInterface.swift | 2 +- .../Model/DTO/ClassPerspectiveDTO.swift | 2 +- .../Model/Mapper/ClassMemberDTO+.swift | 2 +- .../Model/Mapper/ClassPerspectiveDTO+.swift | 2 +- .../ClassRepositoryImpl+Recommendation.swift | 2 +- .../Sources/ClassBattleAPIContractTests.swift | 3 +- .../Sources/ClassErrorMappingTests.swift | 14 +++ .../Tests/Sources/ClassMemberAPITests.swift | 2 +- .../Interface/Sources/Core/HomeBundle.swift | 3 + .../Section/Comment/HomeBestComment.swift | 30 ++++++ .../Sources/Model/HomeDataDTO+.swift | 13 +++ .../Sources/Model/HomeDataDTO.swift | 9 ++ .../Tests/Sources/HomeRepositoryTests.swift | 11 +++ .../Reducer/CommentReplyFeature.swift | 23 ++++- .../Reducer/ClassRecommendFeature.swift | 4 +- .../Reducer/ClassReportFeature.swift | 43 +++++++-- .../ClassReport/View/ClassReportView.swift | 21 +++- .../Tests/Sources/ClassReportTests.swift | 48 ++++++++-- .../Sources/Main/Reducer/HomeFeature.swift | 16 ++++ .../View/Components/HomeBestCommentRow.swift | 46 +++++++++ .../View/Components/HomeSectionHeader.swift | 32 ++----- .../View/Components/HomeSkeletonView.swift | 80 ++++------------ .../Home/Sources/Main/View/HomeView.swift | 90 +++++++++++------- .../Home/Tests/Sources/HomeTests.swift | 45 +++++++++ Tools/FigmaClassAudit/README.md | 6 +- Tools/FigmaClassAudit/code.js | 37 ++++++- Tools/FigmaClassAudit/ui.html | 57 +++++++++-- docs/class-android-ios-parity-20261029.md | 64 +++++++++++++ docs/evidence/class-result-figma-20261029.png | Bin 0 -> 58349 bytes .../home-figma-13956-18883-20261030.png | Bin 0 -> 606879 bytes 33 files changed, 622 insertions(+), 168 deletions(-) create mode 100644 Projects/Domain/HomeDomain/Interface/Sources/Section/Comment/HomeBestComment.swift create mode 100644 Projects/Feature/Home/Sources/Main/View/Components/HomeBestCommentRow.swift create mode 100644 docs/class-android-ios-parity-20261029.md create mode 100644 docs/evidence/class-result-figma-20261029.png create mode 100644 docs/evidence/home-figma-13956-18883-20261030.png diff --git a/Projects/App/Tests/Sources/ClassFlowVisualAuditTests.swift b/Projects/App/Tests/Sources/ClassFlowVisualAuditTests.swift index f05f37c6..6f9c8152 100644 --- a/Projects/App/Tests/Sources/ClassFlowVisualAuditTests.swift +++ b/Projects/App/Tests/Sources/ClassFlowVisualAuditTests.swift @@ -435,6 +435,68 @@ final class ClassFlowVisualAuditTests: XCTestCase { } } + @MainActor + func testCaptureServerResultParticipationAndStance() async throws { + guard ProcessInfo.processInfo.environment["PICKE_CLASS_VISUAL_AUDIT"] == "1" else { + throw XCTSkip("Set PICKE_CLASS_VISUAL_AUDIT=1 to capture the server result") + } + let source = ClassRoom.mockClosedSociety + let room = ClassRoom( + id: source.id, + name: source.name, + joinCode: source.joinCode, + battle: source.battle, + deadline: source.deadline, + memberCount: source.memberCount, + role: .owner, + status: .closed, + allowsAnonymousOpinion: source.allowsAnonymousOpinion, + requiresComment: source.requiresComment, + ownerParticipates: source.ownerParticipates, + memberCapacity: source.memberCapacity + ) + var state = ClassReportFeature.State(room: room, instanceID: UUID()) + state.sourceViewState = .ready + state.insight = ClassAIInsight(text: "처음에는 민로스의 태도를 비난할 수 있다는 의견이 많았지만, 대화 후에는 형평과 감정을 분리해 봐야 한다는 의견이 늘어났어요.") + state.classResult = ClassResult( + classID: room.id, + closed: true, + closeAt: room.deadline, + stats: .init( + activeMemberCount: 32, + completedCount: 28, + participationRate: 87.5, + commentWriterCount: 24, + commentRate: 75, + postVoteCount: 28, + options: [ + .init(id: 1, title: "낮춰야한다", preVoteCount: 18, postVoteCount: 18, postVoteRatio: 64), + .init(id: 2, title: "교정이 우선이다", preVoteCount: 10, postVoteCount: 10, postVoteRatio: 36) + ] + ), + myReportState: nil, + insightState: "SUCCEEDED" + ) + let store = Store(initialState: state) { ClassReportFeature() } + let (top, bottom) = try await capture( + AnyView(ClassReportView(store: store)), + ready: nil, + includeScrollBottom: true, + viewport: CGSize(width: 402, height: 874) + ) + for (name, image) in [("top", top), ("bottom", try XCTUnwrap(bottom))] { + let attachment = XCTAttachment(image: image) + attachment.name = "class-server-result-402-\(name)" + attachment.lifetime = .keepAlways + add(attachment) + } + let topText = try recognizedAuditText(in: top, name: "class-server-result-402-top") + let bottomText = try recognizedAuditText(in: XCTUnwrap(bottom), name: "class-server-result-402-bottom") + XCTAssertTrue(topText.contains("클래스참여현황"), "Participation card missing: \(topText)") + XCTAssertTrue(topText.contains("참여한멤버"), "Participation caption missing: \(topText)") + XCTAssertTrue(bottomText.contains("입장분포"), "Stance card missing: \(bottomText)") + } + @MainActor func testClassReportLayoutFooters() async throws { guard ProcessInfo.processInfo.environment["PICKE_CLASS_VISUAL_AUDIT"] == "1" else { diff --git a/Projects/Core/PickeNetwork/Sources/Monitor/PickeEventMonitor.swift b/Projects/Core/PickeNetwork/Sources/Monitor/PickeEventMonitor.swift index eb253a88..1a52d3d4 100644 --- a/Projects/Core/PickeNetwork/Sources/Monitor/PickeEventMonitor.swift +++ b/Projects/Core/PickeNetwork/Sources/Monitor/PickeEventMonitor.swift @@ -29,10 +29,14 @@ struct PickeEventMonitor: EventMonitor { ) { let urlRequest = response.request let method = urlRequest?.httpMethod ?? "?" - let url = urlRequest?.url?.absoluteString ?? "?" + // Query values can contain invite or ticket codes; keep only the route in logs. + let url = urlRequest?.url?.path ?? "?" let status = response.response?.statusCode ?? -1 let milliseconds = Int((response.metrics?.taskInterval.duration ?? 0) * 1000) let isFailure = response.error != nil + let isSensitiveRoute = url.hasPrefix("/api/v1/auth/") || + url.hasPrefix("/api/v1/classes") || + url.hasPrefix("/api/v1/class-tickets") var lines = ["\(isFailure ? "❌" : "✅") \(method) \(url) → \(status) (\(milliseconds)ms)"] @@ -41,7 +45,7 @@ struct PickeEventMonitor: EventMonitor { lines.append(" · Request Headers\n\(format(headers))") } if let body = urlRequest?.httpBody, !body.isEmpty { - lines.append(" · Request Body\n\(indent(prettyBody(body)))") + lines.append(" · Request Body\n\(isSensitiveRoute ? " " : indent(prettyBody(body)))") } // ── 응답 ── @@ -58,7 +62,7 @@ struct PickeEventMonitor: EventMonitor { } } if let data = response.data, !data.isEmpty { - lines.append(" · Response Body\n\(indent(prettyBody(data)))") + lines.append(" · Response Body\n\(isSensitiveRoute ? " " : indent(prettyBody(data)))") } let message = lines.joined(separator: "\n") @@ -118,7 +122,11 @@ private extension PickeEventMonitor { func format(_ headers: [String: String]) -> String { headers .sorted { $0.key < $1.key } - .map { " \($0.key): \($0.value)" } + .map { key, value in + let shouldRedact = ["authorization", "cookie", "set-cookie"] + .contains(key.lowercased()) || key.lowercased().contains("token") + return " \(key): \(shouldRedact ? "" : value)" + } .joined(separator: "\n") } diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassError.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassError.swift index e0845fd1..752081ab 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassError.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Error/ClassError.swift @@ -32,6 +32,7 @@ public enum ClassError: LocalizedError, Equatable, Sendable { case inviteCodeFailed case requestKeyConflict case network(String) + case userMessage(String) case unknown(String) public var errorDescription: String? { @@ -63,7 +64,9 @@ public enum ClassError: LocalizedError, Equatable, Sendable { case .topicDraftUsed: return "이 주제 초안으로 이미 만든 클래스가 있습니다." case .inviteCodeFailed: return "초대코드 생성에 실패했습니다. 다시 시도해 주세요." case .requestKeyConflict: return "클래스 생성 요청이 충돌했어요. 입력 내용을 확인하고 다시 시도해 주세요." - case let .network(message), let .unknown(message): return message + case let .network(message): return message + case let .userMessage(message): return message + case .unknown: return "클래스 요청을 처리하지 못했습니다. 다시 시도해 주세요." } } diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Room/ClassRoomInterface.swift b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Room/ClassRoomInterface.swift index 748b4666..50fad1ad 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Room/ClassRoomInterface.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Room/ClassRoomInterface.swift @@ -23,7 +23,7 @@ public protocol ClassRoomInterface: Sendable { public extension ClassRoomInterface { func updateName(id _: Int, name _: String) async throws -> ClassRoom { - throw ClassError.unknown("클래스 이름 변경을 지원하지 않습니다.") + throw ClassError.userMessage("클래스 이름 변경을 지원하지 않습니다.") } func fetchClassPreview(joinCode: String) async throws -> ClassJoinPreview { diff --git a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassPerspectiveDTO.swift b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassPerspectiveDTO.swift index 1bb7d9b9..7d0d77f9 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassPerspectiveDTO.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassPerspectiveDTO.swift @@ -10,7 +10,7 @@ struct ClassPerspectiveReceiptDTO: Decodable, Sendable { struct ClassPerspectiveItemDTO: Decodable, Sendable { struct User: Decodable, Sendable { - let userTag: String + let userTag: String? let nickname: String let characterType: String? let characterImageUrl: String? diff --git a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassMemberDTO+.swift b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassMemberDTO+.swift index bf06f21d..f8168db2 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassMemberDTO+.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassMemberDTO+.swift @@ -15,7 +15,7 @@ extension ClassMemberDTO { extension ClassMemberDTO { static func toMemberList(_ payload: [Self]) throws -> ClassMemberList { guard let current = payload.first(where: \.me) else { - throw ClassError.unknown("내 멤버 정보를 찾을 수 없어요.") + throw ClassError.userMessage("내 멤버 정보를 찾을 수 없어요.") } return ClassMemberList( members: payload.map { $0.toDomain() }, diff --git a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassPerspectiveDTO+.swift b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassPerspectiveDTO+.swift index e99be720..beaf61eb 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassPerspectiveDTO+.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassPerspectiveDTO+.swift @@ -14,7 +14,7 @@ extension ClassPerspectiveReceiptDTO { extension ClassPerspectiveItemDTO.User { func toDomain() -> ClassPerspectiveAuthor { ClassPerspectiveAuthor( - userTag: userTag, + userTag: userTag ?? "", nickname: nickname, characterType: characterType, characterImageURL: characterImageUrl.flatMap(URL.init(string:)) diff --git a/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Recommendation.swift b/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Recommendation.swift index 040b152f..50b1051c 100644 --- a/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Recommendation.swift +++ b/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Recommendation.swift @@ -13,7 +13,7 @@ import PickeNetwork extension ClassRepositoryImpl { public func fetchRecommendedBattles(filter: ClassTopicFilter) async throws -> [ClassBattleSummary] { guard (1 ... 3).contains(filter.categories.count), filter.keyword.count <= 50 else { - throw ClassError.unknown("추천 조건을 확인해 주세요.") + throw ClassError.userMessage("추천 조건을 확인해 주세요.") } do { let data = try await client.send( diff --git a/Projects/Domain/ClassDomain/Tests/Sources/ClassBattleAPIContractTests.swift b/Projects/Domain/ClassDomain/Tests/Sources/ClassBattleAPIContractTests.swift index 9fa50632..b0e1eb9a 100644 --- a/Projects/Domain/ClassDomain/Tests/Sources/ClassBattleAPIContractTests.swift +++ b/Projects/Domain/ClassDomain/Tests/Sources/ClassBattleAPIContractTests.swift @@ -114,7 +114,7 @@ struct ClassBattleAPIContractTests { "postVote":{"optionId":32,"label":"B","title":"창작이 아니다"},"status":"COMPLETED","opinionChanged":true} """ let pageJSON = """ - {"items":[{"perspectiveId":50,"user":{"userTag":"u1","nickname":"피케"}, + {"items":[{"perspectiveId":50,"user":{"userTag":null,"nickname":"피케"}, "option":{"optionId":31,"label":"A","title":"창작이다"},"content":"의견", "likeCount":3,"commentCount":1,"isLiked":false,"isMyPerspective":true, "createdAt":"2026-10-21T12:00:00"}],"nextCursor":"next","hasNext":true} @@ -125,6 +125,7 @@ struct ClassBattleAPIContractTests { #expect(history.status == .completed) #expect(history.opinionChanged == true) #expect(page.items.first?.id == 50) + #expect(page.items.first?.author.userTag == "") #expect(page.items.first?.isMyPerspective == true) #expect(page.nextCursor == "next") #expect(page.hasNext) diff --git a/Projects/Domain/ClassDomain/Tests/Sources/ClassErrorMappingTests.swift b/Projects/Domain/ClassDomain/Tests/Sources/ClassErrorMappingTests.swift index f774a29b..f2609eae 100644 --- a/Projects/Domain/ClassDomain/Tests/Sources/ClassErrorMappingTests.swift +++ b/Projects/Domain/ClassDomain/Tests/Sources/ClassErrorMappingTests.swift @@ -44,4 +44,18 @@ struct ClassErrorMappingTests { func 메시지가_없으면_기본_문구를_보여준다() { #expect(ClassErrorMapper.map(code: nil, message: nil, fallback: "기본 문구") == .network("기본 문구")) } + + @Test + func 알수없는_내부_오류는_구현_상세를_화면에_표시하지_않는다() { + let error = ClassError.unknown("AuthenticationError.excessiveRefresh") + #expect(error.message == "클래스 요청을 처리하지 못했습니다. 다시 시도해 주세요.") + } + + @Test + func 알수없는_오류여도_명시적인_사용자_안내는_유지한다() { + let message = "AI 주제 생성 시간이 초과됐어요. 다시 확인해 주세요." + #expect(ClassError.userMessage(message).message == message) + #expect(ClassError.unknown("AuthenticationError.excessiveRefresh: 인증 토큰 갱신 실패").message != + "AuthenticationError.excessiveRefresh: 인증 토큰 갱신 실패") + } } diff --git a/Projects/Domain/ClassDomain/Tests/Sources/ClassMemberAPITests.swift b/Projects/Domain/ClassDomain/Tests/Sources/ClassMemberAPITests.swift index 87d61e13..a2d9a270 100644 --- a/Projects/Domain/ClassDomain/Tests/Sources/ClassMemberAPITests.swift +++ b/Projects/Domain/ClassDomain/Tests/Sources/ClassMemberAPITests.swift @@ -49,7 +49,7 @@ struct ClassMemberAPITests { [{"memberId":10,"displayName":"운영자","role":"OWNER","characterUrl":null,"me":false}] """ let payload = try JSONDecoder().decode([ClassMemberDTO].self, from: Data(json.utf8)) - #expect(throws: ClassError.unknown("내 멤버 정보를 찾을 수 없어요.")) { + #expect(throws: ClassError.userMessage("내 멤버 정보를 찾을 수 없어요.")) { try ClassMemberDTO.toMemberList(payload) } } diff --git a/Projects/Domain/HomeDomain/Interface/Sources/Core/HomeBundle.swift b/Projects/Domain/HomeDomain/Interface/Sources/Core/HomeBundle.swift index c0d88d85..de2b8b4d 100644 --- a/Projects/Domain/HomeDomain/Interface/Sources/Core/HomeBundle.swift +++ b/Projects/Domain/HomeDomain/Interface/Sources/Core/HomeBundle.swift @@ -13,6 +13,7 @@ public struct HomeBundle: Equatable { public let heroes: [HeroBattle] public let hotBattles: [HotBattle] public let bestBattles: [BestBattle] + public let bestComments: [HomeBestComment] public let quizzes: [QuizQuestion] public let votes: [VoteQuestion] public let newBattles: [NewBattle] @@ -22,6 +23,7 @@ public struct HomeBundle: Equatable { heroes: [HeroBattle], hotBattles: [HotBattle], bestBattles: [BestBattle], + bestComments: [HomeBestComment] = [], quizzes: [QuizQuestion], votes: [VoteQuestion], newBattles: [NewBattle] @@ -30,6 +32,7 @@ public struct HomeBundle: Equatable { self.heroes = heroes self.hotBattles = hotBattles self.bestBattles = bestBattles + self.bestComments = bestComments self.quizzes = quizzes self.votes = votes self.newBattles = newBattles diff --git a/Projects/Domain/HomeDomain/Interface/Sources/Section/Comment/HomeBestComment.swift b/Projects/Domain/HomeDomain/Interface/Sources/Section/Comment/HomeBestComment.swift new file mode 100644 index 00000000..918c55cc --- /dev/null +++ b/Projects/Domain/HomeDomain/Interface/Sources/Section/Comment/HomeBestComment.swift @@ -0,0 +1,30 @@ +// +// HomeBestComment.swift +// HomeDomainInterface +// + +import Foundation + +/// 홈 API가 제공하는 Best 댓글. 없는 battleId는 DTO 매핑에서 제외한다. +public struct HomeBestComment: Equatable, Identifiable { + public let battleId: Int + public let battleTitle: String + public let optionTitle: String + public let content: String + + public var id: Int { + battleId + } + + public init( + battleId: Int, + battleTitle: String, + optionTitle: String, + content: String + ) { + self.battleId = battleId + self.battleTitle = battleTitle + self.optionTitle = optionTitle + self.content = content + } +} diff --git a/Projects/Domain/HomeDomain/Sources/Model/HomeDataDTO+.swift b/Projects/Domain/HomeDomain/Sources/Model/HomeDataDTO+.swift index 0ab6a8b0..62c1e900 100644 --- a/Projects/Domain/HomeDomain/Sources/Model/HomeDataDTO+.swift +++ b/Projects/Domain/HomeDomain/Sources/Model/HomeDataDTO+.swift @@ -63,6 +63,18 @@ public extension BestBattleDTO { } } +public extension BestCommentDTO { + func toDomain() -> HomeBestComment? { + guard let battleId else { return nil } + return HomeBestComment( + battleId: battleId, + battleTitle: battleTitle ?? "", + optionTitle: optionTitle ?? "", + content: content ?? "" + ) + } +} + public extension TodayQuizDTO { func toDomain() -> QuizQuestion { QuizQuestion( @@ -127,6 +139,7 @@ public extension HomeDataDTO { bestBattles: bestBattles.enumerated().map { idx, dto in dto.toDomain(rank: idx + 1) }, + bestComments: bestComments?.compactMap { $0.toDomain() } ?? [], quizzes: todayQuizzes.map { $0.toDomain() }, votes: todayVotes.map { $0.toDomain() }, newBattles: newBattles.map { $0.toDomain() } diff --git a/Projects/Domain/HomeDomain/Sources/Model/HomeDataDTO.swift b/Projects/Domain/HomeDomain/Sources/Model/HomeDataDTO.swift index ffafba67..3ff4891b 100644 --- a/Projects/Domain/HomeDomain/Sources/Model/HomeDataDTO.swift +++ b/Projects/Domain/HomeDomain/Sources/Model/HomeDataDTO.swift @@ -15,11 +15,20 @@ public struct HomeDataDTO: Decodable { public let editorPicks: [EditorPickDTO] public let trendingBattles: [TrendingBattleDTO] public let bestBattles: [BestBattleDTO] + public let bestComments: [BestCommentDTO]? public let todayQuizzes: [TodayQuizDTO] public let todayVotes: [TodayVoteDTO] public let newBattles: [NewBattleDTO] } +/// Android 홈 계약의 선택 필드. 현재 서버가 아직 보내지 않는 경우에도 홈 로드는 유지한다. +public struct BestCommentDTO: Decodable { + public let battleId: Int? + public let battleTitle: String? + public let optionTitle: String? + public let content: String? +} + public struct TagDTO: Decodable, Equatable { public let tagId: Int public let name: String diff --git a/Projects/Domain/HomeDomain/Tests/Sources/HomeRepositoryTests.swift b/Projects/Domain/HomeDomain/Tests/Sources/HomeRepositoryTests.swift index b826a746..8cde60e8 100644 --- a/Projects/Domain/HomeDomain/Tests/Sources/HomeRepositoryTests.swift +++ b/Projects/Domain/HomeDomain/Tests/Sources/HomeRepositoryTests.swift @@ -70,6 +70,10 @@ struct HomeRepositoryTests { "viewCount": 900 } ], + "bestComments": [ + { "battleId": 21, "battleTitle": "인간은 본래 선한가", "optionTitle": "선하다", "content": "선하게 태어나요" }, + { "battleId": null, "battleTitle": "제외", "optionTitle": "", "content": "" } + ], "todayQuizzes": [ { "battleId": 31, @@ -175,6 +179,12 @@ struct HomeRepositoryTests { #expect(bundle.bestBattles[1].rank == 2) #expect(bundle.bestBattles[1].battleId == 22) #expect(bundle.bestBattles[1].tags.isEmpty) + #expect(bundle.bestComments == [HomeBestComment( + battleId: 21, + battleTitle: "인간은 본래 선한가", + optionTitle: "선하다", + content: "선하게 태어나요" + )]) #expect(bundle.quizzes.count == 1) #expect(bundle.quizzes[0].isCorrectA == true) @@ -205,6 +215,7 @@ struct HomeRepositoryTests { #expect(bundle.heroes.isEmpty) #expect(bundle.hotBattles.isEmpty) #expect(bundle.bestBattles.isEmpty) + #expect(bundle.bestComments.isEmpty) #expect(bundle.quizzes.isEmpty) #expect(bundle.votes.isEmpty) #expect(bundle.newBattles.isEmpty) diff --git a/Projects/Feature/Chat/Sources/CommentReply/Reducer/CommentReplyFeature.swift b/Projects/Feature/Chat/Sources/CommentReply/Reducer/CommentReplyFeature.swift index 06c94537..542d16a7 100644 --- a/Projects/Feature/Chat/Sources/CommentReply/Reducer/CommentReplyFeature.swift +++ b/Projects/Feature/Chat/Sources/CommentReply/Reducer/CommentReplyFeature.swift @@ -147,6 +147,7 @@ public struct CommentReplyFeature { case createResponse(Result) case updateResponse(Result, commentId: Int) case deleteResponse(Result) + case parentDeleteResponse(Result) case parentLikeResponse(Result) case replyLikeResponse(Result) case parentLikesResponse(Result) @@ -491,9 +492,14 @@ extension CommentReplyFeature { case .deleteParent: let perspectiveId = state.perspectiveId return .run { [repository = perspectiveUseCase] send in - try? await repository.deletePerspective(perspectiveId: perspectiveId) - await send(.delegate(.dismiss)) + let result = await Result { + try await repository.deletePerspective(perspectiveId: perspectiveId) + return perspectiveId + } + .mapError(PerspectiveError.from) + await send(.inner(.parentDeleteResponse(result))) } + .cancellable(id: CancelID.mutate, cancelInFlight: false) case .reportParent: let perspectiveId = state.perspectiveId @@ -566,6 +572,19 @@ extension CommentReplyFeature { } return .none + case let .parentDeleteResponse(result): + switch result { + case .success: + return .send(.delegate(.dismiss)) + case let .failure(error): + PickeLogger.error("[CommentReplyFeature] deleteParent failed: \(error.localizedDescription)", category: .ui) + state.customAlert = .alert( + title: "의견을 삭제할 수 없어요", + message: "클래스 설정이나 진행 상태를 확인해 주세요." + ) + return .none + } + case let .parentLikeResponse(result): switch result { case let .success(payload): diff --git a/Projects/Feature/Class/Sources/ClassRecommend/Reducer/ClassRecommendFeature.swift b/Projects/Feature/Class/Sources/ClassRecommend/Reducer/ClassRecommendFeature.swift index 7c757696..d0886092 100644 --- a/Projects/Feature/Class/Sources/ClassRecommend/Reducer/ClassRecommendFeature.swift +++ b/Projects/Feature/Class/Sources/ClassRecommend/Reducer/ClassRecommendFeature.swift @@ -377,10 +377,10 @@ extension ClassRecommendFeature { current = try await useCase.fetchTopicDraft(id: draft.id) } guard current.suggestStatus != "GENERATING" else { - throw ClassError.unknown("AI 주제 생성 시간이 초과됐어요. 다시 확인해 주세요.") + throw ClassError.userMessage("AI 주제 생성 시간이 초과됐어요. 다시 확인해 주세요.") } guard current.suggestStatus == "SUCCEEDED" else { - throw ClassError.unknown(current.clarificationMessage ?? "AI 주제를 만들지 못했어요. 조건을 확인해 주세요.") + throw ClassError.userMessage(current.clarificationMessage ?? "AI 주제를 만들지 못했어요. 조건을 확인해 주세요.") } return current } diff --git a/Projects/Feature/Class/Sources/ClassReport/Reducer/ClassReportFeature.swift b/Projects/Feature/Class/Sources/ClassReport/Reducer/ClassReportFeature.swift index ba243350..9288d770 100644 --- a/Projects/Feature/Class/Sources/ClassReport/Reducer/ClassReportFeature.swift +++ b/Projects/Feature/Class/Sources/ClassReport/Reducer/ClassReportFeature.swift @@ -90,13 +90,17 @@ public struct ClassReportFeature { } public var serverParticipationRate: String { - guard let rate = classResult?.stats.participationRate else { return "" } - return rate.formatted(.number.precision(.fractionLength(0...1))) + "%" + guard let stats = classResult?.stats else { return "" } + guard stats.activeMemberCount > 0 else { return "아직 참여 없음" } + return (Double(stats.completedCount) / Double(stats.activeMemberCount) * 100) + .formatted(.number.precision(.fractionLength(0...1))) + "%" } public var serverCommentRate: String { - guard let rate = classResult?.stats.commentRate else { return "" } - return rate.formatted(.number.precision(.fractionLength(0...1))) + "%" + guard let stats = classResult?.stats else { return "" } + guard stats.activeMemberCount > 0 else { return "아직 참여 없음" } + return (Double(stats.commentWriterCount) / Double(stats.activeMemberCount) * 100) + .formatted(.number.precision(.fractionLength(0...1))) + "%" } public var serverParticipationCount: String { @@ -110,11 +114,13 @@ public struct ClassReportFeature { } public var serverParticipationProgress: Double { - min(max((classResult?.stats.participationRate ?? 0) / 100, 0), 1) + guard let stats = classResult?.stats, stats.activeMemberCount > 0 else { return 0 } + return min(max(Double(stats.completedCount) / Double(stats.activeMemberCount), 0), 1) } public var serverCommentProgress: Double { - min(max((classResult?.stats.commentRate ?? 0) / 100, 0), 1) + guard let stats = classResult?.stats, stats.activeMemberCount > 0 else { return 0 } + return min(max(Double(stats.commentWriterCount) / Double(stats.activeMemberCount), 0), 1) } public var serverOptionCounts: [Int: String] { @@ -124,12 +130,18 @@ public struct ClassReportFeature { } } + public var serverDistributionTotalCount: Int { + classResult?.stats.options.reduce(0) { $0 + max($1.postVoteCount, 0) } ?? 0 + } + public var serverDistributionTotalText: String { - "\(classResult?.stats.postVoteCount ?? 0)명" + "\(serverDistributionTotalCount)명" } public var serverDistributionProgress: Double { - min(max((classResult?.stats.options.first?.postVoteRatio ?? 0) / 100, 0), 1) + guard serverDistributionTotalCount > 0 else { return 0 } + return Double(max(classResult?.stats.options.first?.postVoteCount ?? 0, 0)) + / Double(serverDistributionTotalCount) } public var serverDistributionOptionCountTexts: [Int: String] { @@ -141,8 +153,19 @@ public struct ClassReportFeature { public var serverDistributionOptionRatioTexts: [Int: String] { guard let options = classResult?.stats.options else { return [:] } - return options.reduce(into: [:]) { values, option in - values[option.id] = option.postVoteRatio.formatted(.number.precision(.fractionLength(0...1))) + "%" + guard serverDistributionTotalCount > 0 else { + return options.reduce(into: [:]) { $0[$1.id] = "아직 참여 없음" } + } + var remainingTenths = 1000 + return options.enumerated().reduce(into: [:]) { values, pair in + let (index, option) = pair + let tenths = index == options.count - 1 + ? remainingTenths + : Int((Double(max(option.postVoteCount, 0)) * 1000 + / Double(serverDistributionTotalCount)).rounded()) + remainingTenths -= tenths + values[option.id] = (Double(tenths) / 10) + .formatted(.number.precision(.fractionLength(0...1))) + "%" } } diff --git a/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift b/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift index 018b02cc..50673f6e 100644 --- a/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift +++ b/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift @@ -162,6 +162,7 @@ private extension ClassReportView { participationBar( title: "배틀 참여율", rate: store.serverParticipationRate, + caption: "참여한 멤버", detail: store.serverParticipationCount, progress: store.serverParticipationProgress, color: .primary500 @@ -169,6 +170,7 @@ private extension ClassReportView { participationBar( title: "댓글 작성률", rate: store.serverCommentRate, + caption: "댓글을 남긴 멤버", detail: store.serverCommentCount, progress: store.serverCommentProgress, color: .secondary500 @@ -283,6 +285,7 @@ private extension ClassReportView { func participationBar( title: String, rate: String, + caption: String, detail: String, progress: Double, color: Color @@ -296,16 +299,20 @@ private extension ClassReportView { .pretendardFont(.semiBold12) } .foregroundStyle(.gray800) - Text(detail) - .pretendardFont(.medium10) - .foregroundStyle(.gray300) + HStack { + Text(caption) + Spacer() + Text(detail) + } + .pretendardFont(.medium10) + .foregroundStyle(.gray300) GeometryReader { geometry in ZStack(alignment: .leading) { Capsule().fill(.beige500) Capsule().fill(color).frame(width: geometry.size.width * progress) } } - .frame(height: 5) + .frame(height: 6) } .padding(.vertical, 2) } @@ -314,7 +321,11 @@ private extension ClassReportView { func stanceDistribution(_ options: [ClassResultOption]) -> some View { HStack(spacing: 24) { ZStack { - Circle().stroke(.secondary500, lineWidth: 12) + Circle() + .stroke( + store.serverDistributionTotalCount == 0 ? Color.beige500 : Color.secondary500, + lineWidth: 12 + ) Circle() .trim(from: 0, to: store.serverDistributionProgress) .stroke(.primary500, style: StrokeStyle(lineWidth: 12, lineCap: .butt)) diff --git a/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift b/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift index 9fe667c8..84d3b479 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift @@ -61,20 +61,26 @@ struct ClassReportTests { private func result( room: ClassRoom, reportState: String?, - postVoteRatio: Double = 54.5 + postVoteRatio: Double = 54.5, + activeMemberCount: Int = 12, + firstCount: Int = 6, + secondCount: Int = 5 ) -> ClassResult { ClassResult( classID: room.id, closed: true, closeAt: room.deadline, stats: .init( - activeMemberCount: 12, - completedCount: 10, + activeMemberCount: activeMemberCount, + completedCount: min(10, activeMemberCount), participationRate: 83.3, - commentWriterCount: 9, + commentWriterCount: min(9, activeMemberCount), commentRate: 75, - postVoteCount: 11, - options: [.init(id: 31, title: "창작이다", preVoteCount: 8, postVoteCount: 6, postVoteRatio: postVoteRatio)] + postVoteCount: firstCount + secondCount, + options: [ + .init(id: 31, title: "창작이다", preVoteCount: 8, postVoteCount: firstCount, postVoteRatio: postVoteRatio), + .init(id: 32, title: "아니다", preVoteCount: 4, postVoteCount: secondCount, postVoteRatio: 100 - postVoteRatio) + ] ), myReportState: reportState, insightState: nil @@ -93,27 +99,49 @@ struct ClassReportTests { #expect(state.serverCommentRate == "75%") #expect(state.serverParticipationCount == "10 / 12명") #expect(state.serverCommentCount == "9 / 12명") - #expect(abs(state.serverParticipationProgress - 0.833) < 0.0001) + #expect(abs(state.serverParticipationProgress - 0.833) < 0.001) #expect(abs(state.serverCommentProgress - 0.75) < 0.0001) let option = try #require(state.classResult?.stats.options.first) #expect(state.serverOptionCounts[option.id] == "사전 8 · 사후 6") #expect(state.serverDistributionTotalText == "11명") - #expect(abs(state.serverDistributionProgress - 0.545) < 0.0001) + #expect(abs(state.serverDistributionProgress - (6.0 / 11.0)) < 0.0001) #expect(state.serverDistributionOptionCountTexts[option.id] == "6명") #expect(state.serverDistributionOptionRatioTexts[option.id] == "54.5%") + #expect(state.serverDistributionOptionRatioTexts[32] == "45.5%") state.classResult = result( room: .mockClosedSociety, reportState: "SUCCEEDED", postVoteRatio: -10 ) - #expect(state.serverDistributionProgress == 0) + #expect(abs(state.serverDistributionProgress - (6.0 / 11.0)) < 0.0001) state.classResult = result( room: .mockClosedSociety, reportState: "SUCCEEDED", postVoteRatio: 110 ) - #expect(state.serverDistributionProgress == 1) + #expect(abs(state.serverDistributionProgress - 0.545) < 0.001) + } + + @Test + func 참여_대상과_사후_투표가_없으면_미참여_상태를_표시한다() { + var state = ClassReportFeature.State(room: .mockClosedSociety, instanceID: UUID()) + state.classResult = result( + room: .mockClosedSociety, + reportState: "SUCCEEDED", + activeMemberCount: 0, + firstCount: 0, + secondCount: 0 + ) + #expect(state.serverParticipationRate == "아직 참여 없음") + #expect(state.serverCommentRate == "아직 참여 없음") + #expect(state.serverParticipationProgress == 0) + #expect(state.serverCommentProgress == 0) + #expect(state.serverDistributionTotalCount == 0) + #expect(state.serverDistributionTotalText == "0명") + #expect(state.serverDistributionProgress == 0) + #expect(state.serverDistributionOptionRatioTexts[31] == "아직 참여 없음") + #expect(state.serverDistributionOptionRatioTexts[32] == "아직 참여 없음") } @Test diff --git a/Projects/Feature/Home/Sources/Main/Reducer/HomeFeature.swift b/Projects/Feature/Home/Sources/Main/Reducer/HomeFeature.swift index 8cbed6c9..7edfb2d3 100644 --- a/Projects/Feature/Home/Sources/Main/Reducer/HomeFeature.swift +++ b/Projects/Feature/Home/Sources/Main/Reducer/HomeFeature.swift @@ -34,6 +34,7 @@ public struct HomeFeature { public var heroIndex: Int = 0 public var hotBattles: [HotBattle] = [] public var bestBattles: [BestBattle] = [] + public var bestComments: [HomeBestComment] = [] public var quizzes: [QuizQuestion] = [] public var votes: [VoteQuestion] = [] public var newBattles: [NewBattle] = [] @@ -49,11 +50,17 @@ public struct HomeFeature { public var currentQuiz: QuizQuestion? { quizzes.first } public var currentVote: VoteQuestion? { votes.first } + public var hasVisibleHomeContent: Bool { + !heroes.isEmpty || !hotBattles.isEmpty || !bestBattles.isEmpty || + !bestComments.isEmpty || !newBattles.isEmpty + } + public var shouldShowSkeleton: Bool { (viewState == .loading || !hasLoadedHome) && heroes.isEmpty && hotBattles.isEmpty && bestBattles.isEmpty && + bestComments.isEmpty && quizzes.isEmpty && votes.isEmpty && newBattles.isEmpty @@ -80,6 +87,7 @@ public struct HomeFeature { case heroTapped(HeroBattle) case hotBattleTapped(HotBattle) case bestBattleTapped(BestBattle) + case bestCommentTapped(HomeBestComment) case newBattleTapped(NewBattle) case notificationTapped /// 홈 피드 네이티브 광고 클릭 @@ -205,6 +213,13 @@ extension HomeFeature { ))) return .send(.delegate(.presentPreVote(battleId: battle.battleId))) + case let .bestCommentTapped(comment): + analyticsUseCase.track(.contentAction(ContentActionData( + action: .battleCardTap, + contentID: "\(comment.battleId)" + ))) + return .send(.delegate(.presentPreVote(battleId: comment.battleId))) + case let .newBattleTapped(battle): analyticsUseCase.track(.contentAction(ContentActionData( action: .newBattleTap, @@ -278,6 +293,7 @@ extension HomeFeature { state.heroIndex = 0 state.hotBattles = home.hotBattles state.bestBattles = home.bestBattles + state.bestComments = home.bestComments state.quizzes = home.quizzes state.votes = home.votes state.newBattles = home.newBattles diff --git a/Projects/Feature/Home/Sources/Main/View/Components/HomeBestCommentRow.swift b/Projects/Feature/Home/Sources/Main/View/Components/HomeBestCommentRow.swift new file mode 100644 index 00000000..17370564 --- /dev/null +++ b/Projects/Feature/Home/Sources/Main/View/Components/HomeBestCommentRow.swift @@ -0,0 +1,46 @@ +// +// HomeBestCommentRow.swift +// Home +// + +import HomeDomainInterface +import PickeDesignKit +import SwiftUI + +struct HomeBestCommentRow: View { + let comment: HomeBestComment + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + Text(comment.battleTitle) + .pretendardFont(.semiBold12) + .foregroundStyle(.neutral300) + .lineLimit(1) + + HStack(alignment: .top, spacing: 4) { + Image(systemName: "arrow.turn.down.right") + .foregroundStyle(.primary500) + .frame(width: 16, height: 16) + Text(attributedContent) + .pretendardFont(.bodySmall) + .foregroundStyle(.neutral500) + .lineLimit(2) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + .padding(16) + .frame(maxWidth: .infinity, alignment: .leading) + } + + private var attributedContent: AttributedString { + guard !comment.optionTitle.isEmpty else { + return AttributedString(comment.content) + } + var text = AttributedString(" \(comment.optionTitle) \(comment.content)") + if let range = text.range(of: comment.optionTitle) { + text[range].foregroundColor = .primary500 + text[range].backgroundColor = .beige600 + } + return text + } +} diff --git a/Projects/Feature/Home/Sources/Main/View/Components/HomeSectionHeader.swift b/Projects/Feature/Home/Sources/Main/View/Components/HomeSectionHeader.swift index 620ba433..8ec60a4b 100644 --- a/Projects/Feature/Home/Sources/Main/View/Components/HomeSectionHeader.swift +++ b/Projects/Feature/Home/Sources/Main/View/Components/HomeSectionHeader.swift @@ -12,7 +12,7 @@ import PickeDesignKit /// "지금 뜨는 배틀 / 더 보기" 같은 섹션 헤더 공통 컴포넌트. struct HomeSectionHeader: View { let title: String - let onSeeMoreTapped: () -> Void + let onSeeMoreTapped: (() -> Void)? var body: some View { HStack(spacing: 12) { @@ -20,35 +20,23 @@ struct HomeSectionHeader: View { .pretendardFont(.bold18) .kerning(-0.45) Spacer(minLength: 0) - Button(action: onSeeMoreTapped) { - Text("더 보기") - .pretendardFont(.medium13) - .foregroundStyle(.neutral300) + if let onSeeMoreTapped { + Button(action: onSeeMoreTapped) { + Text("더 보기") + .pretendardFont(.medium13) + .foregroundStyle(.neutral300) + } } } .padding(.horizontal, 16) } - /// 강조 규칙: - /// - 라틴 단어 (Best · Pické 등) 가 있으면 그 단어만 primary500, 나머지 한글은 neutral900 - /// - 라틴 단어가 없으면 한글 `배틀` 만 primary500, 나머지 한글은 neutral900 + /// Figma 홈 헤더: 섹션 주제어(배틀/댓글)만 primary500으로 강조한다. private var attributedTitle: AttributedString { var attr = AttributedString(title) attr.foregroundColor = .neutral900 - - let latinPattern = /[A-Za-zÀ-ÿ]+/ - let latinMatches = Array(title.matches(of: latinPattern)) - - if latinMatches.isEmpty { - if let range = attr.range(of: "배틀") { - attr[range].foregroundColor = .primary500 - } - } else { - for match in latinMatches { - if let range = attr.range(of: String(match.0)) { - attr[range].foregroundColor = .primary500 - } - } + if let range = attr.range(of: "배틀") ?? attr.range(of: "댓글") { + attr[range].foregroundColor = .primary500 } return attr } diff --git a/Projects/Feature/Home/Sources/Main/View/Components/HomeSkeletonView.swift b/Projects/Feature/Home/Sources/Main/View/Components/HomeSkeletonView.swift index 9cfd5436..95595c1e 100644 --- a/Projects/Feature/Home/Sources/Main/View/Components/HomeSkeletonView.swift +++ b/Projects/Feature/Home/Sources/Main/View/Components/HomeSkeletonView.swift @@ -15,7 +15,7 @@ struct HomeSkeletonView: View { HomeHeroSkeletonView() HomeHotBattlesSkeletonView() HomeBestBattlesSkeletonView() - HomeTodayPickeSkeletonView() + HomeBestCommentsSkeletonView() HomeNewBattlesSkeletonView() } .padding(.bottom, 24) @@ -119,70 +119,28 @@ private struct HomeBestBattlesSkeletonView: View { } } -private struct HomeTodayPickeSkeletonView: View { +private struct HomeBestCommentsSkeletonView: View { var body: some View { - VStack(alignment: .leading, spacing: 16) { - SkeletonSectionHeader(width: 104) - VStack(spacing: 16) { - todayQuizCard() - todayVoteCard() - } - .padding(.horizontal, 16) - } - } - - @ViewBuilder - private func todayQuizCard() -> some View { - VStack(alignment: .leading, spacing: 20) { - HStack { - SkeletonBlock(width: 42, height: 20, color: .primary50) - Spacer() - SkeletonBlock(width: 76, height: 12) - } - VStack(spacing: 8) { - SkeletonBlock(width: 220, height: 16) - SkeletonBlock(width: 260, height: 12) - SkeletonBlock(width: 180, height: 12) - } - .frame(maxWidth: .infinity) - - HStack(spacing: 8) { - SkeletonBlock(height: 74, color: .beige50) - SkeletonBlock(height: 74, color: .beige50) - } - } - .padding(.vertical, 20) - .padding(.horizontal, 16) - .pickeCard(.beige400, border: .beige700) - } - - @ViewBuilder - private func todayVoteCard() -> some View { - VStack(alignment: .leading, spacing: 20) { - HStack { - SkeletonBlock(width: 42, height: 20, color: .primary50) - Spacer() - SkeletonBlock(width: 76, height: 12) - } - VStack(spacing: 8) { - HStack(spacing: 8) { - SkeletonBlock(width: 78, height: 16) - SkeletonBlock(width: 44, height: 24, color: .beige50) - SkeletonBlock(width: 24, height: 16) - } - SkeletonBlock(width: 230, height: 12) - } - .frame(maxWidth: .infinity) - - LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: 2), spacing: 8) { - ForEach(0 ..< 4, id: \.self) { _ in - SkeletonBlock(height: 44, color: .beige50) + VStack(alignment: .leading, spacing: 12) { + SkeletonSectionHeader(width: 100) + VStack(spacing: 0) { + ForEach(0 ..< 3, id: \.self) { index in + VStack(alignment: .leading, spacing: 8) { + SkeletonBlock(width: 220, height: 12) + HStack(spacing: 4) { + SkeletonBlock(width: 16, height: 16) + SkeletonBlock(width: 48, height: 16, color: .beige600) + SkeletonBlock(width: 210, height: 12) + } + } + .padding(16) + if index < 2 { + Divider().background(.beige600) + } } } + .padding(.horizontal, 16) } - .padding(.vertical, 20) - .padding(.horizontal, 16) - .pickeCard(.beige50, border: .beige700) } } diff --git a/Projects/Feature/Home/Sources/Main/View/HomeView.swift b/Projects/Feature/Home/Sources/Main/View/HomeView.swift index 86ac10ef..8565a0ad 100644 --- a/Projects/Feature/Home/Sources/Main/View/HomeView.swift +++ b/Projects/Feature/Home/Sources/Main/View/HomeView.swift @@ -32,6 +32,8 @@ public struct HomeView: View { ScrollView(showsIndicators: false) { if store.shouldShowSkeleton { HomeSkeletonView() + } else if !store.hasVisibleHomeContent { + emptyHomeSection() } else { VStack(spacing: 32) { if !store.heroes.isEmpty { @@ -42,18 +44,15 @@ public struct HomeView: View { ) } - if !store.hotBattles.isEmpty { - hotBattlesSection() + hotBattlesSection() + if !store.hotBattles.isEmpty || !store.bestBattles.isEmpty { adSection() } if !store.bestBattles.isEmpty { bestBattlesSection() - if store.hotBattles.isEmpty { - adSection() - } } - if !store.quizzes.isEmpty || !store.votes.isEmpty { - todayPickeSection() + if !store.bestComments.isEmpty { + bestCommentsSection() } if !store.newBattles.isEmpty { newBattlesSection() @@ -82,6 +81,21 @@ public struct HomeView: View { // MARK: - Sections extension HomeView { + @ViewBuilder + private func emptyHomeSection() -> some View { + VStack(spacing: 16) { + Image(asset: .noDataLogo) + .resizable() + .scaledToFit() + .frame(width: 160, height: 120) + Text("아직 준비된 배틀이 없어요!") + .pretendardFont(.bodyMedium) + .foregroundStyle(.beige800) + } + .frame(maxWidth: .infinity) + .frame(minHeight: 500) + } + /// 배틀 섹션 아래 네이티브 광고 — AdFit 은 한 화면에 같은 단위 1개만 허용해 호출부에서 하나만 그린다. @ViewBuilder private func adSection() -> some View { @@ -98,53 +112,61 @@ extension HomeView { HomeSectionHeader(title: "지금 뜨는 배틀") { send(.seeMoreTapped(.hotBattles)) } - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 16) { - ForEach(store.hotBattles) { battle in - HotBattleCardView(battle: battle) - .contentShape(Rectangle()) - .onTapGesture { send(.hotBattleTapped(battle)) } + if store.hotBattles.isEmpty { + Text("지금 뜨는 배틀이 아직 없어요") + .pretendardFont(.bodyMedium) + .foregroundStyle(.neutral300) + .frame(width: 220, height: 274) + .background(.beige50) + .roundedBorder(.beige600, lineWidth: 1) + .padding(.horizontal, 16) + } else { + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 16) { + ForEach(store.hotBattles) { battle in + HotBattleCardView(battle: battle) + .contentShape(Rectangle()) + .onTapGesture { send(.hotBattleTapped(battle)) } + } } + .padding(.horizontal, 16) } - .padding(.horizontal, 16) } } } @ViewBuilder - private func bestBattlesSection() -> some View { + private func bestCommentsSection() -> some View { VStack(alignment: .leading, spacing: 12) { - HomeSectionHeader(title: "Best 배틀") { - send(.seeMoreTapped(.bestBattles)) - } + HomeSectionHeader(title: "Best 댓글", onSeeMoreTapped: nil) VStack(spacing: 0) { - ForEach(Array(store.bestBattles.enumerated()), id: \.element.id) { index, battle in - BestBattleCardView(battle: battle) + ForEach(Array(store.bestComments.enumerated()), id: \.offset) { index, comment in + HomeBestCommentRow(comment: comment) .contentShape(Rectangle()) - .onTapGesture { send(.bestBattleTapped(battle)) } - if index < store.bestBattles.count - 1 { + .onTapGesture { send(.bestCommentTapped(comment)) } + if index < store.bestComments.count - 1 { PickeDivider(.beige600) + .padding(.horizontal, 16) } } } - .padding(.horizontal, 16) } } @ViewBuilder - private func todayPickeSection() -> some View { - VStack(alignment: .leading, spacing: 16) { - HomeSectionHeader(title: "오늘의 Pické") { - send(.seeMoreTapped(.todayPicke)) + private func bestBattlesSection() -> some View { + VStack(alignment: .leading, spacing: 12) { + HomeSectionHeader(title: "Best 배틀") { + send(.seeMoreTapped(.bestBattles)) } - VStack(spacing: 16) { - if let quiz = store.currentQuiz { - QuizCardView(question: quiz) - } - if let vote = store.currentVote { - VoteCardView(question: vote) + VStack(spacing: 0) { + ForEach(Array(store.bestBattles.enumerated()), id: \.element.id) { index, battle in + BestBattleCardView(battle: battle) .contentShape(Rectangle()) - .onTapGesture {} + .onTapGesture { send(.bestBattleTapped(battle)) } + if index < store.bestBattles.count - 1 { + PickeDivider(.beige600) + } } } .padding(.horizontal, 16) diff --git a/Projects/Feature/Home/Tests/Sources/HomeTests.swift b/Projects/Feature/Home/Tests/Sources/HomeTests.swift index 9e79b66b..a8762fdf 100644 --- a/Projects/Feature/Home/Tests/Sources/HomeTests.swift +++ b/Projects/Feature/Home/Tests/Sources/HomeTests.swift @@ -53,6 +53,38 @@ struct HomeTests { #expect(store.state.hasUnreadNotification == false) #expect(store.state.shouldShowSkeleton == false) } + + @Test + @MainActor + func homeResponseStoresBestCommentsWithoutMockFallback() async { + let comment = HomeBestComment( + battleId: 21, + battleTitle: "인간은 본래 선한가", + optionTitle: "선하다", + content: "선하게 태어나요" + ) + let store = TestStore(initialState: HomeFeature.State()) { + HomeFeature() + } + + await store.send(.inner(.homeResponse(.success(.commentsOnly(comment))))) { + $0.hasLoadedHome = true + $0.bestComments = [comment] + } + + #expect(store.state.hotBattles.isEmpty) + #expect(store.state.hasVisibleHomeContent) + #expect(store.state.shouldShowSkeleton == false) + } + + @Test + func loadedEmptyHomeHasNoVisibleContent() { + var state = HomeFeature.State() + state.hasLoadedHome = true + + #expect(state.shouldShowSkeleton == false) + #expect(state.hasVisibleHomeContent == false) + } } private extension HomeBundle { @@ -65,4 +97,17 @@ private extension HomeBundle { votes: [], newBattles: [] ) + + static func commentsOnly(_ comment: HomeBestComment) -> HomeBundle { + HomeBundle( + newNotice: false, + heroes: [], + hotBattles: [], + bestBattles: [], + bestComments: [comment], + quizzes: [], + votes: [], + newBattles: [] + ) + } } diff --git a/Tools/FigmaClassAudit/README.md b/Tools/FigmaClassAudit/README.md index 2eadc6ce..6a2b04fa 100644 --- a/Tools/FigmaClassAudit/README.md +++ b/Tools/FigmaClassAudit/README.md @@ -9,15 +9,19 @@ | 최종 투표 | `13337:57185` | | 의견 | `13384:57726` | | 결과 | `13757:17445` | +| 홈 (375×2010) | `13956:18883` | ## 실행 1. Figma 데스크톱에서 [Pické 디자인 파일](https://www.figma.com/design/6Z49tKXvCCQDd7tyGtvtiI/)을 엽니다. 원본이 보기 전용이면 **Edit file menu → Duplicate to your drafts**로 개인 사본을 열어 주세요. 노드 ID는 사본에서도 유지됩니다. 2. **Plugins → Development → Import plugin from manifest…**에서 이 폴더의 `manifest.json`을 선택합니다. 3. **Plugins → Development → Picke Class Design Audit**를 실행합니다. -4. 각 행의 **PNG + JSON** 또는 **전체 ZIP 내려받기**를 누릅니다. +4. 각 행의 **PNG + JSON** 또는 **전체 ZIP 내려받기**를 누릅니다. 단일 화면도 PNG와 JSON을 하나의 ZIP으로 저장합니다. `13956:18883`은 새로 전달된 홈 프레임입니다. 화면 내용은 내보낸 PNG·JSON으로 확인합니다. + 업데이트된 링크의 `2027:2` 아래 화면은 **업데이트된 화면 찾기**에서 이름으로 검색한 뒤 해당 행의 **PNG + JSON**을 누릅니다. 검색 결과는 최대 100개이며 프레임·섹션·컴포넌트와 상위 레이어 경로를 표시합니다. 5. Device Hub에서 앱 화면을 PNG로 캡처한 뒤 **앱 화면 대조**에서 해당 노드와 캡처를 선택합니다. **차이 이미지 만들기**를 누르면 빨간색으로 다른 픽셀과 비율을 보여 주며, 결과 PNG를 내려받을 수 있습니다. 화면만 잘라낸 같은 크기의 캡처를 사용해야 정확합니다. 내보낸 JSON에는 각 하위 노드의 이름·종류·좌표·크기·채우기·텍스트/글꼴 등 읽을 수 있는 속성이 포함됩니다. 노드가 현재 파일에 없으면 해당 ID의 오류가 표시됩니다. 비교는 기준 PNG 크기에 앱 캡처를 맞추고 RGB 채널 최대 차이 30을 넘는 픽셀을 표시합니다. 캡처 크기와 화면 상태가 다르면 수치가 높아질 수 있으므로 차이 이미지를 함께 확인하세요. 이미지와 JSON은 로컬 다운로드만 하며, 네트워크 요청이나 문서 변경은 하지 않습니다. 개발 플러그인으로 사용하므로 게시용 플러그인 ID와 빌드 도구가 필요하지 않습니다. 2026-10-27에 편집 가능한 개인 사본에서 5개 노드를 내보냈고, `picke-class-figma-audit-final.zip`의 PNG 5개·JSON 5개를 `unzip -t`로 확인했습니다. + +2026-11-01에 Pické 디자인 원본에서 개발 플러그인을 실행해 클래스·홈 6개 프레임의 PNG·JSON 12개를 로컬 `~/Downloads/picke-class-figma-audit.zip`에 저장했습니다. `2027:2`의 `클래스 참여 · iPhone 18` 프레임도 로컬 `~/Downloads/picke-class-10052-11290.zip`으로 추출했습니다. 전체 레이어 JSON은 공개 GitHub 저장소에 올리지 않습니다. diff --git a/Tools/FigmaClassAudit/code.js b/Tools/FigmaClassAudit/code.js index 4daaf978..d88dd103 100644 --- a/Tools/FigmaClassAudit/code.js +++ b/Tools/FigmaClassAudit/code.js @@ -5,7 +5,9 @@ const targets = [ { id: "13337:57185", label: "최종 투표" }, { id: "13384:57726", label: "의견" }, { id: "13757:17445", label: "결과" }, + { id: "13956:18883", label: "홈" }, ]; +const sourceNodeId = "2027:2"; figma.showUI(__html__, { width: 500, height: 760, themeColors: true }); @@ -92,13 +94,44 @@ async function exportTarget(target) { figma.ui.onmessage = async (message) => { if (message && message.type === "init") { - figma.ui.postMessage({ type: "ready", fileName: figma.root.name, targets }); + figma.ui.postMessage({ type: "ready", fileName: figma.root.name, targets, sourceNodeId }); + return; + } + if (message && message.type === "search") { + try { + const query = String(message.query || "").trim().toLocaleLowerCase(); + if (!query) throw new Error("검색어를 입력하세요."); + const source = await figma.getNodeByIdAsync(sourceNodeId); + if (!source || typeof source.findAll !== "function") { + throw new Error(`${sourceNodeId} 노드에서 하위 화면을 찾을 수 없습니다.`); + } + if (source.type === "PAGE") await source.loadAsync(); + const matches = source.findAll(node => + (node.type === "FRAME" || node.type === "SECTION" || node.type === "COMPONENT") && + node.name.toLocaleLowerCase().includes(query) + ).slice(0, 100).map(node => { + const ancestors = []; + let parent = node.parent; + while (parent && parent !== source) { + ancestors.unshift(parent.name); + parent = parent.parent; + } + return { + id: node.id, name: node.name, type: node.type, ancestors, + width: property(node, "width"), height: property(node, "height"), + }; + }); + figma.ui.postMessage({ type: "matches", query, matches }); + } catch (error) { + figma.ui.postMessage({ type: "error", message: String(error && error.message ? error.message : error) }); + } return; } if (!message || (message.type !== "export-one" && message.type !== "export-all")) return; const selected = message.type === "export-all" ? targets - : targets.filter((target) => target.id === message.id); + : [{ id: String(message.id || ""), label: String(message.label || message.id || "") }] + .filter((target) => /^\d+:\d+$/.test(target.id)); if (selected.length === 0) { figma.ui.postMessage({ type: "error", message: "지정되지 않은 노드입니다." }); return; diff --git a/Tools/FigmaClassAudit/ui.html b/Tools/FigmaClassAudit/ui.html index 00c8aa3a..ce5e4614 100644 --- a/Tools/FigmaClassAudit/ui.html +++ b/Tools/FigmaClassAudit/ui.html @@ -20,6 +20,8 @@ #status { min-height: 44px; margin-top: 14px; font-size: 12px; white-space: pre-wrap; } .error { color: #c33; } .compare { margin-top: 20px; padding: 12px; border: 1px solid var(--figma-color-border, #ddd); border-radius: 6px; } + .search { margin-top: 20px; padding: 12px; border: 1px solid var(--figma-color-border, #ddd); border-radius: 6px; } + .search input { box-sizing: border-box; width: 100%; margin: 8px 0; padding: 8px; } .compare label { display: block; margin: 8px 0; font-size: 12px; } .compare select, .compare input { max-width: 100%; margin-top: 5px; } #diff { display: none; width: 100%; margin-top: 10px; image-rendering: auto; } @@ -27,9 +29,16 @@

클래스 확정 시안 내보내기

-

열린 Figma 파일: 확인 중…
지정한 5개 노드만 읽습니다. 파일을 수정하거나 외부로 전송하지 않습니다.

+

열린 Figma 파일: 확인 중…
지정된 6개 노드와 검색한 화면을 읽습니다. 파일을 수정하거나 외부로 전송하지 않습니다.

+
앱 화면 대조

Device Hub에서 화면 부분만 잘라낸 PNG를 선택하세요. 상태 표시줄과 기기 테두리는 제외해야 합니다. 픽셀 차이는 화면 상태와 글꼴 렌더링의 영향도 받습니다.

@@ -47,6 +56,9 @@

클래스 확정 시안 내보내기

const status = document.getElementById('status'); const encoder = new TextEncoder(); const compareNode = document.getElementById('compare-node'); + const searchButton = document.getElementById('search-button'); + const query = document.getElementById('query'); + const matches = document.getElementById('matches'); const compareFile = document.getElementById('compare-file'); const compareButton = document.getElementById('compare-button'); const compareResult = document.getElementById('compare-result'); @@ -208,7 +220,33 @@

클래스 확정 시안 내보내기

compareNode.appendChild(option); } allButton.disabled = false; + searchButton.disabled = false; compareButton.disabled = false; + document.getElementById('source').textContent = '링크의 ' + message.sourceNodeId + ' 노드에서 이름을 검색합니다.'; + } else if (message.type === 'matches') { + setBusy(false); + matches.replaceChildren(); + if (!message.matches.length) matches.textContent = '일치하는 프레임이 없습니다.'; + for (const match of message.matches) { + const row = document.createElement('div'); + row.className = 'row'; + const description = document.createElement('div'); + description.className = 'node'; + const title = document.createElement('strong'); + title.textContent = match.name; + const detail = document.createElement('small'); + detail.textContent = `${match.id} · ${match.type} · ${match.width}×${match.height}`; + if (match.ancestors && match.ancestors.length) { + detail.textContent += ` · ${match.ancestors.join(' / ')}`; + } + description.append(title, detail); + const button = document.createElement('button'); + button.textContent = 'PNG + JSON'; + button.onclick = () => parent.postMessage({ pluginMessage: { type: 'export-one', id: match.id, label: match.name } }, '*'); + row.append(description, button); + matches.appendChild(row); + } + status.textContent = message.matches.length + '개 프레임을 찾았습니다.'; } else if (message.type === 'busy') { setBusy(true); status.className = ''; @@ -228,12 +266,11 @@

클래스 확정 시안 내보내기

return; } const entries = asEntries(message.results); - if (message.bundle && entries.length) { - download(zip(entries), 'picke-class-figma-audit.zip', 'application/zip'); - } else { - for (const entry of entries) { - download(entry.data, entry.name, entry.name.endsWith('.png') ? 'image/png' : 'application/json'); - } + if (entries.length) { + const name = message.bundle + ? 'picke-class-figma-audit.zip' + : fileStem(message.results[0]) + '.zip'; + download(zip(entries), name, 'application/zip'); } status.className = message.errors.length ? 'error' : ''; status.textContent = message.results.length + '개 내보냄' + (message.errors.length @@ -249,6 +286,12 @@

클래스 확정 시안 내보내기

}; allButton.onclick = () => parent.postMessage({ pluginMessage: { type: 'export-all' } }, '*'); + searchButton.onclick = () => { + setBusy(true); + status.textContent = '프레임을 찾고 있습니다…'; + parent.postMessage({ pluginMessage: { type: 'search', query: query.value } }, '*'); + }; + query.onkeydown = event => { if (event.key === 'Enter') searchButton.click(); }; compareButton.onclick = () => { if (!compareFile.files.length) { compareResult.textContent = '앱 캡처 PNG를 먼저 선택하세요.'; diff --git a/docs/class-android-ios-parity-20261029.md b/docs/class-android-ios-parity-20261029.md new file mode 100644 index 00000000..b092efa4 --- /dev/null +++ b/docs/class-android-ios-parity-20261029.md @@ -0,0 +1,64 @@ +# Class Android/iOS API 대조 (2026-10-29) + +기준: [Android `develop` `05c14af`](https://github.com/SWYP-Find/Picke-android/tree/05c14af23f0246e0e0f6d49e835dfb5151828696), [서버 클래스 API 명세 `1dc6db2`](https://github.com/SWYP-Find/Server/blob/1dc6db2c5de786bd7ba51ec3a052724102010c27/docs/api-specs/class-api.md), 이 저장소의 현재 작업 브랜치. Android의 열린 PR은 없으며 [#135 AI 토론](https://github.com/SWYP-Find/Picke-android/pull/135), [#137 결과](https://github.com/SWYP-Find/Picke-android/pull/137), [#139 후속 수정](https://github.com/SWYP-Find/Picke-android/pull/139)이 머지된 상태다. 코드와 명세의 정적 대조이며 인증된 실서버 호출이나 화면 일치의 증거는 아니다. + +## Endpoint + +Android `ClassroomApi` 30개, `VoteApi`의 클래스 전용 2개, `PerspectiveApi`의 클래스 전용 3개에 있는 클래스 관련 endpoint 35개 모두 iOS에 대응 구현이 있다. iOS 경로는 `Projects/Service/APIEndpoint/Sources/Class/`의 Ticket, Room, Member, Battle, TopicDraft, Recommendation 서비스에 있다. 공통 요청의 경로·메서드·주요 body/query 필드는 일치한다. + +| 영역 | Android | iOS와 서버 명세 대조 | +| --- | --- | --- | +| 이용권·클래스·가입·멤버 | `ClassroomApi.kt` | 19개 endpoint 대응. 클래스 생성의 선택적 `ownerDisplayName`·`requestKey`, 초안 제안의 선택적 `filter`는 iOS가 추가 지원한다. | +| 투표·의견 | `ClassroomApi.kt`, `VoteApi.kt`, `PerspectiveApi.kt` | 8개 endpoint 대응. iOS는 중간 투표와 투표 이력의 `midVotes`를 추가 지원한다. | +| Android에 아직 없는 서버 기능 | 해당 API 없음 | iOS는 초안 조회·직접 수정 검증과 중간 투표를 추가 구현했다. Android에도 AI 토론·답글·재시도, 결과·내 리포트·인사이트·재시도 API와 ViewModel 연결이 머지됐다. | + +## 계약 차이와 처리 + +| 항목 | 확인 결과 | +| --- | --- | +| 클래스 의견 작성자 `userTag` | 서버 응답은 `null`일 수 있고 Android DTO도 nullable이다. iOS DTO를 optional로 수정하고 도메인 매핑에서 빈 문자열로 처리했다. | +| 내 의견 조회 | Android `PerspectiveApi.kt`는 단일 DTO를 반환하도록 선언했지만 서버 명세는 배열이다. iOS 배열 파싱을 유지한다. | +| 배틀 추천 선택지 | Android DTO의 `label/rationale/philosopher*`는 구형 형태다. 서버와 iOS는 `optionId/position/title/stance/representative/imageUrl`을 사용한다. | +| TTS 완료 호출 | Android는 최종 투표 제출 직전에 호출한다. iOS는 재생 완료 단계에서 호출하며, 서버의 재생 완료 시 호출 계약에 부합한다. | +| 필수 의견 삭제 | 서버는 클래스 설정에 따라 삭제를 거부할 수 있다. iOS는 실패 후 화면을 닫던 동작을 수정해 오류 알림을 표시한다. | +| 초대코드 실패 표시 | Android는 미분류 요청 실패를 일반 토스트로 표시한다. iOS 실기동에서는 인증 재시도 오류의 내부 타입 문자열이 코드 입력 화면에 그대로 노출됐다. `ClassError.unknown` 표시 문구를 일반 요청 오류로 바꿨다. | +| 401 재시도 | Android `TokenAuthenticator`는 재발급 뒤에도 401이면 두 번째 응답에서 재시도를 멈춘다. iOS는 실제 초대코드 조회에서 Alamofire `excessiveRefresh`가 발생했다. 서버가 새 토큰을 거부하는지, 토큰 저장·갱신 경로가 어긋나는지는 추가 인증 증거가 필요하다. | + +최신 Android AI 토론·결과 ViewModel은 실제 Repository를 호출한다. Android 결과 카드의 0명 상태는 `아직 참여 없음`과 중립색 도넛을 사용하고, 비율은 응답의 인원수로 계산한다. iOS 결과 화면도 인원수 기반 비율, 0명 문구·중립색 도넛, 참여 설명 문구와 6pt 막대로 맞췄다. 별도 Chrome 탭의 최신 Figma 파일에서 결과 프레임 `13757:17445`를 2026-10-29에 다시 내보내 [`docs/evidence/class-result-figma-20261029.png`](evidence/class-result-figma-20261029.png)에 보관했다. PNG SHA-256 `2720fafbbfb6b68f5d22eaae0cc57851c07e9a636007064eacc7e77954cf8d15`는 2026-10-27 플러그인 추출본과 완전히 같았다. 따라서 이 결과 프레임 자체의 시안은 바뀌지 않았다. 사용자가 준 `2027:2`는 이 파일의 와이어프레임/Hifi 페이지를 열었으며, 그 아래 다른 변경 화면의 전체 추출·대조는 남아 있다. + +## 이번 실행 검증 + +- iPhone 18 Pro (`68DAE7F3-C265-46F4-9FEE-71140108E210`) 대상 `tuist test ClassDomain` 성공. Foundation Models 기기 전용 테스트 4개는 시뮬레이터에서 건너뛰었다. +- 같은 대상 `tuist test Chat`, `tuist test PickeNetwork`, 최종 `tuist build Picke-Stage` 성공. 빌드된 Stage 앱을 시뮬레이터에 설치하고 실행해 클래스 진입 화면까지 확인했다. +- Device Hub에서 앱 홈과 클래스 진입 화면은 표시됐다. `내 클래스` 목록은 재시도 후에도 오류 화면이었고, 앱 로그에서 `GET /api/v1/classes`의 서버 `AUTH_401`을 확인했다. 따라서 제공된 테스트 클래스·초대코드·이용권 코드의 인증된 실제 응답은 확인하지 못했다. +- 최종 설치 앱을 iPhone 18 Pro Device Hub에서 다시 조작했다. `내 클래스`는 오류 화면이었고, `클래스 참여`에서 테스트 초대코드 여섯 글자 입력 및 활성화된 버튼까지 시각 확인했다. 버튼을 누른 실제 `GET /api/v1/classes/join/preview?inviteCode=`는 서버 `AUTH_401`로 끝났으며 화면에는 `AuthenticationError.excessiveRefresh`가 포함된 내부 오류가 표시됐다. +- 오류 문구 수정 후 `tuist test ClassDomain`과 `tuist build Picke-Stage`가 성공했다. 새 Stage 앱을 같은 iPhone 18 Pro에 설치·실행해 동일한 초대코드를 다시 요청했다. 응답 실패는 계속됐지만, 화면에는 `클래스 요청을 처리하지 못했습니다. 다시 시도해 주세요.`만 표시되고 내부 오류 문자열은 사라진 것을 Device Hub 접근성 트리와 스크린샷으로 확인했다. +- Android `ClassJoinCodeStep.kt`와 iOS `ClassJoinView.swift`의 코드 입력 UI는 모두 6칸, 대문자 ASCII 입력, 코드 검증 후 이름 입력의 순서다. 실제 Android 실행 화면이나 최신 Figma 캡처가 없어 색·간격·폰트의 일치 여부는 판정하지 않았다. +- Figma 개발 플러그인에는 `2027:2` 하위 화면 검색과 PNG/JSON 내보내기를 추가했고 JavaScript 문법 검사를 통과했다. 별도 Chrome 탭에서 Figma 로그인을 완료하고 결과 프레임 PNG를 다시 내보내 기존 플러그인 PNG와 SHA-256 일치를 확인했다. 새 디자인 전체에 대한 플러그인 실행과 앱 화면의 픽셀 대조는 아직 실행하지 못했다. 사용 중인 원래 탭은 건드리지 않았다. +- API 로그 진단 중 인증 헤더가 그대로 기록되는 문제를 발견해 향후 로그에서 인증 헤더와 인증 API 본문을 가리도록 수정했다. `PickeNetwork` 테스트가 통과했다. +- 2026-10-30에 전체 워크스페이스를 재생성한 뒤 단일 iPhone 18 Pro 목적지로 `xcodebuild -workspace Picke.xcworkspace -scheme Picke-Stage ... build`를 실행해 종료 코드 0을 확인했다. `Class`와 `Picke-Stage`의 `build-for-testing`도 종료 코드 0으로 성공했다. +- 새 Stage 앱을 iPhone 18 Pro에 설치·실행해 다시 확인했다. `GET /api/v1/classes`와 `GET /api/v1/classes/join/preview?inviteCode=`가 모두 `dev.picke.store`에서 `401 / AUTH_401`을 반환했다. 초대코드 입력 화면에는 일반 오류 문구만 표시되고 인증 헤더는 로그에서 ``로 가려졌다. 이 계정의 클래스 상세·가입·이용권 등록은 진행할 수 없었다. +- `ClassReportTests` 실행은 5분 이상 결과가 없어 중단했다. 한 테스트만 지정한 실행은 종료 코드 0이었으나 결과 번들의 `totalTestCount=0`이라 통과로 간주하지 않는다. 결과 화면 캡처 테스트는 한 번 건너뛰기(`PICKE_CLASS_VISUAL_AUDIT`가 테스트 호스트에 미전달)를 확인했고, 환경변수를 넣은 재실행은 4분 넘게 완료되지 않아 중단했다. 테스트 타깃 컴파일은 성공했지만 실제 캡처와 Figma 픽셀 비교는 미완료다. +- 2026-10-30에 추가 전달된 Figma 노드 `13956:18883`을 감사 플러그인의 기본 내보내기 대상에 추가했다. 별도 Chrome 창의 Figma 내보내기로 `홈` 프레임(375×2010)의 2배율 PNG [`docs/evidence/home-figma-13956-18883-20261030.png`](evidence/home-figma-13956-18883-20261030.png)를 확보했다(750×4020, SHA-256 `6d6741a75f6538fd5d79fef7b193ad89686ce25ffc97702d93d6db0a3f82e99e`). 개발 플러그인은 등록됐지만 Figma Desktop 공유 창이 가로막아 실행·JSON 추출은 확인하지 못했다. 사용 중인 원래 탭과 FESTICORE 창은 변경하지 않았다. +- 동일한 iPhone 18 Pro 설치 앱 로그에서 클래스 API뿐 아니라 출석 체크·주간 출석·읽지 않은 알림도 `AUTH_401`로 끝났다. 따라서 클래스 전용 endpoint 문제보다 현재 저장된 로그인 세션 또는 공통 인증 갱신의 문제일 가능성이 크다. 갱신 요청의 최종 HTTP 응답은 확인되지 않았으므로 원인은 확정하지 않았다. +- iOS 인증 코드와 Android `TokenAuthenticator`·`AuthInterceptor`를 다시 정적 대조했다. Bearer 주입, `POST /api/v1/auth/refresh`와 `X-Refresh-Token`, 새 토큰 저장·공유 인터셉터 갱신 경로에 규약 불일치는 발견하지 못했다. 새 CoreSimulator 로그 조회는 샌드박스 접근 오류로 실패했다. 기존 요청 401 → 갱신 HTTP 상태 → 갱신 뒤 재요청 상태를 비밀값 없이 확보하기 전까지 특정 코드·서버·토큰을 원인으로 단정하지 않는다. +- Device Hub의 iPhone 18 Pro에서 이전에 실제 홈 화면의 헤더·대표 배틀·Best 배틀·하단 탭을 확인했다. 이후 같은 시뮬레이터의 전면 앱이 FESTICORE Design Kit으로 바뀌어 현재 Pické 화면 캡처를 얻지 못했다. 다른 작업을 방해하지 않기 위해 앱 전환·재설치는 하지 않았다. 따라서 새 Figma PNG와 iOS의 픽셀 비교·시각 일치 판정은 미완료다. + +## Figma 홈 `13956:18883` 정적 대조 (2026-10-30) + +Figma PNG에는 대표 배틀, `지금 뜨는 배틀`, `Best 배틀`, `Best 댓글`, `새로운 배틀`이 순서대로 있다. 현재 iOS `HomeView`는 대표 배틀·뜨는 배틀·Best 배틀·조건부 `오늘의 Pické`·새로운 배틀을 표시하고, 광고를 삽입한다. Android `05c14af`의 `HomeScreen`은 뜨는 배틀이 0건이어도 제목과 빈 카드를 표시하고, 광고를 삽입하며, `오늘의 Pické`는 홈에서 표시하지 않도록 주석 처리했다. Android에도 `Best 댓글` 홈 섹션은 없다. iOS `GET /api/v1/home` 응답 모델과 Android 홈 화면 모델에는 Best 댓글 목록 필드가 없어 실제 데이터 기반으로 시안의 이 섹션을 그릴 수 없다. 화면 데이터가 다른 상태이므로 카드 글자·사진의 픽셀 차이는 UI 구현 결함으로 판정하지 않았다. + +## 전달 상태 + +2026-10-30 GitHub에서 [PR #78](https://github.com/SWYP-Find/Picke-iOS/pull/78)의 `develop` 머지(`1b9ed1d`)와 [PR #79](https://github.com/SWYP-Find/Picke-iOS/pull/79)의 열린 Draft 상태를 다시 확인했다. 현재 작업 트리의 추가 수정은 #79에 반영되지 않았다. 인증 API 성공, Figma 화면 실측 비교, 전체 테스트 실행 및 TestFlight 업로드가 끝나지 않아 이번 추가 작업을 배포 완료로 표시하지 않는다. Claude CLI 계획 검토와 구현 후 점검은 각각 시간 초과로 종료돼 교차 검토 승인 증거도 없다. + +## 2026-11-01 재검증과 최신 디자인 추출 + +- Figma Desktop에서 이 저장소의 `Picke Class Design Audit` 개발 플러그인을 실제 실행했다. 지정된 클래스 5개와 홈 1개의 PNG 및 레이어 JSON 12개 파일을 로컬 `~/Downloads/picke-class-figma-audit.zip`으로 내보냈다. `2027:2` 아래에서 `클래스 참여 · iPhone 18` 프레임 `10052:11290`(402×874)을 검색해 PNG와 JSON을 로컬 `~/Downloads/picke-class-10052-11290.zip`에 저장했다. 전체 레이어 JSON은 공개 GitHub 저장소의 PR에 포함하지 않는다. 단일 화면도 한 ZIP으로 내려받도록 플러그인을 수정해 Figma Desktop의 연속 다운로드 차단을 피했다. +- 추출한 iPhone 18 참여 프레임은 코드 입력을 한 개의 테두리 상자로 그리고 코드 찾기 안내 카드를 둔다. 최신 Android `ClassJoinCodeStep`과 iOS `ClassJoinView`는 6칸 코드 입력을 사용한다. 이 프레임에는 두 개의 하단 CTA가 겹쳐 보여 확정 화면인지 판단할 추가 맥락이 필요하다. 따라서 이 프레임 하나만으로 기존 양 플랫폼의 입력 흐름을 변경하지 않았다. +- 최신 Android `develop` `34448b4`는 홈의 `Best 댓글` 섹션과 nullable `bestComments` 응답 필드를 추가했다. 따라서 위 2026-10-30 대조의 “Android에도 Best 댓글 홈 섹션은 없다”는 문장은 현재 기준으로 유효하지 않다. 서버 `a08c30f`의 `HomeResponse`와 홈 API 명세에는 해당 필드가 없으므로 실데이터 표시 여부는 API 계약 업데이트가 필요하다. 최신 Android 홈에는 `오늘의 Pické` 섹션이 없고, 뜨는 배틀이 0건이어도 빈 상태 카드가 표시된다. +- iPhone 18 Pro 시뮬레이터의 기존 `io.Picke.co` 앱을 실행해 실제 로그인된 홈과 광고 팝업을 캡처했다. Device Hub는 `cgWindowNotFound`로 연결되지 않아 팝업을 닫은 화면 및 클래스 화면의 새 픽셀 대조를 아직 완료하지 못했다. 이 캡처는 새 코드가 빌드·설치된 증거가 아니다. +- 코드 리뷰에서 이용권 코드가 GET 쿼리의 전체 URL 로그에 남는 경로를 확인했다. 네트워크 로그는 URL 경로만 기록하고 인증·클래스·이용권 API의 요청/응답 본문을 가리도록 수정했다. 미분류 내부 오류는 일반 문구로 표시하고, 앱이 명시적으로 만든 사용자 안내는 별도 `userMessage` 타입으로 유지한다. +- `xcodebuild -workspace Picke.xcworkspace -scheme Picke-Stage -configuration Stage -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build -quiet`가 종료 코드 0으로 성공했다. 새 `io.Picke.co` 앱 번들을 iPhone 18 Pro에 설치·실행했고 스플래시 뒤 SNS 로그인 화면으로 이동하는 것을 `simctl` 스크린샷으로 확인했다. 이전 로그인 세션이 유지되지 않아 이 실행에서는 홈·클래스·이용권의 인증된 API와 새 UI를 검증하지 못했다. Device Hub 재연결도 화면 캡처 오류 `-10005`로 실패했다. +- Home Stage 빌드가 성공했고 HomeDomain `HomeRepositoryTests` 4/4, Home 테스트 5/5가 실제 xcresult에서 통과했다. ClassDomain의 `ClassBattleAPIContractTests`와 `ClassErrorMappingTests` 9/9, `ClassMemberAPITests` 3/3, Class `ClassReportTests` 12/12도 통과했다. 첫 ClassReport 실행에서 비율 기대값이 잘못된 테스트 1건을 수정한 뒤 재실행해 12/12를 확인했다. +- Claude CLI 구현 후 교차 점검은 샌드박스 DNS 실패 후 네트워크 제한 밖에서 재시도했으나 180초 시간 초과로 종료됐다. 승인 또는 코드 검토 완료로 간주하지 않는다. PR #79의 새 변경분은 아직 원격 CI를 통과한 상태가 아니며, 인증된 실화면·API 확인과 Figma 픽셀 비교도 남아 있다. diff --git a/docs/evidence/class-result-figma-20261029.png b/docs/evidence/class-result-figma-20261029.png new file mode 100644 index 0000000000000000000000000000000000000000..35b8d542ed03e3f72e2f9a7a486a39daf7724cf5 GIT binary patch literal 58349 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� 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

4SKbyp&D1`Ffi;ntJt{8)`T_7(c@I=Kj1{zA< z9(Ot!T| zI$%pyQC%&F7T&?N3W1^!pidvb-#TrC)^x^lMVjIEIvl-v`)G)t$dTl0l6C(Dav2F^K3eQ>1*s1?gmJN* z)x~zbbYY-k&&=X#1KWv6j2c-eXKkY(vsdC}TiL4h%|*jb({jbQ1yul&hvhsH8~C{) z;$-FR`RI-~GDp?Wz;6#85-9Z+CpM)It&pLGJX$@%)HW;eY)fwViM&+OlGH>z3u2p~ z=9yPB&bYViiXFk-6dh4e*)%k@vb7HPxEtRDd?M#}{p;YHYlspnTXEZ^n{`&83Sx%+ zdBH-yb4E1zVGiPNksKoeqgKB6@kI+CiD_8odf4qKH5TT;lcd-K)iLzuxHp>qX_s1# z^OdTI%8X!O!V$;%wFZ2YbN^NOMxm1zGUVYNM}uvM~VtvRk*ggzK-s ztXvC0si+RsX^+K{^eg9-jc}c{n`;f^aWb&~BPomT?Mnjo=EdU6+GQ24}9UU5^=P+k0ChBiL*dQ}E>6~|?y zF*o+f1{m#B%~cs!SRx_(XLG@(AwIU5!-IdX>ntQ6|b&q!lj#+ha<@%LVP zSoIiJ@tjuOA}nw_RBsyKwg|48Ni*eSCjNZw-xWd1%+_0SQJCWIjObafVr0J>|ax0{(8rTu@Q!w z;z7@K6L@!Z`QvMwc(>uxr)DaQ=c=(>IUgQotAp(4`r2g~lSrl1<`c^3vRtE(g_8dQ zknzKzKN^j66$QP~*;S8`>xE0g33L<02E^1BJbYizttL99V&+H%>}Y?JdL#U^s@epW z{Cl3fP-F-Lb~3C=zP3mc$qSeXtN84dRHs0}8c2-kt*&Hnlaxqn3cJ7dH>>)6ttsgi z2szN@ObWn)X?~1K8!`rg2bKWUiz+D*mcsu302zx`iRyjr5mYrd609+MT&4|(I!gf$ zzkafn5fc-a^oTw!D;$$AV}*d}Z>Pmy>Xq+4=m7{zMRhByqNRC_YRpgJY|r;A0Bbd( zsVp~0ff|9Bm1IeJEyhYkVF zTSainQ4UV*+n{aGQ_TO^J`Gm?y(H;H{l75e{!as`5po0di>`@D&M>J+87Kr-&&X)m z`Zja2XyzUhcXSOX1xhBZ`ZGPhtoqunw==8eRtyghr=)Y3A`ND54{87WD_k1QHyFz5 z>Ue$y*sKM2+?rlRP#Ou}Ja4r8SswEmcV}Gte3w&$pMQ48|2ZcIhql#)*FrjEDkx+$ zlh?NC*jxLHnl_M$)JbJg)d313yVj%sQ6V8|xdWP;SU$v4XY+bjQE*#s7d+T4>l+(a z0LRF}LK#LxaaoyCJdLMkw`335c&6|)ne9ubBF-6#&=!WF8(j8uPC>!feeZsr9z|t# zNcGbc3Rv~O)fKi@NDXAVD#h(7R!ofp6`-u|Y?8&2iWg<5>wJ3sDP{{;{1UI*hO@=a zYn=aMc9oYq#s!iKpINR2=1<{5U)}EK#Xrdod^|)X`KqP<^cocKw9CXEu(b$b5_UJK%zFkpLq29(M{Rd_YO{c-KY)@4wk_H_DxiB8~Hvx zJ==;S^QGDBuk1*WqVBBUIgO-G5k7KK|ras zT5UEWP?{O|f!iuFW&}gm#VAz)97D^VPV0_t71=uMExv*hLK*R4y=|cx>aM+>wZM-Z z@YV9?J4BM?ZEfOWy{%)SRRL5eI+lZV;c=5`oPxe9h^b+gK+udcadVUzNH2msyIzcZ z6lSj-K@2t?h3?W9a>Goam}$~%jXzq}7FuLuNm$s5LG~8kN+L%Xm>9W+^Bn?pVF8$~ zXu^c%I=*)ookwm38#rpYDIZy+$Z%?h06=S5(9egN8@H{Q277NNmtlmF3->^{hQ(uS z1DB*Ea~y5;RvKxR2M`q%vkwm14L#=#`-B^_c#HtYWCJ;u2Ncne0Ptbb0=Z%?=hTKP z()!h6oWUnzOEgLWn?nO<|9&R;J*4<@iCK=jm`Mh%u#KifSk_#;ASe@IjFFseB!kjzm~xmBm^cT+RL6qx+5Se4 zIcGvNywrGh<;MV;>>2Ai0Co^Mf-t3Ly>KTS!gHTc0|m#{Y+?4MJfH?8{}8F<@E(~O z1^~Y))6oC%zEWJ;bgDwx1ueIuGey5}=Fa>MH+0@$?vq(hW*)l9*RwE}GSO&GV`dejWYFY$W z5WEUD9@(twJAJLNossF0Of`a6-b7C)m8fR~fWu<)1kuccPpH`jQ{HFD$p&31ckgbt z*ze;!J+bhmN`T~- zWH=4iRH|~?L+1fRG^ztav>>EYr_4Zn3RIZu-Kx2KLk%sFEGU|i{tnT@VVENKe10k^ z`R0Ky0l&%nawUda3D5nU&rGCZDM04#^Cj6CK#`8(n?3&{4%8Y`?U}+1i?KD_KQK4r zeHa}q3YkQ_51vv_gZ-7dUzu$s9fCIQ)`Bw)> zAZKRhvX9!H8#U=Xp}1iBcb?Y@y^O@C{ya4~F_ACew2<$2v6p-CAr#n!cpl7^6)8nB1x@F@4J8w@)3A)}d##*Nru?F9 z=Fn=o{|Ug*HiYO%X6(PS2(Jp+T)g!EOXGh5eK@@0_Zm*z%?nn$ocBUs#)V{jx?5Z= z$@>KqD^|F@p9{KiEjrKFzEhpUtvmn#<+Fz$0=fpo_4+jo^QQhezO=xfOrBWmfBhSb zjzfn93F(e3EWQkL$>rekF*DG4-<;fsvpTHl(DH&VtIlDT&BMcO`X_Uk+2ejj|93^% zSFbo31hj*fQ1JazQLpieto{U<+8O#-A zrD3D8(!2Qmt^W1?>RC7tUCb&LD6{w?Z+ofSqkB5@gYa~Ga#ZB)5|UU z{O;n&3YnUJ9{vSA41QlJ3#edn#H84QXvY=?8xos^3~#NCdx#y7&PHuRFV`&@oG@Cw z)vw5EXpvZfzm(fIDq(GS!fCK0CH$EdXb7d#uBiq?mxR+XX%jBmIIbPH;tjd0|UjCvev6@8&lGCv<&i*mTd9l+WqA zXtN*;r=^9SkCc+A1`a95;qVv`&XBIYiRWIX-%C~yt7NpV(u($&i92ISX|z$?2|0c7 zPbq`)41Cq=#Hgu^K3U9hd(U@A0xnDWk$7MipYS*0g+zm~3K+7T+$T9y9upZ@S)hoJ z`zoD;WXE_y;Amj1B6=N)i}X4YA)-aNHF`kX{P=U?p=|NlA-z5R-C397Qyg*k|hVkccd6ilPdm-v_ z6G`_Z+4lm=TYh@BdJaWkmpHhR$q_{&&uF92fNgu4#S{xxfq! z6_i;y-{>m`80v~$EilB4#R}-#>ubBfTtu=@Z@KN(>DG2S-&lNgaiqWx-%VCqI58^H zIiN#egzwH$2A*h<9%m;h#;VuaPC-E3u*qy=jsL2fl)%?yGIGEM!I%f8pl8_M2Bvw; zkpQA~NC0v2e>l+pn`E|(wFu)}`=5%Xw%ezkCz5S2M1zS^`vvE_W=#!^eEegbl6NAs zrG=7=i0xGG+!n3jEnJun)Huu&7YyBCVmgN!8McNAB5+B>mdvPnjbJDlvP!|m}xa2H~`?=Lj?bzvd%if=0hn= z4nK$WNB?w%k8AZr5vT_UIxQ^j_fBa6*srKKjS91lRd3o)e$>%}3yxL%2lzB%3*K$Q z9=q9A|J(^50=(xt*H6aY7b?VHp0-a5=yn*lU2dV%APYr*rNe-;O4TE&3$hfs&3toNIr+K6ASFFv1G z_6wEY65px&RGs+%KUwb^yq0eK!e^Ry4sb*Tp|n8zZ{rOK;gIb@!E>Ld^)ur3=P*K? z@Z{^rT9>OivYS|&o>@24?z%G{101yV%jY!` zGDF{-ey?E1f$$RidC?Hc*~GY0NHUn!uIHQQ%qzeW_i&%OpSMvGQ*_ZTkAH}nvn4yk z#3jv^pzR#9Hz&tKn$bd?qTZ18Vjt7fKq51fKE7^0@8NWs<(J(9sLKjI?k^ZWePW3; zBv{3bdwjV5kFjfo!Z5C&&{43ARkfM})Y7756y_@LTWghFeZRE4sqo{a>``$B2xm7tE?DuI5Vn# zFt4XnO;)}vA{W6-TSyp7U)U$aqVxzqig zJ^W$%T0+_YC-E;*9E_k>v(7=Xloy^39aj>Dv6(d$dj1l|&N82F z&PhvQ9IT)y;EFT$8SpyS`KbMA0)6|Qwlc+_qQ=mY`!2OLosnt6# z-WbGANymKhIZ0Z|67VJzN`82n5`b$JAq()x)2zM^Oo#&eh={Y6!cqz;Ax1WPaD#jL zsak6;AFw$pI8J^>6bpOM?^T-mj65r|n&jR;sS;P^HTl)DTFbh~F4WwydJ*O-RwFoR zixC<8SViE$=CY^T;n9=2&l^}#q}KM|RW$w^c;jyFeWa&5Rhi-7?C>=pzX>T~MD4px zlZGoJG@)7Jas7_jGMNL$wimZ>a_#NG{2bEs8~ri4;00VkZszwwMeF+ujTa4I`q1c+ zKRcKG3VFfj8lKyl@+D58Z{8kxHvO_#Ex_#Wg`1H?dgLBg(tZ^b_>YcZY3}sw2 zF2C)M>=q?*a$CClOl}lhqaGI>DB704cy0SN+UEupTJh(gzF*mmybv>7){FMSGhs~@ z1eX0qN?08Y3dGJU#4UNaelCue(Q!tYmw zYGOw>#Xl9YX|=hT&x#J=8VS#6ts=A6Zm&{b;k~9MO5MU%ck&QUeafx}I0>rs*!9}W z{>h!~Mo7D4f#3WN7n$6(!QLQxjQ)x7 zk68=XABF366%}LHI63FT>eMQ$4PDd6GoeK2|1?nkWljUGwL{A1nJ>pb#>RgnZR+#? zh)aK~w(856VxZ0h>n=~M)b#fX%D!1?oafq#*q+un%1LC`7iVL!`I$q;{px{zTDV?Z zQ(ynX_A?MLiZFE9JTO|@SGI*DNsHg|Mz;OWKOlQp=saK;iBHNLXV>R@<^A^_({PZ4 z9lj*u%fbkOMk|tC%^vFq`B9YkP#aOyZkPqT4}0nt6-6YHhM3+t6iJx!^2hns1Pn%3;bDDzpqpWO zyY@$PM^kmASsgcWwEDsEYUoc%Sj|(oruLWBQov2~eo$RU4_}j?Hoz`Upj-Lm1>icH z0sILK^V|UR>{0*{7B)!=kV^hQYB=Eb<|BKW{T^8z<^|B?)2)6c1p1$x3fTe9FPpE& z4j0>Rso-bvNN9>+-c6gK`0-bAXYG}98PCzS4(%U*0;C$Ba`7L#h5zNY;$Cd@EnT_{ z3Q7n%#Pr3C21I4{8GzarAlni25chQh8yV53oL&MZa^ber0C=CG`bd!&OP;#>q3piqW|}wbu)JyVe7o9 z`^CvviTYrOFPE&~&j_;T$n$WOTQ%hpfE z${IV{BEGZ)ZY}pO9|n#^?B6f{<+y0;36Gq3DsokjP5_nq&PJ3nMb9AW_FEPqCq{wl zON#6!bZkjmIV6RJdi1PKE_r~L5*nN>Y0eq9lF@W@kZD9AXY3aGe2`>k%}J?f64gag zPG14dTS+S@-$kZ~XZAb7WRjGh9znU9>>Ol;P45i|a0yTTRaIW?=hh{c^g?!hvxd^+ zTe%WG(@zyQhE+LV+KP=+2LJJ4<$i_SBd4y)wnB!0iFW#*T$4@5k*=cANc<__m_3e_ zW?bm8G(`NO0-Ksn1Y{0fT|Gf`MuXwy@wHUc2w666l(pp+QPdUzgl+BbXlT!L;W;r;zrTF zMsIh5ry{o5dCt(>?FWbYkh`AD#B2p<7nKu}t~la& zy>@}-T?iM4$hX2QVI<+NhIc_4cJ|R?ikV$%C_*PMLz9RG?YQ52$wwL2`o&E=5HZCtqdH<68b~weP{;jr9zVA zLu=@`69d5M>e)ivT1E2mY6?7;a*=V5>9C>9f>Y5*$7JBjvm6rm^L=ZNG1dB7B^h1c z7#Z`(sa$Vaie8}D|A>MO+=XJn==%%FFQ(9>6_`0l78hGg360iQ@FMQ5N&jqZVCNk9C|XN;*AxjQ=08G1%qnvN0X)~6m+P>0$7s6PJkORM*B z0U#U4*j(<6I{d2pwDZlh&9AgTIw~v5d2#BK!|Z-XNq6_c;JAXhssEAZ&O?7Z<%l0* zn=n;ozM#CUSwBn@8kUW)!oVbtmrANh6DLGR*zM23^ciTz)&9 zk2K5}4V>1#{#{=n|Ikfzs;|clsBCobYJTWE|%Q&)YhU1Nz*t6$QVf zH=JU3=VcqI9iL6SkFUz~4u0i60~z^BMVALZa+VFy1ok4M=3z9T1>?3?rJthQr9OC) z#X(XiVOz-oT5ZoR-mpF;HT$%LV|NziwK2-yr8%EEhTlkIqa~I!pnV27Ry5MLrPd3a zrt_ro+5_XRE#MY+ozky&Ps|%VwOux7kEjNTGj)Wj#E%L-7Gj0~Sl=QuIcAZOwh37qhD>GLM?Xi5lL1 z!Q=_Z)n#y8kQgyCDbDPhOC^;0zOT-4Q}`)d;R?{V1lsNcN-ifXDqH+&zn6S4-AWvj z*?Y}a$rv^#JuUv0Dup?K=gZe0#RPeyDB(-EsAPyrVeXAeD+yXQ>^j%$D7HMnvQrKP zgV0x(C1oaNNLLlEX=;06-9`j2FZ?}I$G*~a0D45iAZVy5-ZjFodVFzoH$Ga(d~kQN zNWT2?fV*ckV48-iP2+V6q!WP&KBxs}0862!mR807r5^$!2m_`~n>}+1 z+HP+DQo4h(@f^UM_@Z;MpxDg!EC@gdD;N%Zqi_d=ge=7HvgVf$d$JSrTlNBsBbIK* zXs4>8sK9V_2fQGEA7}$rW z)}A}#!_iT0Nmz7bJckem_l@C#n4-kj6+mdj2b>Q}iQN&6!(;b#4$lq|A~LiqY!A=}c$n&V8SgZw>UH=9 z$v$}iF%-hw=&zXRBjhLrnIimijAS(Z#W$dw&t`*XKNW!kvJVZ&EiKmvzR4+v33#hR zPrZ<A> zo&o2+j1Vl9=M24fiHrnBmOJ=lq5?n-h=%HxWG$MMHHvW&L<&9i6Ep|D!uqpG9qAp^ z=)vE+9snso!3Jn!?|Vl$HqLxJuZwbyWIum4;0`+{8uEHBKZbG6wj3=)XN{Ezj2?b& z`ou;f^{9=b-(7WKLNAlK8v(H-in&z?64;*6I0&@86=ic!*c3~HNIqZp#fbkFk&s}D z>VRLC_ux`;IsD?T6nLBj?%cHCh+<5s%<_IhqHQe*mSbtw*wS}TW(0zT6i(Pu>b1*!!!`ZYuae!Ks$D39M*?-&4knf(*n?q2OrT(MteU_0{Py^W_fpg}oMvP2xg8IXa2=Yq-mzB&L zs@{L!+%eaCOph1Z>P&&0=P}C%Vn7#CSGiWaSbnOAQ~bX>TS%+8;45dXw7Ns17E~^9 zcQ6Oc0uyvuN#gT7V9GGgZuo7LC|EC zFaiVacFNQf7kc9UGfkiJcyqqv?o;c2*~0zW4ITkTH`+#*MEl>wM{BVhvxoiQxTxtA zYr;9Uk<^_I+>sSdcJz*nm>w=~6;;)<3BFok8=JD*bJENLXBh=L46-P_TGPhGdfzhGQ8F>Varq>d; zTk)N#tQ;K9XIf>Gc%z)=RJ6;diIQUpbri|f9+gUrW{Jo&Vn|_@-C`dSM&@|UBi=Ma+^7)82fCnqP%sa zLJWEB0PwY(ZuI1v7r)UcxhXnc5U{0>?9P_!*>A1=5L@tZUT#vo+;g)trijL!lWlf5 zT52>z{$+q2wtF?2XR_-Q?U>WM;m4{46jEA1TJxYavEPz-_8{ z(0#!AOjxOM8m5*1^7_}bzVlSUb0{o8-=uAkta8bxo5q4OT2UFIC({{Yc38ax%giBM z|J&tbrADrTsWEpjVO|8nl!74Y>ZTZrXfg1Fdr|25sIaSn>N=~#6xz-6FyR$gFKrZg_+ zT3@0ynKSxH#MK}Bwla8ZrG+_;_*{Y_)utAqDHv%)d9=AnNF)&E#-CRSeTt+nv@}5l zN&EUnK$%83v>RVAiwPwH9Sf!$>^PA#Rv~>Ky4s}KcZglLJu!zs4>te`s}TQp+4ZK+ z;is$Z7ZZZfkPp1bM(1TcYCi`J!5;zV1y=htgXbdNFpetZ#awPQ8A^077JC6cDj~rq zCMn#nHL9Q0OR37vo{(){9$4|aY(p)DQ!?GKxwd~k{n>Aze1k1#CWF3FBE5XU+wwFZ z>WGBXkGVFFE~%)b^orIQHCKJJO|2C``X$ShJlWk6eE9?kJ=Dj*vb6G7vgi^RB!Es4 z$|xzQ{XNr%gTV&;kAe<+H?F|p1AH19j@6faPBP#aCF0`wQ}?f_20@#|-3OoRoRfr2J2|37jwPyrPUR$IojmJ4P6F6|tg?p5bKe6JQYIap!R7`m z#%#zQM}Zykj9w}@<#%95KGFWK59L+4*ZCBDJD+XNmF^B8dXIlM+_8W)6FN*Rc0X9n zM|vX119qf-jNsL6M>$5%MRB<0$_Ke^a7qAk6qvuI6iI4ZM1u^SVhZ|N6`H@}rCkvZ z&weP6KUJIWu)fHwRm`K;GgL+s56!pf1tcdmL>F?`!k=4!^<^rHx}uZSUDcjGAl{5L zF^c4$rIfis+pitDP>;!e@&U)p+Wt1&rW_Z{fv*x{Z-5+ONN$E>?1=X3#vLwFzPBUj8UOj+U}ml zT2wGB%2$qplaX{`m#m2HFHSHGEQGPu=gWdzU+-AXhQCjz78dspf$sdXoKq#wSCTHS zK&Z*4g^xg<38wUbfUz*{IW^n-4KMJs>|vLhYC~*EGoiX#Um{Q7S^{*&w7JHp&|ABo zDhzbpt4F&3DvB~d&s1S&KEXh)O0un^(rsf9$j~_aLPkY>{gkA!W1VAn9k!xS1|b>Z z)EJWiL12|rEn*1%jYjzM!()1!661XCNFB(;+km(iTLxd(NY!|A!R~%10+Yj>4mqj} z+_v8`{?qRwA)}Gi_d}e-!o9qYr_}{LhEFq$OJDrG&MQf`9-!>d_5XS+sLUaDM?4!i zG5?hQ77vLRE!A-r6eC9o!9dElXUaHeyTs<|!P@#M>^m-uZG$mBT?uGijz@X5Du>?=0-63%Dt!LZxU zeYyH5>k;|4cOP2_db?kVaKdtIq3v+n*+q|??0Y~iu!|QmU|EF{_I$6t^>G9O=cu~@ zTL}{fFqRr8mz&R3&S}7HEQ+&%Q%xy7d5V9$#Csyj_ycUuE-6L_VmEv6s9lI|2bzUqo<&uJ!bvhY+Fa~b&|P@ zn1M`f1TrDVcU(lH7ueAkc?MM-o26q2&$3#GL2^{!rp??C)*ug1cLJhI7mRRyN!xRcrf z95+S{Y%|f!0abyqAKufMHAQ|gIY6T+_=7J24tH0pV44FqQX=@P6WHNc1Oys)^?WUn zHcu>tw_V=W=vxOQoxT|JxYxD)gOeR89F1l!k|)*P>;K^Gt>5B`-soQ3o!~lnAh-pG z!5spG;1Ghl4}rloI0Oi;0fH0U-GT=9!QCAO>Ezo!ecGRTZ`*tSfH`wypS|~a-_KfW zIqJt3db)jH1Sf`^?tmW?cKF=%JS3?l{}45-*VhafY~pPO!>0;hl$i~$E$=QkFdhz{ zQYGqLu+!&t6%2HniMqwzPGgN=jze<4!zp_#`;LS$r*|{+c=J4vz*Lv3u`TVY-_k@s z#xevmI`J?;Z%G?0x5BXHOrQ3vkWv5D6JX&+=kLOzp_X#Xbr#&iw~blQ7)6I3W$6Ws z&!2;ZjJe>`+HqtG#l-^`VB(VjUi>qp9{NpbG#7f#5F}W!#(co7KB^mA#L8Pd0FoFJ zfko+7@VIF)CaCb`P4!-IjZ_KdTDXc@m@|neN!vGs23P}!*$MVbvR74JVP~eU^?X?^ z18ef=Fx0CKZ|MQLppW`{>Q92RDQ{U>Rnk2OBJUP1=?D*jYDjbl6JJhR>Mg&)G3;98 zbHl0u+sW~{-e*Lw518hpygQ*jewmG8>#A`c;1^J+0|vW-7NkK<_LY~!lioOi_1#~QE=4L&W)o#D<|7Od=TGV7G z#tM=geMof;;fa{=@UTX(0+WWqCM{OT+L?#YK5tMq1TR_K%4Ej=sv8q8E~ZM9Vq~@4 zB}a=9!`vDlc!`M-Da+9>({&sL9&hfNId#+Nty=z2(7)xK1YPL3aIjKW>UuD`NHWoo zK?9IU0WRRt4|idQ1?a~fxuvs;T#XET+6vtrVjN5rxGrYh9aDk_=V5S)a$4{aRC)$JT95=X+}{u9PHCsx|OVsTdM=~ z;BgoR5p}$zav*wYptx}=jKG80(F+_TER_2ka`R$48Bvh6B^bj#{!_W?hzPddrc69| z%W3_U!{3F09?fCEM8E?pOL3NerSKkwSG6oLfMHKV;Wq;f5;CCtTU3>{m9f}IDkINt zFxkg$0B{ox37HDM;JdVxe>}|U-n!=#mKi=UodQDVC)=opHa zf!s9>4qugFx8-Ab@ng}#Ogjit?UVW<-n>PBNfBz)lD>A%IG9uNEiUdRgi~lkM#pnN z4@(apNQd}exu@erH5j=*`;QGqa|!ytC-dmhQ^3H$n1Lzd|GO^>PWW40VODvJsFqT( zdV|^S;GBOR@(+lV@C(j+&rNFfFes`J-24BxVE+HTUd7t&pTxcxCX?VGwQyzR{bM(hY(!M#ee^Bx~kbevdk^*y$<2a!_|LDszhYA`F7k zxuW)Csaa9~^bHnHjS0b~@@!~^pa(k-IC7>TwwHV-zz#;G0j3W$;8vEU1+brHC<-B@Ca%kg72x3(I7^(H%I&nUm#~xAy!m73QNp@&9Qy-dQHxGm8+dU ziT69XPKVUt(9msP&tW2!ewgoT6spX?QH>Um-}&jb)}TD&uO{Dz-~EPQuyoY)c)Pn)W#PmU>X zM!~RHgMG^@PvHRra@%PZKDJS{4YnxU($659=^Z3p4+`_vGY3_Z%>@f?rV9RJc!ZFl z6t@Dbs2T;h%PYj6;ql?PPXD3dr7=FV0-jN2w6q#gsiJnbHa;SF1g*1kqk6Rj)Tjio z^4=nQ$2s-J`-Xoyuw&<21=EthORQMuNJS8Eqc^AV$T@7Iks?J|$f)}WrcydD{v2|v z@BD6A>szF{^Ry{!2yDQdP$B}*0^#D|6e<}~L7{T4tW&?vU!ZF{3d;(U6Y!CnzIlFZOACeMzuJ0;nr zy=LtHVVv+#abRTT$y#&2o`-C%%8zJX z1!CyTfWnjiX*#s7b=y8&YSDq!fMtoOy$k<*4-@m5=~!(g%&EzVdu>sy@V@stER$$4 zSoK}y}_Yx7#^rk$>>%VWuK!>7HFZ!fAT{!fDJT~U`J%;=mSC8v9HWm ze*KvyrHm5UrH;P)8e{%LA^sl*eNW%mJ+Il$H&od6ADfhZ8>#%joWn13GMAHjH1S)! z55@j|nxKB;fiZHz$cGZLeQ(ryr!l|w+8H^4#}zRhwnxHTF|QaU1HqKzTcn1`+hn3) zi%A869zcqnni^%lsvRDL22(pK`r_gzoFRh<)0uC<}RN11D; zwUG*gzne{Rzig!Uwd~`{tMbj-pPd1=?%(!!XXZIC2E}3-gsdIbm+X6Mo<5sx4dq~? z3grZvgCb-k@8s^KTGl5~*lIi_0847=idr}?hPjt8jQf0K@PB%XEk_ z9Dsgn?(zO;0c5<*26>5fvc~8M^hJ{#|1unRrp@_!H!-7A>-Vz$7_$8l&Dd@rV zBnX@cVBweh=eHLh4QbN+s1d8$r7SSp@>1*M-7ePwawW8QL-T0X1Z|IbkOOnWoI0wx zD-L?tTcQ9x=Eafo~LjAs4&-oIFxTa-rKEH?2ze z*$Eb9h-#eiNQsvMOr!w@?e?Li0``6@aT+{N23zX+KNEO5Ebq3`*ypGoal!fnB>$Ca z#yPHBEmdl{`LOr_hQWWMV_K{Vr3aW2pV{OAnPst6B_@X-t*`8LH(4je7$jiqX=RDl|fnm6HIJOafSGD%k zvB)%kwEMZ$y>7eRHuC6teAy|~Sa2Ge$({y_lsOyVIlELjGn)BhtZej}Li>T2K`HNj zgMpx1;0R0|Qj0jUH{NXWJU>wcJ>ANKyykp;uICRNe1B6nJ=1j2$RN%MTYJB%R&|;~ zaXk9)<$qY@oIY#cObvECU9ycegQVa=_j#{&zYT9&wa*9*{rZy?AzOh<P``#_3&$321uEz3qcHLf&^Ik5$nw0LGpFhN?1y2UA>d}x4cHHf~R&+o)A37QE5TW6R`_(e{f#E6@G?D8DmK;~(v$ki?P5sAblwYLYBVQ*yl)TRT z9!(@P-Kg&!ro29!wj5lEYo!Xo-#tVQ(r7`R=g$}`V9A4VgW^LfvdDkzDXv99ag2L( z3wt3~{+`2|Ov}SJjU$OM_U4e{!JFosqMXpY-f@AP>E!lY@QAnKA{*OL^0ob=!yP9i zjlep%4Z3=S4-lde-Mhp>=dh^akNh4#`WKhhal!IA_nkqDHu!3}kr5o1aS8iV#+Q6k zpXS?^S1aSDzaYGwp19730VZ5h>c?;oK$`GLxT@o1uYNqO(wZAy7><~TjfDE)m14iN zKByD|v8Vn*svx=`@%kVp_olM__VpK67fZX=xo2+tdiUe~7FFqT%}eM>`jHxszd7#M zRG+JP<~8D*Ytt^%xcN{q6~F+0(h>zV0UAPAuQ<$fqwB;O+rKC-_~;vnA6u+XafhH^ zK4QxdZEsY~P`7*H zJwil}h*P_T-c8~2@-+#x@bQjXFiU@1a2@nWMfXex6qhdC7!X}n*4O! zw+i>vzi3@QoSj)J88rYy$%*dfLpYOfA-eSjZdgsBxsK)>TTOpmwr> z?-I}MRfj9hzg*sLM$i+U!E}3d%d#>&E7XKa$R&}EvEdc2_(_9e=iiJQ9S09DcWGYw&VcNc6Tugs#3q7Gkh04BfFvWABnj#~DuN3dWG?jXJ!FY=nzGOdIZ2s(z1=&y>t7@}1E;$9yX}WLiyxzecR`HEC&! zq&NBxV2(uHOz&xJF(FcVZzIR*Cg=q3viHh2-493OwptxZ8TUTlt@gVZ`o^O-d(+zc zMy?5UJ3=x*&8nq{{mhssT-LN>Ton77g0QJ1ela%T-gRpv7XcR>m$09Ub}KQqDTC%G zhBv6eO}$Oy$}wGrTUu^vJB)?j5;g-t9}<63cjSfYBO%OpP28mF^AYqNNJbzdv@bFj zMWIq|hS=?pyx!8S0V?gvFP#&c8{lwf_?O@mPssP~JR?#h7m7(UwQ1iFxyKI4FA#;~ zp2wnfTU5zOpAH}t-E?%xZ=a?UGanM{fyN!_WD(g?N?pvWvrr%9#pcn1XB z`z|#3;l~$-Q#Yf>zOAPL89%xXYhaiHeC=B2uR83pO!HU zICOV;=N_}qTHG%gAGCkC=xei3x5sG1Z}#_GW1=$q17Kx`tp_|H2Kfo9Rh71^Z*K7enSK*l)#&mjC^34e@oR+ z)BUo(T6Be_TK3N2USkoRPUjQ)9pwZ|Y$#;Y0n1z$r+w{qFeNag@r!Psb36WRT}`_n zT70_`NXL~LO-yR&#EKj3m~h^-#4qglclbA?5G*2zg_Lo?h;b$Qkx@Ny0to>vd)AX= z-YSK^!g)~Cy9;dzdlJ$k)PAt0NR1nbA8nuWUs-zJUP(W6y&0tN(wWeu5QcYBU1uW< znv}$K4EQxYKHl$x2^QVT@lq~i5Nzw4ZYK`N3uX#6Ln z`H)hJkfr0yB)0aCgw~a}oaZ|)W4w3NNL&UbkwK=_iN!{tSSvYpYO|sCjl);z z)F_RCwD+>H;J*WGaLu#sdxJzAbDy(OBL-ZJ6ce`!umCENe`GW&4AaSa*=jOY3tTNA zuN#kcy=WgpP`*~PN0G0r9jh*nJ5nJ zh9Ip4u27o_`RDa?U$!VYCU#kv8JI1q)aJU!psJxUiCU+=h<@3RIogW}(ETpbOqaMo zh1?V2wg8KUUq$P&AJMNzPqdQI{>KT1JNz!8$I#feo*=t7OyjZSElWyI-TcJ$F3q#8=Rupk4i7hBgA=Rvm}D?W6WyG$#;7Oi zn-eD#Klt7KGsoF)ukT7AIRWeQKnyg3ebBy3bnX}EuVO#IFzu#r_c>9?3@P_j2=K$I z2%a{FMn^l~ckUiQXRUm>W{`2!&5#6y^b2V5`Mf=kTsw13=tjfj&0C44O4^bG9xfx4 z&H2^L)R!o`^vZ7OBuFqX^k8hQv<$+7oH@*P?1PTfaAn~=|U zRo^gRT?v->G`_rGv%A(wzpPf%eItV9d1EglB`34mSpsIFp$rhzE5NwL6O;a!zi-X_ zbm5|g+te&pE2Sv1Cbc?ru5TNhKAx^`I0RPMB$~&rSk)MjXTMowRE{3p^f;UL03vb# z;6wPI7AWvoF_ufmCkikrDlxUXhlxp7RS1Wr)@)jQ6Ctjh(8%Y)BUFT^w*DWe3F37Y zG&E}@_BiAw>q1gZvwR=ULEp?Sf21Y1Z`&gEv6&2E0;DR)!Wf3wYGhH49Q`Eb@`f-) zi8}TB)U~ej6lNVM0ombxuRbPmNE_H7S=b}uTO6BH2=ShDRjdMWJ{Te-NV8+#kpm-_ zDD@9Sp~s)y-Z_<^&HInrzmnHAGHQL|!C&C&%BAgIO&{{8H~6|J`P3eHPW$^nenXc0 z;}0L*_%=$SaKmty-mK|_0r4(r+22$TtRctIV>b5b{vpK#+P7`bbv=nuMb8zo|D*3x z<33X$tgwRg-<^`W{ugF-34*}H;dd5Nyv1fdPJ4yEKYqSTN?T926AdK1hni)7$yo4w)Y z0)%z!TMoqQlfS2+y5+2GUNl8A>x9JGkurSe`RQEROt{t%My%d%qgtf8B3XjS`V_bBI+ef~Cn)$WgaT zX%?g^mT6q6%!&vdA4e?EeEs#gjhefGhnP5)K6t&G6opJBt(a;Mi=3IofQyc6z$nq& ze6)z)t5P8Ne#4jBFoKn401Oi$+Zm8YPbJ*G*fjHf7xaqbn7CZFD{9|k zvKYZxNzjj7lH~>d&sZPAZ@5JTWstYdhrAX?GsE!hH^5!Ii3yGXeY$sMOfJ6@@D1As z40{yKu-PsZ&)==f(rPpIej0>@Bc4+Uk|+|uc7c_& zdl{ebVQao`VALlcVfSnf8b(6E*UJcC@RG3|zir$?cjIRIafXO8=QJro#)Uuh&aOri zn1REF(;KN@h4p)mode74oMPN(rihBWRNEXKE5wR+`(Z2(E z2;6CQ1RDkzWJ2l$>G-WgsK1g4lL6=>f<}G!sU8Gp?F0PyLC4Di%jRqywA?kyY}TNk>z8E_DK%%jkk2O+viOK}kzObH2aF)}x}4qq`2Xk&7yPrvve; z2{K7PNV*rTh=ydC@r?Eq1)21`6=~yWStp-GHLHE(^>d{{*BT<`*a?r@N}{@(*)BW`aP{t! zJUVBysf;GM5@h`uU!?HeV(A-oJQykK0o*(MeS%zmvu|Uc71%$uW(5)4`jUCR$H<|x zWdD0~vg#9ar8=EWE{f&e5Zq|za&oe)RjU<8Fr_e~Z6#t!9efeStvD_8ZIa;DO`4{}XuQ^qj7T}++tl8idw+ZrKzLHn0EAzpNyQ7+r#sJ2w|KPo}=Nhm*kR$HlpRCx7B z0o(T)ZeRv3Vd-r2bYHm-MWh}VV`G7S3H;-ki>}cr7X^Yt56f&=nP%=+%l*MJkOo$3lWFuwDQ*W-_T1|TTZl;sA&A1fZvFpG+Ei_ z^74xsxq0rs#Y4mrkH`Nc&hU($u#{1PF5+nB+|v8cjxJaxDb8B;1@YC8YmB?Vp$=hQ|OovBj+U=p^}Co zvD{Iw=$(Dp74-V5-|YyXU#a(=ZdrFD%)>cU`sKOH=R>S8Iuq7`3Bu!{!=i?p)8!$| z55cSrK&85%3HnL7i5di%QNqsyG@;(eUfffnd z?dGIXSuHQg$G~@5AIJyq^*jcq8nftmK5p~QcKSi7Z#pQ%9b#P`x8FXJ(y&#Y`5(w8 zQA0D;AzpD0ZQFCpO0kSKuUid}47--?xrvM&Ei1#4mxqxYt!a)dr`Q+=0a549miw@b znfG7^PN(OrrRNOn$GhT@OT;J5s@FYEurd2HjH)}(Dk`y}zL&3UyP?`UJn=VNAzp5N znDadKwty~wM>kHITJ}=|pI7>*cBo`s(0qHixCTSC0UY|5e}9`30uNKu-@qDqBgJ!- zoCdc~T1PQ?R4a|T?Jny9+#!VZHR17ne>;z4llzXK#+n%)pPRN9xK2OCLO8fO=oqFs zGEA5#Ih?u(7g2jx3L86t#RrV+ZwO~{gnQ7Wd|rl4O5E0bYPfIl62$aFU`EMwa6Nz2 zU*xYM>%N;T8`rW|q7NeNwRy!pP>sj6@BV0j^F6n_Luvn2YdLz&5#FWJ>P`(a8>ZAu z0tU@hS3?EGiWpDKUr5fla*ke(s0O-v5o^3(et8@Q2n2|mKg!V-x*zsM4&*krZ;85z z);*fF?JQ8}68UX4ohyub*XeSV#NBK-tvcx+yh7(PX0CYWyeHUHW{pP%IG=|=LUan z-7gKaQqi>U?^E5SWY&MF)Hew2f!)3xT|CDv`;o9Q;2}|7lbYkj>A!J67e^nkK0i;Lqh19@@On)8iq^@HtYl)(HmGt!EiBuPFT0v?Q zynNIO^E)&V#eH-DHL0r!3&t~MZ?CHDZjfvdCtQUe*OqitG-#nK3g2|aWR6;v@*G4A z@#9!XG z`yu*fmEG70&9oI;Oln*QL4wE}9WT?efW&xHv=!Xp*6HGq&=kB65;Jx2xuMw7j*Z7Z zkvRYiAT|=nc}_IGj1^zBb|y2N3!XW)>Af)os-(EbC0yi^ZPdAzTRFK=J2ym+5emit z6epPDSXW851>(i|-7;+@EBIpqKPW&SQd5ek4kTaWmxW>>#cJ={3ni=@i0RxWsVEDPU(8nX!}F4HZ0ZuZUD4#QijfE!a#zP8=VZ}VyTs@0> zQ*ooEu-_z6T^K9$T5%BS&Yr7q2hSc&Se$7ymv()4B&%=r?vs5(uPZ+G1?zDK0gYT2 zRi4!A-zQFDpyRo(z03VpL&e(Gx;R)Pht~lu5$^+ZCNmzZ?K)ro?q)fL838{ss-U;& zWX>3|#{g|FQ{4ME_alXeHFAZ0NUWi$H9kUb$LSC=38`Tr%zHj!s&8nY&K{Xl4rp{9 zKudkA@auh*LO`HD*u*>hb*?@xO`W?z0TuX>V?S5^afMH!pdIg;9q{UmWym?s2!zY) zb1l>bn)Mv$2b@D^A_=uAhpY+-sR2MMM{B&hAiOD3*vU!0pZu0xv);@2j00$}DbOQz zG$5ZJ#QS_rmU*>ynb*ZtDv!fNLqW@jG$A7wfgX2QO+Lmw3mSp& z&S3rbFz-oJv&K`iI{dG}XiETI!8+tZ6ZAEuyrgH9tvoXcEcIB5CO9?0XW5*%Rg{Ck zrkO4XERYnerLW-zL$KCdQhKp0(z`KVxeZgn{BLt;XMi8N`1Qvhbb zFxp68&youH8y?6f_{%IQ=RU@d3@zt`eM67ZgcM`qYtsgECQZ=o5{znrwf{S?gU3Xt}G12{D^1M@-^KVXnW`=zed z6rU+@;lM|SjYCejrO)&hww-Y8k$1=vcFccZ4uQ?6)&Gge`rqHtXx;&Tn?v1xHHx|K z#~&V1LFeamU9**@wTEwF2mA_PQi(sWfiHenX9ZPWaYE`-zing1(xCt`n?{E)!!sKFo;hcBRWoMdU&M`_ zmWw7N?B-yGLU+%l1g9?p^Rqpn^DLtn``M@mhqHTvgL5|cK$|5>Bi@FbHE*el$Bx%^ z#6ya^6$4MlW8Q7wN z{*#3oBk<;Xk$rvF|1LlVJK{}eAw-s2TL>7wZ$Z{i7t^L+N|eqL(xPQs{h-pK#m z){s^<9dP3^&uSCZ+fnRtP+8C5O1iiw(dMsIXaH})Q+0>2)rlNtygR5G0byJ^Lgh_PHBf& zdkgJ2e^9;cPVeEZNT+_HG4v63{o+ZEFk>*ktjhOUwg*$4A6=eYS6lJ}Wz4=G$!!e2 zbX=_z#4PPxRdOMd?6IOW2Qo58@q+-VMs%!~K-C zFMOf|v^{$zKXsmxFv?-Y9gf z!#GbUyH%M^3v$ReRE>k>cl?lY%tkgGYgNe`vy%Sx+{s3H!M}A=by7WJ^$ByQL3WjI z^;GZryPbuh^`#i6X|e!KV|0%!dihd^U)3J9OL9hr{B+qVL13 zn80faLoRiuV#)P9nzd4J_ItDLeI>u^B|X5XE*98`%8>fUMLbu;X3>oHxzfD2O)T;mx!0XO<2O0_Ke|ma!SS<%iHrkCl=!+ z(GGIV4~w@UU*1VkN1En7d3cPep5%>ySqya#JQO-;J2zmZ_wI#Oc29V0&S4686rq^G zVDqSj2%M^6o(w?~aZiel59Q=+w>sp|&@@dTc<`r6}*q3ko zD@~kW1?@{Yy2SpU#RD5zX*`YUT@mDxYRLWO*$Ye2TXq&3MVvN${RBN@xE)7;Jm%|U9v~UTM06Pyi{)~W-*KpT@5Z&2 zAchIn_h}hZYvKtxLJpBU@&?aLCA;ps1aqfyhDh~D< zjw@GmV$Utd-L+T!T{YBUHMCrkMOxp+$hx{urf`=fGf!w&5W?2PtNLDiCj5~%rH z_&^M6GKI44T^y>@AAz6js^OL_O@Uw;uJ{Z za~meugPuAI-F%ak0KnohKhovU4+XB>U$XU>fJMYE^me!%5o|#k62~mb{|HmKVw6 z$!eMQzV_wG=`2!XWrT*JWAOuX`jyD9fv&c}EDArtMI235jzY40(uW10ryGSaw8)Cv zh6sfX8Dv$SQx>%SPobLelaoucFjJGJ#mG*jJ*EIb-sQLNe^CtyV-K8E5A>28O@3Kq9pTox#@{EQ@@9XxUl{UmrBrnBxLuAFNTb z8g#pZ9gCS48qOhCsTw6Fyo~9+OY=#?)6eew&(Ou+)H^LLPfbD9UQ3@08&Ff$i>E}` z>ZEj&M=Xa4)AzENPEw~8k;0ma3TOXn$GP$Z4Tf>XhX{2(3|;XjXYS(&zIrAQYKg>z zPxK|qU@efwOkTbfACJ=DkF36P6>aqtC@Cxm^tl@M%Kbq;_yf>kYDn5U5uVl>mh_b} zuBH<=qfHZu+w`rdKHv~cR_CK2m;^kSI@C>o&B-zH*S~Dr++>}SHiMvmcLgW{O+MPw~@W9!C zp`{OR;ycf``;W_zN_j zcX=Q2%CYrV;vGS4c!ZE4;qM0D9SpVnwdFR+RS3jeWcRSKI{3c-0p`E$vl?#E!z0WxTYpdIPB6!&=c;@FS53&LVv8Xd}sEfCj_y?nnFhM4h+N% zyT#9a;dO1Y7du6pa*DD%72e>hZ8_y)HvN?3SQcQQG#V!R&v>g1I%$@zkiOf*;|`oc zD#+eI)7)mth&Az>Nz?*AYAA!&6& z+(dQK4wHa}Fu%pJuNu6&Xz4~a&-os=iTqKY<_isaWG^Pca)1V18`9gHM1ciTzU&n{ zshpULLWFFLO0$JW9GU8sBfF?S2;^UEfO|KPh`f)BpIQ+BC5Ol{Lc+C>YFoLDQWKFv~;snz$WdobUYSlT^Um6!- z?klST>!}&vG)S{WDHjGw!r1Dpllqa9@xrD${0W#Yb{?o+ci3#LOX3^1JbqZm;8=Yt z=33_cI#QR27!(pFYbald#TIu|jF>ge$z?XqH-IoGtxSYM)9WowKaSLhi|>tLeqyK-@7qQgus=#ZR( zf1Fb!MQBUJ+&?Fc{Y|6ulcA8i6itEw&aV3yJ0_KvjAp?5;~Sp32K~wPD{N7CX*q_Z z94e(A>P+|v6F>Ts-G_{l*LT;VsWH$JX_%uNaFgOI1OPk=?VK8 zRJ#G+0n>>+dSl9``VJf{Wec$Ra%D!ssyUwIv70dPG_pE0?b z;El7`Y?*RId-J#n96h^~%bBtUx0d1rRh_8Glz2<|h60~`W(yl?ymte(DbSujtWNN> zU-gOm$}7;>khn%#{CQ%{U(rQkY0~ z3A`WJpovSnD+QdCyCfGKfQ%6Vg?=T!_7s4;lo+7&**i(CsdBG8^Z;iLZ|hRVuNcDD zb>%887BszCF%wo_b^vP zhFD>4Kk@V$TSJvDH7gC)Lf%2~{uM1VY)Pu#rA_bnOBoof-@sqWskTX@a)Q2lZS}R+ zlT;~ialT}%*~CT_yIpkA8}|aL-V9=1Y$H4JbVib@K-id1V=bF&vCUTqEAqno;MRuU z+&wxaQC~GI-={7%UwvDr(#Q(Dj)>T5S850Qj2hxOa97#Kd%0~q%e+@-p>a4bSnwgg`+--iIb=bzOA+d2{d|w;{q7XVoW-Z^mB+fhkco4dF z;-%QQ-uuR>lf=At(ug%^*0`M%;ge3qsim?xsbTX8yK2!>OxSBzu#LQj_!DnvsrStj zvTQ<#%h{?Hs9xI2O6&XyRJ@3Al>F=}{v;>MppHn!}l1UZcQ@`gqzEp8TdW-_>3CXp?MN1~Q!&~w%K0Gv75A`u- z>58$e$OYxVJYJt&VMW2UqNt;@h&Il&A*Ef01ign9&Clng_JREdn&IX6#yrm)@g2+= zJpo~ANfL>1S%0y~_kjQelUGK1Pgt*>J-b0ecVzPeM(XMgSZBxDCtLFMdSF#Ld%dZU8AMC!em6zIaty}$9i-2Usy^fM7n$tf<$E&%0#|% z9}ZLPFm&QqCgS0ahwo`SJNh`e_$o`HhiN&QwbD3M~rdd8je5F|opVJQmk&i(`@@dnQz4dIl~9$M9NjrY~DXi53->r4!Bz41A%b)nI7sX4^g}rdjL{I=l0O*vQus92EXx zw}g{Y_%PzD99;xCnR<|ABgJHXM*pXgm2E}$Bj3b$GT>)Vak@ID&Y$K~!8BZ-%*riO z8A3AO+sY}dEcn-lJSv{&(h48$c^5g0t#ws@E-Gd=zPtK!MtTB zoh-3{pkRhl((V9bp!s5C9q%8pj%ShURoS^vaqwV6s-pg83bf&hj1D5S>Hu7RxEx?| zcgVBoa1Xs4NJa7Tr#a;w!xR%s_8wBG!kFdPauw8!Sj2{ZY(sugH_whwtCzZKmSg z*K8w{%Q#hU#b>t}kNiPUxw6+aHz|c=_){Q^J@HVSMXT@Do7CX9M_in<*|bzEa@Pc>I+YPs}x zmRE7AUy3E-THC0P{6D0nP888gqbkRersDrz^V$Lj5q@aXP%d1`Fgd_h)la!y$UDn3 zzrSR0=YWXMenhzgaR7oTSlc>06d~;uKfd45t0Jqmgx@wNof_zO$D6lU2)R>=u%F(# zJY;`|=v@^Me0rs*dKnQo<$*ThCl(?xT70Y*6dP`0pc+&)`MFnC2xKG+oop$rQ)KHG z4_{8aTOFNjW_=5CL78h5XHPEtYxHrzw} zZI07-o~U(9)|9o&yV$tJ>796J|5jx@P?YAMch3J+=NEf(q4%n;@|-G8)%udUh=!(^ z8L_~uKAm1#$jErK8s&V~INu6;i)7Ph$X%xKA5Sd{IjevNSPol7v#@$9f75~5a*O+2 z{VY2k%XQYZj5Y>gAN_Z&SOeW#&K_Z*YD-qL~)T|GnJ za-$|=fIeCi`;Z@MKzKt4r%Duy;ni;t|-hRQbmv92)1YkMPmhG%5!hcu#4kE5#_T z@Q3h7)PaA_Q!a^rQCM*s=3gXY8fWRt5*)8)G_p#E1Xbm>Ko{ommY7aF-V&K8PQ`x_ z1uhqh$s2k%q!zQco{EdFw7j{ajIkelr0E1_A9k)2;zNC=kD#v(pVT-|FB#?Lno-r0 zx;wy$w>K44+Nw#n%ED^(HdQE)n!!t7SKkbcHKw0zryYH(y@k(HDG7z=nkx@e-;-@e zntj8fP13J^E;`kns;<<35esX@J@UV-vHSVbYLn1Gpl9`{U=OHyJwYT9uSv>Oz%g#A ztlKe#DCqoFwaGu(dgPht_*Cey4GKN^6UTkIyecQO{PqYGdNwu|S|{i&>$}}_Tdye0 zxatav@6lm=FgKAt;C~p`xff5ATU=Z7VlbjVK&KjMkDY>=7Bf~lkGzdH+1cs!Z%e3S z-C08wG@|#`Q#!abJ8JqfEbl{cm+9Gj{I9Qvo^tfTpX&f*U{;;C4DmH9SwsCk6K zBh+DaEAKdVh4Ak2yX#q_;e>+lpAi=pb=bU8{DMhGciGOuIv7iJGi&{Cg(J#b__s9v z@Ar-VCwL~a9L+{DmQtwPQ@peG-e<+8_*ZPKC%@y0&7j4rsO!Y)T}gH}_02B|iJz4% zhpe;v$1Ba#PiK%$lPm$pOySq3tJ&x@ULES;n`M9KSD)FfI0?rNPQ4aw)pS0^1Yn0~ z73OE9toP<|;`+a_<~y(Hl?nUd=wR)XWLroFm~q5R z^vaecv1w=5V5U^3OhqVY!|)|*#}<}-Qi|Vw(|elh7bD`3`Bpa=iaO@HtxoNr=_r<1 zobCHT?7x(DmVZsZQ6GmckKQ$k8o zI!CuiNT(nnqeS|i_x&f_-^bUv&Ux^CU3JcRf6jNRy0~gruFego=IV*UmYNTR1gGa4 zx*XUDm!aph`b=7~N<>_np$q0fWUs?-jbL?HFV&y}6z-=Z=hM-01`)~l4r5`#&+9!u zy*6mM_$1B+GO2Y=+lQB#AfiDxM?qlV!D}L z=eu7Qb3Mt0{$8II1WJkyM=e~s64fyQ6UcUoZud4d*V)>kxw&y2rxHeT*bTw9)V!bH z2E_D@UwDMQz8p8Iu^qVi_Gt5|3ro55!0;%%ro4Q_IhMm!)^qDaPG$}!$Agsj@peho zV9mA8Mo(FacML{+!@YsepUMrfU;5mh5!v(!3P1VyJkxu$s=Tq`H7@AbpM#l^7o^tk-(<22`% zjj$Kg)R@JQ$+Cx(FDqQ>Up#V3K8@*kcUA33Zv!@WZx&TJz32PH$OkW-+1yQ#6cC;1oKnwnL#!7HRgfc2UDQ=6jb0^^n3(raw9<&Oi7l$ z5#INnRoE*Fl3Tk)5Lme+S*Kj5SpZJ*eys5wdR;}gO=uqLcoD(CJr*7JpY_)?2vURh zV%7)fCrzu|jNOLmp6#%-@< z+J$mRu*^Rxso^G*mwVpB$mye=@^gd=F8Za&OLo29v8`bn`}-Y+4-jt}G~}g3I%N@V z9gX6?Uri`+_uCNGmLd^f<7I?cnK@7-Joln`_!Qt!=n*agKHqU^M!1sVr$H#w?(7JaXKjDPN!n>u5 zMM%Hr3S|3MvQzgmzg<)fp?X&tKLw3%o+065A&VWzH};81*NrU*#+9VauFs-_e%Xxk7R@Hl8&%#~`Lc4wnvZ+e_w8=CzG6#cs)8CJaNW@O^`1-8 zFYfwnC!ZoX$z@cx&4eu_GJ^Eh`JY`wRj+GmPHuMIG?~Az{9+VEjOc7M1 zxx53dPuAZAb-!qGRAjftLzzXk(`Ri_fZGJ%JJQHLsu?WOApLP-st1^2FF68zbs!biNjbEi%90mytob*+U_K*(4KGBAk_1qfl>noX78<{NKQc=g4FJX#-frE1BI(_FAvN!8?yiwK$1DNP!abLXU>lfa*MKVTXV`|=Y# zv-!!B4=fo^i%W6VdpxTCNVK5BoS#$kN!~zrXMU)Zy4Fi@mJ9YI?%2!iLALkv?ca@i z(r%~C``ui$e0j|i!sShsuednc#(?72!xhEO&Yu;3u01s<=QWU*iY@kd@ zb&>&1Ag+8xaw!(RCSB2@h`OkoqS6cK^Fj4y#aQr0jtcb{BTj%HgEx!&5`Bqffq)`` zG(PE6V_U4KKX{wYD(N~S)a6oIs#9!J4N+k%1%eS=cn`@Ay(E~@SzbSD28_@$fW#VI zO!6XmuBDHh1=nm0hIgO|kgCJ54{3KED9huo97ZSJ1QHR#q>Q1{%tI=G&Inl4h~ZeV z)l%|6N#JzZsL71soQzw}%E}BcI9|ZBRA$~wzi){gL?@SQKB(VZCgc0ZZfr&6r%5m* zXv~ykSrkHR9R*a*FKFzcqp!7GiG<*3Z_N{(Cj7h`t-ci83lC}dt%;!_5q$6ZgjML_{^J^&9>8$_?p3ym~d`|qni{PJQyn=On?$#0A#?LYgN^x{LnEVlb& zSr~a~=V-!oPP{+^*YXc0Q_@aU)-#g2WV; zwY+Cwi|6^j`sO8qCGjZu|E`So6bB=U7KFuHOVsX*xBShM_1e_ikv=f8_3y_7AJcqu zO9_=(^w+09I-$BA4}svH#GJ$r>-!-$r;oEz#8O#7ZxV@AU87VssI;u(R>~Kfb8r2C zt+Y99=P-@e#+$*xq;LE07Zj$5@=G>X>DN?O7dD0ZBYu5K(MVoS+OXI8@Y(y(s*4Cs!6889Ndj-~x>o-0a&;3alvg|z$Of$cUO9QaXX z=a-_yZd7jKa<6!%)}?rcQ`?uqZa6dd&~J1wC(^}euC*gk;zYYWwL!7Ak54t;MA`!W)N?!uqzYKO=t)Cr-**?yA53nU)Z+GRO!5Owogl!?S zR@1-?6T)c~v0-r@z-wsHq?*HoPYtpYin6L$Qt_ z^{>xI8OA03%z#Ji?9SucY0Xu(FwG(4UA2P2K39GVYUp`&?#}M6u(Z~L@g_+{t}8kN?WG!lb;__-Z~s=58P0nuj2a363VV)}~ zk~NB+L^zQl`AJxxW4Q6GVJG5Z^&ekx;SZO6`2$ByLvmADv~llYfd}`G^|h8f-tTue zcRwUvjFx2WmES2Q@z@p?6g1Y+Az%BHXt49+dFL69e{z{=l3yu@R6{Gb;+A{|2V#x` zYhI!6b8-1fZ|()!LZZ^nIM+;F7$^po-j%$lc^1Jchlx~BxN|n91lKYuHMRYd@+;=B zH$zqn<2eSD7Vv?s7+HJOwS*0O&&#&8y1H`ln+PW%L==0<<>*VQ!-q3{?J6FkLT-?W zPy}Row`*T(m^oQx@1JfOhwz5V0G4;Ofdem_Z&PA$+9yi9*<>%C9eW}M9fam}X{a`S@LBV5Si_?ue8{1UWBM7a>Y~ zNr);d+I4pfp8Jt!^YA?~D!_9f1oHb?c`pR7#?H%N@{alkRvxx5M9B=~)> zl)$jXBD23Ttf2h^6c&1gkeF%?Ut)MM+II!Cxu|f zna{^o>%2=I)4bY=XgzjX5VoZd1d#CHr#RMem75(CSn>XxhkLlI1dCQHgiUF3Q-@pi z>uHVJ8E&;$Tt}wsAqi*_jIyajlD)C)th z2rj;G0~)Wc2DElFUA zAhYIp?Si0p}_UOBNn#qab zpZ!CWJO`Y#J3Cc<*0ujJy>5x8*Ge%k$m|*sQw3^ooASffrtc7WgD8V`0)g<`uI?`J z;R09`v-nU+n}yiG^NeC>sHb*w_|Ni_vE|Z0{eMTaEEt8qNtpSn=XqQot;i2aUHmzvR=ea%Ax1M9ygBEbCJ1FXMzn#s@U3LHD-Ab}{j zi+$w1{bf>aO|_~S-Y9hP4-*Iux9JJrs}4*-fOouz<-73;Xc>S8Bpy~E*ZeQB91tE6 zo9w8pe+usw{!!AJg(F6Q1@$+9Mt8&nnbSY9J53E5_$vKEq=Cy2=ax`m3Am8;5d-2sxrX7QW%8#0N-U{qtQe zKr_&tg^ie*5j$}d{cANxsVzi`S^37@(5h@+NSs)TDb?+j(Kq;zI@e2R`vh=CfgV_{h%aC^JU}ymt}h#sKM~5p~Jvknzxremx0lI z^~-G62jZ`1xohvke!hUHnnUbk`07_2F9XeMEFy;rf#@8H_0_Xg@2(gG@#i!2E)HR< zG>vA$34udyvY?t=4T-9dz#>-02AE~<*Gg1{KL(8j9uXBJ*HHi20r)UQSfHt%*cdnW z)HN-ur6y72(}td$EVnqK8UZQoPF>{#OPd;u^2T02G4{U^#t4H)&7TEE3juqRD-NLk zw|saj&$(eH7VI5V+X9A!i$PB93*jY@Kzl?!hWKOAIV_ z$oaOxZ4Qp|gcJ0qhuOb*{O_9k8+$qV2R`+6|eqA+P6)D4y+)L zRXLdj;OlYFGM#~Ig^D)|uS&FDyb@hAd+~@s8A&^L=%qt-9Nhn^#?PI%0`4KVpIsT7 zdXpPrZ~q-4B-!=}^z6HcP;m8Aaq!dcCoe&+eE)Il;y-5&@}9Xk@agk|ZPKC_qO!mb zO+$v1Ej=EfP10DOwkBz)P@JYS0=hHejF^@Yu(gs%~d@qkzzzD zIW6K4n+XmMP*me`!q(IrkYM|ON{S{27!C!{CnosBRum4!YG<7F=^MF?4%yhWkm}t& z6eYYr#SU(iNokZsjX6v5Frp;UFsGScz6x@ST8dd#Sck}{CO92ACa~T zL8Pf})EY2%*PMLjRH|mR3V%f;im4cc+W@J{AqV$gChb7mWe?~b@)&rdP)~+6BMFz+Jz~#-_q&9D<7+jxn{40R($J2Hb$eCtb;ibZX zkYPzyxM8S&;(YwvPi`h0CUD&Vw_Zqgr-augd68f-B#b;nOT z{@u~?g?JP!#MO0Ca#7e1dzrK`*pEo9&H5|XZ>4G4lQ74Ph=lmCci02OpFXL1x6nZ9 zs@vajyKq%!J!kCRqqNKfTUkB(sj5HXejXf=iVb@MeWKo#`4|H|B|*mSTirl0wrdhH_KKceg<^$9JU z^lKBLME5hDGKK%F#Gx$j+!74Ti4Voa+Xtb#T9&T5 zivIU6ZFgS@EUkmQ+j{P0`XQfHbIAJX{vDA3@+qNn;{ zP;g;vF*xc!EBoF#wM{}B6tqjJkv5dS{CA8w%nwud^78T!xyE(_dX5g!-PV82n?sc! zutC7?YK>|;sPyT&c(6N--T^v*xEAzfNCgtxlbD^M) zZh}-EmznSS^~)qp<=p>l3bJ-APCEQn|J%!?;dlFvm2DAex3fush?NF@opD-!_+9D0 zB<#|YJxw%Se~R?}HE9m4jMO$B-j~8F7ua+BGU-jtUnB6A=m?(9)e3x~bwGRUuMAX$ zGG&&+KfD2Cul}W#$-Reabgd^l>x`fF_+$FR8X7yp_Wtx;AN3*kiH77F_mO`yl9L)3 zmDm8=OsG)y>T+Bo8I>U#w)SfdAK||}fXjx`c24ipbJ|2vCQSDXB56ITO>?dOHB-b% zC`EHC4HuSAhvGCT!-1*R5CIX5pl@^0v@O3@Gt>ru!*Xfk!V50ZYw@&@7a|k}jnYp@ zwFQ}3`5g9;*h3L(3sK=Kjf%8<@%S3CtKJ}Lx3iWWIb^7@6R4zdTxEB;40eDqj$>F+ z8Sa}YwN0 z2b17SjuYsoK;vy_pRi(`e~LG&V3hF{z4&68K#{IRP)HNGAJ+`S{-iF!1- z7HNA8h>P6Bwym8HpFY3f|MUvQ7`Xk%sbM9WR{TBsEG(m}r$3#e9T>0mv-6Y{34**C za3wlk!-elu3PnLL+yk6ni{&InZ&G$Kz;O9r4M-UxbwQW)(X(zk(x9BFNu+=6NJs>b zDx|hU4iaNknx?spK>)*?^#z&*?kji<$kyH2JEw&%sCXki&$*iiA;ntyUl+)WU~thm zf^%t`-8xdZgt(~9)}x*1G%AT!QSCn*o&SR%yf1_0iqAWl1e6YT7qV3WFrmQgahghWwdQ zb?@D_te)nHD4UzQh`y-oeW;r`9ZKCSk1KMwDn;&$%=>RDHDSG!GM)uR>kFGStvIR} z90{$O895NI?=711c{WT~BWUF9&_VX;9v=S|L4BW4ml)h{ppsRMv>Au_Jom`cvk1P= zL#bpR2=D76lVhzyW`^eogN{IOxlD>mj(n3-<}`}(n$3=Y$n(tOR+yE_FNhylb==vo z^%&b+>9QtYW9PhGvCUo!Q;1(~jKyDP$kT8Aq7n2K1_W+tZPF8ZvqoCM_Ju7ObSBdjdbjab>Dx z-tLw03);iU)M_hOD^!8p->}{$q!(`Ldr-Kbi@_?Xom8h%r_+X9r=VR%km($HCmOu z(fLG6FX+%$En7XdnpyVw{aD5=-4^slvYg?;N342bKz*NmM!ndafIH9cbw*c-D{*T4 zK@Q{2R_u;-BP1E2VQ$U~9!-tdERY|HE9BMmPg3S`tnY5#y+2#GDKBu4nDmr0r1=C$ zRP%yOi|e=_ZbA|nKfTzRJTEPHJYFJm$tg`eW=%KoO|#s7G`lHB-;iVVxqOR;`$Ou~ zJ}H3KZ<7J}K`icD`s>pKZb^YxJG}0wu4`;*BPGb!(nZ4a1keK(huj>(%;nZ=@aMzDoupEE33|LL+vyx z30gYEUftYqVdF80Z)A~xij8<^50!%96sDTa!3OgUHPLV~aq5ZxZ)zZ9GC24oE=XZC z0pTY0^>m=Mm?$1mk!6H;sVij|Blb+F9g?%|CXro^-9AKkk*dM`xPp5uy{$U&mh!W# z05L$%kS)4lvo~1tX`}W$(J(l;Yb4B`okcyTzD(8rl2~JdKUIna$Yo4JZCW@*hwrmq z+Xl6~1-VRDxE;!xCdjv>UUxf-;NlQgyfalqFh(>WwP#dF_Z3zbv)Ii9xGy<^LCEY@R(!f!R>kTEg+9??yFSk11+_h7NX;t0JL5Hv)b(JOc>#)xvtw9nX zr)g(yoIB>xc{T%JZiJ&&+k=#tQmzz$hr|!463g=bTqccDec|YHs-(C)v8dQ5b$iu& zeCSx1a-(OMk^dpBBY@xZXHlx^_(sZBLI3WG$?Le2WsUox{+|;F+Awo;8654@Kz$2bH$aQA(@Q zuTrHv!a!$jk*Xx0)syC83%4E$L!F6XM`3Nmriut0H?0!kQ6Nx;+8cAu?*TD=Gt9~_ z@w4^!tFY@{($C2V<7=XQcy)80~%9;=&&=4SKV1owhP^<_PX2+V+&Sl0I9sQ_41 z7IUkhSEqp7I8&FJCvTmKNcOw@jXBk;%Cc8Hs5SU83cMV&*nOcXQ)j4kGbBLt!b$EE z=mopF`)}e_Wg>C&iQfJm8i)@qkw@99&%flrj%*!W?}bstB$$;lpVK`C7#A zb#?|&DG(JD9;(5a72)74|1e+9rtka(YsiSBBJUT(#;|=tGGgy?eo<+NgvaI1(>fJx znj%-fkY0yyN8$?lhd2b^LU2StTf3U~Ij#?DT#r`hE0Ab44Otxux<<(-CH?T=+4# zE8E-7Y;2-vBx#`95)NmB3+1)h_>em68p^A&_7639GPu!&?~(GC8}niGrOac+S0bHj zH+&PP^+eBly5GSt+Jw9Wk&s_k^MuUd?iHvBqh;aFKUZc*^z9f1=pf>81@mhC~6@;__7|p z3~0KQLJ?i-P0GaiE&g0qqsRp{Z+3NPoCK_7+RlG1D$O?N9*B|3i#RmMzbzbyKl!!n zrD;Ld^fik2UK|=QVzInpve_r(`OzGFNzXwHedv-yXENu#n31H0 zF6nADF<>8 z1K#6*Ypjo)Q+0nAxXJb1=P#UK$Mbyle+hE@uMGbh%m1%t6Z03P_Hww zUokdY*~Wr@k(%zA-Yb=yNtpOjt)TkJVefca#^hH1Ocm~BPNQMi4BsLHPB+6H6ltwNdd+@)73*ZmYExuR_svZ8j1lF+okDAmsCk1h)E-f z?zfa(j3jp>g*>kjhyz;KbGkHk`2qgn5%x3Op}hpbJ%BTg)#nD`ets6}*4yoC-IM*H36RT1su&70!AnE*ah$jGapSX4=u`V~# zSuCUNmv}(<(-a%A~x&G-J4e!Xx7N8qSo1QuX|6dMk>n=rWb(4L5+M%w$!Z zup_J=O>ONJ;6YIwHu^POkhj8kyF~XTBgD6Z@N8Oddn}gH+!Y#pU%o8cTngiWq}5DF z+G^gEbAK~?5w93sRoSvzV+*yk_*!wM0G`uh50aKUZ4uqyfxBQC)ziCMZjiQ;yRqj5 z*-`84ftB$;IR^MGNTJl=M5Hdb|a}}+&O|_JCrT=r!UZw$=vOHZcvm#UW`>Ci- z!xc$sAYi#kMoL*-bI_++a(O{s0EKWY|B$D)FOQGFNEGG)lxv62hWgelCF0);JijY& z-z<2`KhoX+yv{l5`YC~Ci(LQ(<&O&B7K_x`%9Pw<{!DVR`f7KEQ)WX;oSi8(*!83r zlK|^=B@#EnpAV?7jz6!z<;MBy4>;>?Wh0z&9{k-?92Idyohlm1SyL87m3FC=r^EvQ z8N%W=++KUFDskahJ&(bclA0axU(*+F8`fJCIo~Rhf=x7QYV{~e8>-u%N^Nt=nI*_d zs|7oqv^jw@e(F8Sj@QrQ^tN7u#O*Lc8=8VQ2`#sssPaN^?(#OMQuo^$zue$zo~sFa zZNH2zwP1v#8jjGd)Qc*!SFM=ku97x5EGSeXgTaPXXogaqqSNr{rehFIWZfV17HoKu zUM}ZlKC8ma5 zTnOE@ns2#ceS=eX-mq4PwcNR+EF?tg zDeN-39vqjw+MpBK?pGQL+N>?rwC!2H>--kK8&{hUu4T%cuHOZhzep0aWW!a?6X|532WJ2j27f~q20;O`0lu~K~b@jeZdBx z=#3R(sp{90%@@3}>=NrE@pFHvo;|4GHJY)g`;dJE=AgU4Cp^v zuV2upsDtwuAo5oAU??)x&>!CYYOyB9CYIr(PPuokL4*rv$c`L`w zYSoCs;j05#Za;VkH)X9^NSn0k8epkX3WhyiF`l z0=I2Q_DLA}L#oocLQqbtq}F zzq&Hmpq*$8L+XmC4 z)~gxnV>kUrmp8R;Mb{vhQmS|AZ7G@jO_x^dhm@#}X@9fY`NRc4>v@x`>lvCesOi+s z?k5uR8Z|zH$fiPh&tiwI?MC*I7FTR(-+3^I+*By7pC6t1NB6LUBpl(xQX$C|lzKl| z`Nh5fD9asKTTg&K+w#lum`ZzLD{Mi-5mYA%OK#QM)*eg)64h3fGTVmbCHHNp7?uqz zPyaqE6OYeEd;8L51EL&tRx{KR?kzd4udm~Indnn?X$@K(&j!|JLL%-N|e4K661-_+u*r; zBQMjJz9w_W2UGE3n?1&e_3bV-RAuIlg`C>kh@;_Y%@2}bgYKO8t_+>}VhvwLs0tlf z*bd+g0%3d*+omIlw;tN^rjV+>hmaZ2;`H zmP#>dJk#8D1;@oC5C5s!KJsQK_+KaYSnz;ipa*##ZJ_%tF5UEUH^(qE$>F#KqTG`R zd!FR-ylY(|3TogUMsZw4rkNJT3g_pX7d9(|AI`;ey=co)0jHg8$*_&W0>wPkAU{sR zAQK+o{tH2J%@5wlJ$uxa@{7#~haL0`^lftHTtFxZCWy>hS%c8ODtmZgOKR`jdtIfobq%+fLBhR)dbwwZ z43pfHdUx)rO*lbE!?W|` zu)&oTzq^P?D1Z2lj`JN)kjU80&SJt53_F`}y{_KV>o@7p(5C^%&Krhnk-vzJ9imOJ z)0Bn#GWJ4tQL!G698;vnE;QYOEUc9I`(5AP^*k>mlPYqHxD=NkCO^f#ENGA*ww9SG zTm6I%MjF{1sYuBX!lcY}zgHDohKi-}zZV0A!eh&O zw~Gym_Gx~E1+})+vulx^>Na|1+|NjMNcHHPQy3k3dy5M)81D+n#VAAN3pV5rf)enN z>dRNtDL7RFr@UoU-N{Ob6MBZf%zXVrT6x^HhK_Ul6oq)TtY)lq8mS}pTvqN^mB;6Q zTNq7SfN?`h0+kSbv^iElM80OkU%Y9CP$rWzVga7TYuZCuZlL~pLR@VULHSP5)h<1M z(~2ft!=KTaont*gbKMkpbe!xyd2>Ke4dad&^VACkOyt`-oO+`*j%FpTwZ6XCgTM&{ zU%7x5N#0bbp*!Fv2fU`{#8ot0rncHrv%761D%0aGX?*&~|9J>LCK=&~#l>ZI3Oxj_ zz3AE2`4h39Tex4tz~N1|2IWVwv9jlb`x2jEKEmC@mlum?&zqenFvCj0b6Wvbn2*)^ z^CnnBLKu#Xc9H_hEPF#J(}H2)Qor3m1=BCu5ik<&@A>EWrNfq~YAF~w>E@o8r<2Gl zrey+hdMas|U@NSX)fG@Q+|^gx3WxcHgx6?$D8x6d0Y>qk^}(OljJR>Yo)Em9!)& zfTt<~af``_O<@u8C1C=r0>?i?%e>_2svyX8a#@i0#2ySEqBB{w4*);k zK7d=oa7fb*_-u2KjhdeF7tDAZQT~}>JaV9kT?{3J*N&=$mmY$T3jFBA=-qKr%Uqf( z^^cJ7`WLzigvqXsRML+rW{RQ<{bT!s0pM4g*nZPW>@#9*@`ULT8N(;aA~t0>1@82C zWpgHNZMub^C?qT;Tk_0KD&!>i+}l#U1}&FpsUf|F7`N=dHHPJlo6r=D@Dj z_arn)eR}eLfl-b@rT>AH{ufsLE1>@;B>9{x{F`%mj-)%gGvk1&rSP%>lse7lzpHKd zXZ~{9R-K+)hkrbEj8@YdxH|EGBU4(7I_0xRj*9=&kzg&EptX0c=LFHJ(~KP1&ojtnMxH$Q=RBz%Tlq$wXpE}y8q?W4&Z`Jn zM{i=KLG;geq81m}Jv1T2OaO_i=9vM&T+9x(9~5p1K_GHTrF8XwX#i zOnaHxJlN(h{+JmMq;?WeyX>*L_vDA;T)^H+6Oi71rAqYnn?F2qbUWa(Cj6(qzZM#G zHB;TgI!-hO%9ZH;2g5%%8g|8_y~I3}cUC9vghI&q+eiL8@;6%tODt9L`b$^XFI&RlW_Sx318$Qq;2seea9CP91i6SjHwe%>0r4@RK9 zud6(W!34?`3e5gaGB=y6;cIJxRHVr;%)Tf?-==R=gH#I-yjR_q{D^ zQYZs0Mu|n=#Z!@TQot)KQ@)c_wL8PjEJ-x8OYy{wE@##nI(8E93PJlY#M)X2#E6D} zL=chk#{trdasQfd4MaOMe2lF%2H}NuU!Dtk$bARx)X?->|1! zuekPU`t{8#iOwiWwTj`9gS>xw_CO*KQ_9NZ-A_N3ClZ9!DsD{Hy#4(9=y~C=M^sGw zyi9>li3N7unV1Q;XELE>tf8EUXbW$(tw{=M-q}#?2hxLxozWkHnWY@)dN2&_JJs19T*uXLoB5---MHhO%Iv3A>$FpJ{}V`X%F8v*PdsxM@4=3PXkv^#bPf*IzS-hfzig=Wc*A$PxvTrVn9#jgHB=ax!WgrBpoU_QSrhyK zQcKk?r7p}-+_l`e6x68GniJ6zOxtsKzM+{YaA3Y6l`)&BPLe4*kD?<+@nXBJwclO$ zQI^3st7Ek#9iuzo((rSPZbNkBjrzF{8x&jCwyoT99guZc?i!Gt7y)No^)uJa7;}ae zSF?gKUJRNPh7q8`l0hzzS&SWOVkm+_C-xh%Vql=a@bwt~hP5LDY|{NX30e<`XELuV za6&$^R#*YVU5iAQ?X;k9B+^d1;Jsk&;v7O+(1 zSH+C0rR#-sl{M9?QiP$bAr=w+*&6IGN{bg6TeRZDKm}C4_GHMXb)h#${3Yt>fU<2O zV|s<4y7I}_Km93j$6t7@|I!W02xKxoZRoCK<|fL@gl>;!h;@xJD#BrH0UGzF997iY zr(IaS8~Yum-9)`>TE*TD;p*sD|V6<1-nOv;zAc}*&Ln(kmVSt)V?R*97QrKDyM}h z(hd__!{~t@%LNJT8z~c9*w~yV+V=xAhEPPF@*R}G;DW99h+|dumZXuJbs3HzU|CxE zNVVj|4OrjXzJ{q!M6v_nnKK+7%2WYIEC#4Bt_U6d)`ATc*q8IS8W|axUyB>P(UnZ7 z90R|IWZ}pq3wz+w!rR@3Q3i!{8tx^_!xipItHSX;D ze)~~x@c8UT>?P@uUDe#Un~2(ZV!T(fqnNZs0kpWIYs=(Z*$ZO7=~=CiTiCN<(`P%& z*7kOYs1>m>`Q+*KxH~mbbU4XX4$nk0twV1l{t1-MFRRV*Oa&Fz_}5ajsqk7I3ZB}O z84E5&wuOrUqUU1*!^>4EhS1S|H*J`M@v@saFLBgM6~kzUI(z2Kq=i}h8iW^F8Y|DA z^wSO*6!eLA+Dus@2uK=pDZD4x2!riSqLYhd@$+O?>W#OT|w+I*pNfgMja6asX1eJt*jWoV`f30no# z0|*T}T0eq+3%o25M=q^x=(BHQtbqbDW4xd~^^ud!(`yWpl6eBI%Ohy&bHV7^$QBv8 zYGxLYulZ;3^Gj8go5;}|q3s2X1v>&%#FcLzviV+hvMJwE0L$o?XAeA9!-D0tj+VNH$1ZcKem)>QXtGgE*5G%B6zymYV@ ztSrNywZh9<{&^qm{BBTXfzxX*S>Ak#yCqCmg7<;VE-w6)XD9&QZb?w4YLVW* zS#rxbAoRGL^)p>ox+4mYZ1aLSj%0$pzjn)8Q7T{#XqZ-0tcxtQ6VZ^pu}8PimDvbf zA8t=H%Cu6$`dS8`?q7UZD(0;$oG{_6Ycq>YD_o ziQ?)FQYp4ctssp*0*TDa6IajB*mm8n!z4s*uSyfzdmFZ1Um@G;YT2B*ya8UnoX_WM zp=|%%N9?w(qJL}P?>M;dI)m=&zG4+WeJcU48GYF=7x)7N@H+X}v;n0d>RdnmX-a=x z@)Cl6rl`&*WtvV$vOT)MyG-|EJiJG>)?(0oxuMg|$?$Lc7V(^*7EZsVZuzZ+@w-5) zgwF0~0vZ$%uyT{GXrRYj6Z@X7;hdb^vv@Heh?ve@YZ8gS8A4wOa`>3~A=cv;T@t<1 zwL=@U24wK*Z^a>;KLGct@ww;GOy|y#6|*p6Wfw5c)YhBoXb`?j-$+JDCBd!Y8({mH z%$aQ_0!0zuT7T^RP}3SEwcHo*tC>2*_iR7uXmv$jbkDW!i5z;`U_~TDdvIC3JuU1F ze|MUi))lqc6ql%cgZU?`v#mFQxh|GUQypdA0n)Gd(^gsCs9C?MfQM_7%|`DFI?L@b z3s#lWmq{yg8&2)zFiV^euwLAT5AG1pfeV=>KKZeTR}KbgoQLNy>2g_Leqfi}4E>^w z0=42CFZ2NW*V2|Vy<~+_qq_Lv5YgOXQ*34_)?_UtTr_E4hf`ZqZ$c3`Y;zY5)_kH9 z2Ow{?Q{7ZUwvI9e0pUe-Y=#w{T57pSLy_|cdHQAa4ulM1^Guqt1rV4^4rO;J>9V(X z=`zWdCC10)d6>r|?DQ!B!4~1OcM|YU7>=mU2WWZKr=FPDE~p(_Pr`^kK~+%GSksa= z)|pSjqmyp1RKr*dfMmesR-z6n2@HGnD#;*_BL;ubf?nZ200MYRGr1xoDJ)Li`e_s8-;!F zn;~VWxxKyG8H#6kHq3p{Tga1RFnw)kMOizFw0QUqf=Mj1PTXEi(C{U}A%BWD>>|zG zCSb(#pdE58C3Kz@Yql>e|0!cfQC6Lvt8CCD`;NargV2$MUyOrE<0??>Owy`U(h+FdT(D5 z9CqI#I(f*Xi2^Yz#sWsmhRIz&pC0mV_CZSdnv!H=e9le*U%hR7ltd+Ux!KkEf71L& zE?$xRO~`8Y4RLkjCKfO2oz`A2Fn{}`*ny4;bGiR%#o=xmWH4#uf*84mH`qh$a5kE` z<(nTo^@3~ApbFls_P3wM?yatvrcM-(NOsS0-O-_z`|_v%>-4cZ3$anj*+kiN#pTKW z-LJ&T&y5ihjk224!y%WT7B+Y7|1Lfp)xvh~St$Fix1R5v`Ek%gJ07@m0m|;$%AUm} z^oJ1G^^h*eVH6oO50T;g_J{|u@F%Lwu1WNi!5DI`L#3N8T8_d@QL9=e4_- z8wT&18&&P@3RtAwZ=Zd5XSMMf?nUX1qQU7)*@PWH@3&+FXYj{bbF0wqH9-TF{_YCG z`z?&Z&G->MH@*Q4kfglzt;Z>e2tFtt1qG~5A~PCZAAS-~r~ss3l6=85nS|Ro!O(n4 zd@Yor5ni_1H5SVhL}=DQo;=KjD0JT9<*q%aUs`t1qV%BO{n_1eT`3B(Dj<3Kqt40E z{E;%@rD1S0S>MC5X6{iTXJ122%`Bv2#cWfC9AkwtifO{R%z^Py#Gk&M+{J$4)>IAN zEEhF6x(F@|#s;-?$GP1Yf*6Rl2<4Z8(VYr%7Zlne!mrkIZ-n~w4_2{a?@+{Ae@firXS@Qm1RT&yJYA?@?YJDmLQ8 zojl&KdO>KBi6+fv*r$0R!O2|w)Luh$-aY#y8R9*bVubQE`&p_OI_RiH618< ztM^x(lOG)*9XGN(e!ZAEqW+`A!|N={$RBBASzwd-fcQ9W=V5+FUrlG{pKbQntzDVo)<2br~ZA*CW1O@=Fd3m;`&8lTPs6InC{FwvksSPC z@Jqb*%K69uk;HhIHrJj0nm%2|J=d#9JChcCR@jc=#Enm!=dBfej-SgD!SG0E z$nt*RCZtuIcG9)r%@4R`CCa*Ej1B219)2f0s%kU~&fg6U3wMbc$I`^hfzTSBNiUb} zyTGRzzwVC>#7lQMyBU{YB~l4_qfAAFULra{IK+Eng^xf+RT)a1AJED!QKDJ{ao0P1 zM$+s{cVac_A5YS@AU2bazT=P2jXsas=;i( z@Al_}tA2-~XYi&OJ58m_D+NpB9XP-hgSAe%Z_^`aIs2U zd6NfT4&hX*sNI5H)rnby0M`d%wi{W$0!GoW^_CS zwz!tpdv3Nj7;0*4wk#hoIhUyD!k;Y|-@!UB(Nc4pr=z5j$aa}_n-1Jb2t`!z3!3Q1E*!F05+t?BOz>VfUtF85n z6Z5{2K|~m)+U%*0Hh6%k#XcNPb1;b!*we=BM_y;UPeu=+Z3^IfRa#g5s_YwOJwEl{ zP*n*QmVSMA>LvW1YtyOLGSY|Y4(y8K*u~$jUZN?jp6|yBcTQ(DPV2u|s)A&&9+c;#5r!y3Ua_Y5S# z45JNt0H=|7_2;uRo^m&9;eXz#y)u{;tZ_{CyJphk75m|VT&nJp(P~=0UBvxB^MI94 z2dq-H$5fKR4mS&vUOeuP5*G`46Z;k3_sQxS*8onc{b4|!f}~dK*L7uq;|nj)4z~+O zAwNAKd!aW7t1`_Fgc2V2nh>Roj7RUHj_A|@SKLf}Q^Lz#da7izs^n7Ac!ZZOKI9UJ zZkSVU?KdQS5kJm=BmnNYKPP z)Lb?SwFNswyLD@GHjcinP{WtVZdDDGt^Cwkc!<=fyykA^D7j%H^V-zf_EAIlTc%{J4GH`K96hoLV1RLir@ zg?eX0zvd?@TX@gayFM_rS^c<;9yuX<*_RrUA$ZMb<4piK4IFarV&`_K*gu8FiAMsz zSl=t{>U+_sz_-W$s323YwFn2=7MV)$Jf9oO9}S7 zvt!9(uL(qo*|-+m-fZ(`e7wfrBqvl)0`c(hB&JVQ@bm8)p>1QO?<Sb6W_-%{zhE$E-lDS8*6E+*csrFm$@xZ`* z$koQWQ=_Q`>YTT&CCIwDxRoP2T8)R?LMd9UTNb)zXK69lqy>0#zxZdzz%77%2Ws&t z26D!0*8pxbXo+)*t8=j5h>6;bcO!;x9UqPa(aOVbr!pU?EA?f`y!a4VopzI!oI|gy z4upAEzjjk-R zq&DQ~xq+#a<$pbr?zv~z*ellG%l%sP*fN_hDV<0EL@vLrW4dnsLv8=)d%8b6_S!GF zK5WtM*Gc-<`D;g{xZKhfrtoNjTV$B!-K6;|xGv+i-+;PF(hykBsJOPjXks_@Z3mJl zT^m=H*y}FLu~7W;w~Inl=G`*3MHKr!o}j#mFkhfXBV@BVI~%IlL!@S~3vA9;py2c3V0Qkq1i?mr z_reFwM_ymuJMCdtUVv1S4MB7bdxVGzj4Man?);`NlICet#v@y+aUQQ^p|cOTenj1=Un4uPE0h`P^Tz_U_a}-m`t2`t^6# zw}t~o=O?DOc1#$Ez0ft2N0M$I^aCeucU{)|FoJMLJeD+cCIyUGo|17q#?i^87CQN% z%WReT4cC_^a2;cgQ>!98O8Lafz9fMY{+(0!GZzvsvr+7NjwK!n{Stq*%w!S$s5opW zT03{}Q{6k{&s^iy6>ZzkRS04>~rD(RtdJPwzA_CQhC{L#Bq$j5Yk!MAtL98VO=jX1uXGpaXs zwQs8NHfrLhQ)0rl)Kd<=oX@wVzoflBj&qGIRJXbxo7D-#H06Cvlh~Y`IMnCjbL4g2 zyQ$*kN=+il(L$q$NnFVQIx%IoZXy8JKJgtJJJ1_%gU>& z?UilE)p%dyggB2brC+^8NO$=mqvKsw4;J#kLq7(|6(tG-IK3y<=k?Ro&(F14FPTk< z45(NOKHMlxTe3Dc61kV79fmity*fm^n7RV}xBBtqkDlg%pAjChJEdtK5be3=8_Rfc zZpQK>Teo0+KeewVNx!$w36=)R-23pabwiktt)lCe)z-^#QYUY}h^)Qts8_Cp-i{St zj?PsNA8w1Y?@V|rI>0~m>-|b`MxpSpr`%5e)}rLzPW)gP@Kyh2`jErHH(03k!E~!3 zc?*2jV=?hpuEvbBNs>G#k0@jITJ19Yyse(JWPEun!Ib3e`R#Xe?A2$-EdlXDF6&31 zXm`)Ki9oE~j-0VbX~>QLLHw$4Ib!Chgc(w+u1~^#;D85s#G!SyJB7<)PMvqOa!9T= z@ijKJ{)WtNtx1Dm-%#1@{{u)sx4%?oC~~40Z1b4~ja2JChtQx>v3mJ6Rr^zJg>~R-Xq04Kp>?Cnd9OGC-Nkm}wpt6S?t<5f3#c zCR2Gx7dC3tQ7W|!O$)6FHy%)yG%Oxdjkw=!&ReV`GZ;g~l*69Z8N`8B7|s2~QfTw*m4-rkjRostGi0N9AI^ zLlh#pOmf(3qK$v5y2_<}TNqPOztZ%K$aKr^m)ki1y>vLkGzT=z%Re1tY;a$SQK&>Q z5<%on=HkK@+P8laVLxyo?cBAC#s+zL?C)qF-2VA&DUmxnW|zeBWVs@ZMJ3J&8ALPG>NQ&E|XZjOS1QSKTlOR;5#&k%DHqF_pE)WN8-%Gf~?i zWuKPUXIQ?=@7guHK9M=^+3MG_TC$@7nJAqU#F|}~TsX16V#8+T+ zL7!vGBPf|nvnZYRNj3|fLK`#P&)(<7H5xg{NcxK0`%Hz&NoGt~pIKGP#!}fx%NH#z zohWPdt*gjBh^f(5PB*rjxW^`2TT}2bw{Df*I!axpZPl1{pp` zvPiw8Vddq(YY1S3p}Z|gDT8z^quEfR;f#vul!{mC*}L-;&Jzzh8y%6LQ{{KtyVC}< zk|trd1tZzys0Os62;C<1A#ZWktGB&;s=J-qP8zl${o!?J8fHFRk^z;p{YG}=I^2;j zolI?hykAcyyG~@9R2cHR&b*`$@L5z_rH#{;3uSi$&tqfBE_X9(Y0w$u@E|k7 zK2Wp{dWEVdi_mV1X4vv6bE`9_3~Vm z!d2%xg$^Z!!3>cbacax28?a`T*mOH)Yk;ly6dm@yK8qn=XhkERlV4^wS8Y~`8BrT8 z4Y;?#yhokYcIlv8WutoP>qC1hV_g%yMC}p_*IgZbhowUm6x#W26x=2DkO>R9p+>MJhM6y+k_ z(3sRgO&(k*<>Go-%0vUFv{C=qKUm(#`W9^yA!p@F+YFgKKr(DsQ71YX1eHATUMW($ z65A!2^I}1o2dmCH~bfG%$Bz=#py$?=8Y9%aXbZ5tBT0*; z3DOnkv#Sn&aquLfZp%7DJJP6fk&)cfXUTh#8jYI}$+KfFM6;JV{+6pQl{PA~2ewzG zw~ezN;F8l|7D63nAAqUsYX0kN0tM%}E_3ed##!BHD+!q3$NIssbd$&IY7bDV88%z?9Z+a|3(X|jLX zLDUO|x5Nk3OLcB;@L+47+LqmMuz_u{4@;zWLObN9ZDk5hjXbMYS!Icw^>PY|m#gYk zd)!*z90#dwFO)YtM}N9A983^-x!hjJH@PvH_PGrds5&2AaMUz99oxZB&;67UFJFy7 zsU)bW7?G~&o|~_lZdulpDRllM_A}<8{;Nl_`Mu5Baf&jDSytyDXtZbF2WUy6#9grc zl6GB&(ff_TQC8hD`Dd-)(B^f}5o~8u=8zOwU!SR+l?W|7*i)UQ(3ybPMye`xp+%?A z_PjsQ^~bS~)L>QAdCNs{3S%#pyiZCL+MMPgSsPkdSghA|Ri9HFlv1({g{oKDzO7v* zUm~M31N@mD^{BH<>iARa7Za5wE+#k4N9e?BWWPSQ(+$;9Ccf5>c$KRYwaZON+EMVU zGDYj;wxoRC>$0E}T1oVULaqa)^d)4krrxQ9E0e zQG;M@6q>|_Xx-`_OLds7Utm%6R*G7ez1q6$dO~E&!ix^{aU*C7^40(JX16S8B+pex zo=h&H_kAW`Iz<#`e{*OldW5K3AGfcuFJG+FfPF~JmpZ88>J*bV7cq;yZ_e~csXic6 z&^+k`>i2rqa0w`Mc_R;<8cmOWwDz(* zCvE_Z_chLPN=?P`G~H6@n9PuhEHqlH(^(X+C9fkJDQ5EYrIGd8fp#7hCvro!G|5Bk z0J7<5I1)<~x0(x?p$OHg2D>(BJqtS2n+ucTj79yfIv;jwgxqM;T)8Q+KU*K*;tfif z+{mjpCYg0dB}LO4nk11&m0VUW-XO`7CZaZ~oWd`qwDzFfEGz3nhcwVgiqWT-sVaV5 z&`Xq3reV}RH{E61S(q_M6pp|nYR!i~Fr{{%Lo1Fp5>d)W!`UvEv$pL$6DR$WOE0B8 zd-qNZw>TPOZEXrSz}i;C8>hN~O$~BTk3_Ct*^`-aQd5WwQ3_|C4{O$Km}jY#mwsGz zSvm@7NA*!do0fcyWOiw&Ll>Py&7bL$={C2$e##N0&Xn1O=ry^o+WY{ zOD-z4D`p2x&|cdqwA04PvUIZb0d7Zf(;x7FCOVvnlJC2b>+Ap%jc8fj<;GT05M8yw zz1aotxAc7&5-DElt(~}&BswmegVl+uD~iH1MOf^tsm#=Y>cj!GjgMgyYNooRX{k)@ zC(omv<6rs&sfaLW@dF>kAx?v6Zwe=s5Q#2?2)mTQk8Gq;MqP{BI{P87qE|~2F;_>t zm$YE2xiO zs_ls#_*CCT!+%$YzDA_^-4OhHFH2L{-{|Bdl+r9@wLli%$M&6*hKo0|=kGPtgeq4^+pX4*au?I_*)Cd7a+y(PKODVr#>)ikBLP!>^Qs z>%(S)+VKx|`bxPp4Rc^D;h^mPSWCmBFWMNgRq|491zE+{4sh%_Ws_YB=i7TZYQxMf zel_wzIdub!%GDg_P)z@st)ZF3Bz=qaMW;RdOxZ{Isti7OAZlA6`3s;@3W-suL$U_$ zb*VB{dHQGZHtQHDC}X%ZBvPN)v^4UL)z!KslQ~zOTx;y3L8RP9RlR>!Q=VdWg7(UT zqE6`mMvIkwaHZGi-N`JlO!rB~a(e^CmJ(%9e&m4!^57E5gWK%nI!GhPO;J2rx1}A* zvN209@!L*!lqE6y&)@Gaw@Lb}dZzzur(I;!G#!;~{K(5`^-=t5H3=OC0EU z=DD}SXj|k*=axyWjGuFzWgn{4bsiaH1=UukzVH41)}Xu0%X9j{iB=(Fc4|>dO<=%Z z=CR=IzW4Usv<0=rY9sY}$#{IHLzHD_OD%^wCvz&vXD;i$BC)+%R%`P*nDEr=VBekL zxS7G7j65Y@{lnB=oJ@0kOU9GPt@cxDx6Bz#6zrz=C9%!K@|u$@M#x@RM{YCfGujRL zUjIQJs-{c5Iq)hMEmV%bTbuOAtsYURSxuq~ej-zlm*`F(qJ$*vxI9U8M*OR=9$xGe zxgGQ_LnEkNu=`rZI#A{%8=4PZbMCW9i8N4FbT91hift$B(l~Cvx@W?wCE3Y5byMi% zi_#8)V_7>L%ZJLmE-{nmlG~CLj&v;dSjbqHi?}Uulbe04X8G17Hw&$QL{ZH%c@QF% zsJzx^JPj$!PknxL8@)})l-vbM>lGJ=!^F)-$c=_B)2c3 zry9BU?lCuglr0I#v3=?DYq@*bXrJ^M**!ti8*7&>Y6GHo-pqu0=U)3os=jITe|^Jz zEO!=}Ca*8;A(F^UPQ|MBxZ1sPJUfS5`@T7#KSiTZ^) zcnSAd$K2?YKTi#n$&*^gzs*;bU=F&}WY6|dq$X!H>gcA%a1Wl~y4+OSu&qW79YOo( ztQ}BW7JW7wSAJpA;~xJwdivjbdf9yHwq2i_ytB_C{prUIJ{%e$??juAwY*Jx4(%uy z=@<#q2RC(YRz&JZE_Yj%sR_DCgUG>Y&rOzF`$v`)H~2ZX`mMjSqo5s0oPzeUf6As59x#q8o|6_s-KC@u95@~%_d$&zsAkBZ{DlU}2{vDW*=!?U0a~y*g z!UuMp)jl}7TBAeFvY}4jP#WsrswVs3C3#)w zGL%>d3j&7EZL_Bi7D79&&N#a)5@ql7c+V<>T9)ecc(Y2>%(O|LYVAe)m5JkSPFD`I zg(e!WCtC)ZT$Zg(N_JK{N8d#%?e`RPLiA1L$NOE>l-3z3YW;vvj*!jvd&+ITQZm~! zWks^<0cWRErO7$8YpEZp&uE}+z%$whd6`2;C~e4JIu)`_QQzz%%8hNc!9tO9UpH)~ zuF)_3tpx1!pp)VpCpTxR7cr+P>SWs#bUdcFv@ZZ8dq(l~;>VJAJ>t{yBJxn}vQ;f+ zBdead$?-Z@aW9q%2xn|r{p8#l{g|1Q1|wZH1Ty+GL52=V8g`rx)lmq4Y3GzDy>5_( zJlsdt)cOmeAlg}Iq@j-PVnr1-q-2p4F%sIThO3%}Gc0GwQ~7_=wSGROYWbq7sLc_J z$!?`F)fdweil?WPk~J<(EBGkZ(S)o}bN28+zDeY((^1xN_1VdC zS^uWw=i`z@RVf9dtcFZw<)y8k>zh^Gh^hTi9k5PF@3xJ;)}>i$LpirHcPUwUIA8Jt zqu6j&XHzg}_IGj#R5|A+-(=rsxtBs)2+{tKm1fzMy%K|((-m~UpoTO$a*dB*8)RI| zy;6D6{BoX^qsp`B#-fGRjvhbbk6!qmw16B>f~yEe`5@&|@}C!r?VyJaMKMGTEDI zscrYsLb{I;+D4Y1B-MK1_sZ?>Te{gu=sg?5`gl3Z*|<9x-wAP9xj@y@dBPhJF;oA3cu-(W%fwLx%!Wa)3x z0e23wN80M#>en_z6oY+cN69O%9I!#u`Z^)=*_4;uUWg6yB|dtMlyf2ZJ0HTP=&IuN zZ}%fb))pkl;Tjc8Xd{-r8UR@Iigd+`$LE<{aNphvmO_J$N8h-MMpSnu4S5tgp#D6J=ZJDVEr6jV2hlE+AvMcb+s^XEujz4Irp zb845WXeZU@!87%TDYzPA?SPV9AD!%ws;F9w7UZxVJo`%xQVL~q1vN)C7?&K%NK>YO z;KB=N{{4 znjcvXV1sUb@e9R)x<`8bb1Q|}{u~#mlI|_?I&AB_xa_swY%O}V=QRqR$yH32T zau{gGjx@;296PD!>hIUKr_PhVS0kLuUmxhUIg=lOnSAiCvj$!s9~6|&iv8EOQ-;Az z&bBv0pID2Lg52EU^0(5GwMAVnq&7yY(`Ra2b~>oGAe!j`g~3ZU`DU+icallhEiaHS zJ4n=63)QDY?p#{i_cd_dFZ$h4|4p@@g9|MW=M8q?+1Vj)iW)WNCvvEwd;| z(J9TL$gntk3j@71d1li^qOecJ1#N;Y*Y+VeSxZSVh(yu9qHuzWC{IqEX;yu3CV#g; zYlCcNC@$rcyu}{X0<~r4V-Nte?E6X#%Vu`JM+19F>eZvq6y!i2_~=r&m%N+sVw7by zdOl5U+nrrkm%aosB()8}U&|Ca1Fg@Po+M_OO!isw#q8c5WcDugRvc-EweB@g(1lIM zjbEGG5-gg*4{J`nO0-bHbPT?9{~{5EqZz&Rwa%Yrrhbskxkw{jf;t>2Lp zWCGR{;%F-r#_t;zX0R?x3z*Hag#gGR|blI6d7(%MkUb&Y6qNXP$<#F`%n*zRzL z#ygOdXqucyp3sP``K)R96pUpW+#3cRQ;D>fPvxuTKWN8z#@jqL4t;W`o#NnTEgu$z1ko||J-HMI_L zthf+7DcxExGj=JNI)_d0$CJ#5^PdB{A-cqRR95gLBIVPms z{30J~#8|&cp_6Hc{6s|Knwf`9*ix_Gw1b4Q5tS&^3AvoIcjzh}Z4Me7%gN6Q7m<<8 zz6LqeNy2QoaOXLcU;47xQBxdTCclm&xC;`gWcqj%VGg<{`CpO^zgAy*G;^%0!G_ev zQEwzkl!G3(&m-$kG(ge3qMkfh*4QJP5hT+M&4UeJ%(w77s-v583YpF&U{H>J&#UdQ0d@9b)$>Hu*pZVw?!TJUiLL;TUDK)w#3%5ZRgTwd(rNC*+`lD(7IIEXe8htco_&6`3hB z+i$i->dUNeX!5qlPG#qYSLEj3#m&jqw~#y2M{{OrZ|O zb2#})Mly?Y(Ix1;uzin09Q5OPkUgl)gzvva1OkX@}!8~Rf7(*ZNRDnum=?i@H z{?3hYx!%^O)rM-%mJYWImR*g4rk|2K??kaOZH(&;Ayi82YvP;qawwxdDkU|F+L_kx z>*kYOPQpkk%w#IqHVEx_QW|tVDmSu57?m##%0+2<&_k9_nR@3m!b_9dD0yfE@pg5} zIwGI8iSb1FUzDTz4Gx@o%ZJ}I_$v;nN_}J;erPhQ?%NyyS zB-9Q)(o`zuXf+#9S|h1-9t}z?b_xc95}D4VP_BVco7}X|@;nrs(5XT@iGWCV_BS0< z-_x{&NH!t+vaM=gL%SQ4aY&5vE^b|<{rmRQMVDMk+qP}1r#0kIetT{hc-vDFwKD>K zVh$x587xiJz!Yk4qYaRcf-xrVC_^%OLe((kXcNm&T?03Ldu3}2+eT-R+)kI9Wy`Qe zO5Ny(j!$f5*R^e;e&5xoZPpD^nKbgs^!Az<9sBt zK0r5J)z4C>kKlZz@V9L+le#GxyUItTpk|CEpAyTr@JiW6SUar$uq7m=2(_l8KO8_Ur#tmAwO!P$xMuawuQU!8T;?XGH31N6c{!Z6iq6+HVfCNWL6K+eGqv!{AJNS8Hn& z@>8-SepVGml}GkE2x=tGt*^`T0dJ=G{t6BJlon%0&}pfu8El%r*pxYxD21}9jC96E z)1bQaSWo>)dR^m~w*!?f_$W0`!QU*WFLmY*lzI3h`l?R6oF>F}XF(5nktKSe2>b%3 z6xRB96tyItQq#e-GRXd4ZNx$!r*LD-OfJwmh2{mfk&)_$8A&~zK@}uZ6O{-;iZm)} zmOZ(Gkq9q3)Km?HPTTl(V@YpHFl(UpwTq)U37SjbiUZ`B1Io!4Ws`p|H6kdwVy ztdYj!kyIHp3J^Q%Rv_8xCz(-<1=U6n*4O6N;A%e7z(KE~%;Bn7AEmb@8^mZDK@O?U zgR0byicHQFo=-%LGZsFzpJ^m7^}gIRI?YYUFj<5s5%~nP=DKYVQ%<`&{*;5P;=+oI^<@cVRa~dh04NgQYKRj@AgCvN$M_yQ6fC`U`i{UzEytE@aao9dTw(74K@D!{fe-pJA_~iJ%0O|c)-`V$ zH93W6b~3QD(?ycER~>w3txt0Mq#9NGgih*3oLy^N$@13u+jry*uN}oVtvAL#xgOW& zO9jbOlgQjpPa@Saa%VONyBW$5^ezW&A@crCuT@{fdK;Z3ANI%Dm;O;Gy89gdlB}ta zT*#vpJlJYIaWDrtleJ$Gxn$3yidpxybUBQpGq3kFIs55CzTd@85=y~Nk(>mzkW5Zy zw@NxXxaxhWm*{+HD^|6w4dTxhqp@@m$ty(v&RQ|@++Hfb%3DgL#zz}K&Sr?3&0Aqzp}nEz& zFy$R)GGlLxvvU|RTYlAWdql_@Y4V6lGdQyb5uR^7Wv(*;aeZm1glzTV9iU6a8kKrJbg6Rs{s>geUPAvsPWb5<7@qNwp zO}e?sZ!Bvx!ns-BNTF_W=y2RX>wuJ(#9VaoMYLn*PJ3FGYilPvn2oD9Q|$Oc&L~t! zxF%YoXz=y(P`5-LDWSIHZ5@%NAJwh)E5XLe1<@IzU<17Ns@;nDE9$Fj6wSDj>sNek z)@X`9#v_(-Lsc$Fhg3xgHjpGNy3!WA^%$vPdx1+g;N9sm-R)-)1?DLz_|Ora6?O ziP(z*mgATEpx4=hL|LDm!SOf1D@tdB zAn#)|s1|K!41$`2tJhS=Lh40zINOGH9k>l=G*ahBYd3vfY98LS{Spng<9AiR>-~1h zj?6si4E|E?$xmq^3X>re9aR;rF5A(4kHzN;4sxyQLT$2K|!?rdqN)NZ^o*-{`=E9XSi zwg{~pqBsa~lN>r%lXqvvRqyl7Pt~nC3-TDON>I)>vOm_ z=Gi+Vk`ACw|AoYeTy8lOOrj;MYrA&srYAh{3AA(PdE%JlMn0xC_UvS3Y9los`|~G@ zC}LXK2asf^KY7X|y52sV>^gtC%UU~}l50K1g}lzyCb`X!--9geIhtyY4m0_zmt8>S zjMkECYPmhRL(;FwTM0`>^wTax+loGiD2>^ZzIM(j*phmR!azIAjk%F|pS?fOb@iou z>e%S1305#)N>QZh4aZJju;bHmrZlDLp{+GClcgTAy-Yhqzh2h-FD*58$6sg8`CTAC zDJJH+p7>%>bA7hF!IW~Sv_^egqcq4uW+SN4zfRA}rx5uk@;%m}leNAs*o*2Q`Z9r< zyvR@ohJA02TdxDHR5z|##4il=`#u;D3VowE*D1@o@N$-UGQU>sZzl57M6wS0rEdBV z6j6JVDD-)Hs)%R7>|l18QVKqS^fi6Mc8Jo64ilh3U!!LbDUx*w&;zu%F@>3x^vF!LJGl7DNn8Dc1cR6*jz)~ zOJ07Hmn=8a0c@Nx?WO;&$ChS_evS=@_}v9dzAw_>wn}E>^rWmsH<&V_Q{$M_K@*|N znz(rIyfs=Abh2q$D?kczP(`x4$<27`2F9Pr1`*^9#Yc)7HS|2IQ7dUgg*Jn)(FR{L zWQr(Ge~ds;^ZE)J-H5r^rR+Q;4|R=b=>wV`Po16uu8#jD2l=pE)9-?$p$k-*PN9nO>#X$&r`K zybG1T-agPVEkaC28)0J{C^d#}%DHajMl>kuR3G2oZ>1s{`8=m#oAXraMadrG`KP8% zXu8OQ+$3#2WNmEf!rE@58RR_eI`2HX;KB=O&;I)Iq$NGoq^4xqP;C!SBMJuIAC~n| z-b|7;`jF4*JMCGbG}@ZX@LMj(jA~Q}&ymh@gmi;GxD)C5S8Z*_HfmTUbv-@7Y_s2K zvr^eRJkv{gY*|wsPk9K1cL&vtdA+6&`k(;`l|Xp^pbZKnUqKza=197luGt2Yp{HGr zp0#~CSuZ3rL)ONmZH2SfQYc$)%EfiiQ#?UP1&~9BsCW00UTgw3h#A;PPs-Pvl0GWc z@)S8pw5yLV@CnZ)a$BJTweqG*&#L9gXICYvPF|0cJ&+?=pte!EW7fQplzdNwrpw=V zdC+eK=PNV@@(OZ$s`g$7kscYY1FmjifflxGqpjO^(Bjsu<+`PbgK^@qiEg$MTeK_OUF1( ze#ua_{>tmsqqiZfV55)D<jzHRu~9M|B$ihzhbGMhqrdYo5uI@P$Oq)RA* zbkyLgRSkx)8YzGCnrA=x0K?~!yvL&)j9WDX<@MRU^3tSB!kQ@O`T!u zk$*j(I<lqqifqoX{G9TINJ^ElhhnTFBbNiY+v(hOR>!IbyvH{Z4Rz1tXSx;t{Q*t}2Qg=Js*|N0yZMF^1 zDh}(IgZ-+r`r4gQ;{!C!Pl}obHhk!WXZudoC7Y8&;)q34uQt}~QPvQ z9)m`HOUy$z_sV_Q-xlr5cH*9=zSSFc#JUjSbHO!@G7Z*5SBc8;d0ndmK~%M7rHuSp zXMI4nzAN{=>!dl8zNuGoKt7=HsJ8k&YWHlwBO&9Rd-l`Ly%*52M;@jlhmX)B-~3kT zFLv)gpDup@in!jbO{))kknUQo79?9QhQKN@bR) zZ6o0{o*KAvqnCqD)xInh$6f2N&)MvTS?Cl@fG(|)fk9cWl%u_p<+6R>ypy5mP)D|; zhfJk?5J!@}FQdukyt|D~_L=QP^JXrC5MPGk^)7vjsEzll9y*|KgfbDCKc{U!`Dm)% zV@p+iSxViox!^i3gI;?huGFE)G7-g~XV!egb}Xe&BYlJ&eH-*Q1}P;MQuew#QI|OR ztW!P8GuMVLOOjk(p66^UD9j#_52mydw2h?Dw^$u#e+zEC-UpcGh?6XTTB;jRmxBJ+ z{w&KWkG0HpQkvM5lFmQ+pT9G!5zXFS8bx*SP}50y4@!+7qhScDCCU>!MprJY0P@bq z4N1I~C_2X45twk~vq7$2nl2_0fh!sE4Ujp+F-ns=0qmS zf+MeQXtsvdM_ISVA!Ne;iW&6}$=^0suWmcVXTBATf%tDqgerjf_AGb6D;roBlc zgW>*W@Dx5y&BNPbhQGOfwr$@*7hie_?PrHQ`4nd}^F@tHO|^gP41#WEvncxgRH41N zO-dSF@ei$ztuso4N*^TM`+ZXnrPctegP_lkZAE|B(sa9NJY6qDvNG$XISf41S+1Xx z?NOmTh|5TO*1=pCUe)SOUl;6hG6^=t-nmIQs+iq{Ier<5|cdF|69v0oh2oU zY&A>z_?xaLKRG#tym;MtJq{XR&QqUh#Lw5eRxd(zOM_|I9EBW?Axvg7{n^Q!Jx;(J zcYVI3tNQ{t0$uB@Jghh!MHeRy>#qHm(9wq;rn|rNMSA4nhf5lD8O>tRn?3vX)0NM< znzn7*E}Lv8_fGWY;fIb)qVXpt5&CW9{jAh<=-;AEw@Buo4x1)zGXRVA?WDHmI_OaH z0a($b^3tPDP8eEhbwW`GzaBy4OX#|~6sx#jx~C%1ps!1H04T>nhOCEr*6in|zqY}I z+|XHMzdol%?z^e7RW;F#*WHxqDLSJzQ5s;T=nAAk1~qLWznF@AvuyZ{PJEGv)obxu zB)PBzsj=WeEb8=XJ=71yKDZ?)7E5PEQtTn6y)`yEEa~ZzEm!X3+;p>{(ZE_K60)fN zM<~CJjXWi)=UBEkwdXQC^xE-VoM91nRXD3BNC0P zUXxn|^vX#T7R*xi*Df@Y@Wzvx&e1xsEJ{auhP&s-8ffQ`Q$6TIdh+X1?=dzT8Rww;Vqnyk=><{G6UC|s<31GxD1MVedrs0Sv$^1;n}B2M$|)ol zJIR#C`oWN=s56#Y_#jj0wdyC00~Pd`G$NeL!HpfA+k_D1SH(_E?pzw7!%i`m!5C+A z&gz2H=8dw|x(B_`QzLTN6z3gAXx@@nkEmJ1l}v1;M@DvX%%HX_&7*3tb~Y-lI=i~i zI!dPd?rf6YXNEd?9}?`Fy+BO+0R7JFGX*1(Xliz6isW`KldXxhBmAs3b^j(IRruOkAQ|{K1%m@Vg z5C#qYA|~quyxEila;rR)Ey8OL&^E50^=_$Uu1HHu8eFSc@!E)%&;o1bXL^}@M$k3C zice<)YCe5?rT7)p4*KO%KJQ3^|V#D1#m;(ob4dC;uP97*{WTgR2miPvdAYY;@X!&IR%Cj7m!>?gXYk- z*(?TyNadH!NS5MKJ8-HDIxfv;=$!Q2;A*3iHBWa!-8r%bn7zr@r$r}$EMGP6*^c?j zq=tc3gy~!IXSw;0iCBfo9X$LjRIL3;)W_5$rmm9FwvePF z84R|mt=l&JP)Cv^cVb(&Y%6a&z2Kq)w79q>v`?}O$I}dLfMPsL2e6)pW;!4!K@J(z0a3L7OG|G5;%p;XyXHT9V_O!( zYsn7krgmDsk<9W(WNU@1U96=W+9iuN2M38H9gF(wciO?}AVBjFmeFY07dqdE5NAD7 zind1NW$Eb*M;LV23}_DhrpR6NtcX+Bt*R!sYH!PE)W+vvBI?nFJpX{urH@IPjq%g+4X3H45#S{~Ol#TKT#K?<=Lg)gdkp zPw&o6FS1>b$axL!_(>@>n_=LWf@$*>uWI*QpP-J~lRK3VBq$>uKQhO8RPXe0DlS z!-svK`c6{TJV;6Prx(Y#>Oy#lQS2i`b}EbF-{?!GDEJPkeO_#X(Z<^wr9+0S|0ME_ zro4h=IZa!5pD%f$qh`?!-gOJ1T}8)jLg&+DNjW^q285WpQ}Yk zi9Cu%ZMyd4Y!%m%{5mtlzR^LKy=aD1sm&;)IFk1fT?%=R@-Txg2E&viXkavSM4`dU z@1Ay^O4I5sth4h9B5Oo#R4xrFnykL{nKovQ$0z0}Uw?emuSBW&+nsMQhTei{icGB` zisc)A*WNaj2mP&ahf+Ox!o(Z$k%Hd14u(2OVdEhM+mUnAxT(p0id{haUGn;@VhMHb z*>5Cmq7k?YniTS(8l=G!0m*0<=`BahJb?K{fym@-(i zwCjdY-r%}a&)aM3A-f!-?4vE!B_sBF_~dfa*&Z!B|5smN6WgWAvph;Swg&ytx?7Nw zK`8dGqVxWOsSuRX+$TTUuda)CUDEvd67wW4_L3u;<)w}~)6nUxfXPFb>g13oAINK4 zuWR4k0(!m&L51n+H`)0`uAez;%Ovy1bU98oT??DJ8yhHpOLVDA$!3F!L#lEU#jn-l z2X2&pG!5DXkvqovNH-_8v@=l`E|xN;M6o;Vf(@ z)WoPPx&kNJF`tQI0MvpyxwVnemt3Sk+o4g@;Pv}h;n7%`VIDeqW-xNRRfHWg+gwf_ zDX2=}4cQy3tq)HcP0B>g1}(Btnc7h^@^XC@NNOrSB4*akiOu2utTNHYT&^RJPEaz1 zE5+$tzTRI%1ECF*${NGoj!vvfPz^?Ej9QUx-$vhuoesG@ug_mdp#uw-l1`b0l819n zyWubu)9EmTxblCOsloto1suAW;pGp57%FZDdoX$ru$99sz0`F-B!+G-#2mA zx9{9h%d7?@xL1~33J(Znh{mSgV~s-ES1?F+ueMRL_9wrZD`XFrqtdb*kQ;*wX1Mco zup#9!=_E6HHqa#dxVfJi31qiV!A_Z%NZy&UbDi(Jv-I8`*6gr^XJTpd;q78}mrnNKDA*s_jaDHNE zpM%iL*~*eXE4$5_WZwYE|FIe2rdAu7`m-eOT^Ba!l6TZ%v&-UzFiGQ1lMe9PHm7K~B>H~b>%q$I z`wyaD!M}9L+ml7GN%fXU&uOoF80xYZvo{if_l?}i+FNS}L>{&TwjB`l7Wi|W_r5XM z0O^n2E+xyeK0}XORb4aZ)aoxc{*Hd;*^I`*>s&h>SN+yjdQp?Yq|kXq`m5`!VW@F*2PQR-j?;kA zanU?yG19SIIs(p7I205!`O#R(a!uAyJ+fFgY9!CHE!&CFl4h-(J||IdGCkkXG51bN zx|I|=333OfcNu5$ksIIFNh4{}4Dz+EMin(UA6>H?$0KzrTWb_e<4@rZ^7IE~ufO-9 zBB^zZ^!|bVkd#s7UFgXzp(NzbrzU^iFNh?hNHtr@_Zoz!S^o5H-N=-EWIVjb^j#_| z>TC7{y$spgLByj{j&6Q8eH&a69cDGS-05C8=~SQcw5QRfkAEC(-?5|3bt5n}Qr^i- zZ(9ilGJK`k&DN1*lZYm-$MER~Q63?AGwhl@HR`IT%`GWp+iY64K{};0r3-dz^1NI9 zBu~_c-b??3OmCyeL9iKSyb^qLndVM)Je`15S zZKo|;ceM4?C138KQckLiWYPU5DoSC7QUYa5GE3xx;@FPkFWGfKM-55uQ1wgG{Z4kf znyg;+^q$hfNOPG&N7naVPqo3yDJ^|&XJ&KuGr!fo@pn1<(WjZLZ}&*A+EjmL+jPyq zc>fY~)2~!}oTYtbYL2`nZypr$tj`cM;Fzr(y>IxHf9hOoJ4w`8JFGJ{HP0ICEPDv8 zcya%awgn;zbyCM{OHyB*_i4dKbYU>3^;tK=g!XPej>^aToa9o|thoKR_3<*0nB=9G zWrdu5o`q6Bk@te=ECYoBTMqi@dRp~zu@QQ9C!iE8g${!B^Z_%UO_gWT3}(vmF64SV zQg4osI`KZ@^EGb~&A$&eq)BK;m|TI>=-ZXrKs=>tETcitoF|VJ>vKqrUv@>&wU$9Q zYsJYrC5@I^=WMOZIVYxMq4U6twL;y>40=P}`e@1aioKk`m?4 zI~dJg7GcB#P*j&GZpf03oy-AuS=n&6(Ni)xr{*LV|DCeej@5~(j-4l5?`k89^;vQA zwK))(nq?vNQkO~0wHCt46E@?yJqz|GwUIMVpD6A%pN{cU(-~OD5_B$2HLB2F8{KrB zXq>xNKqP&AUSX_(%rqi%7F z6wHF;XUXku`A$aboh^x|a38BL=Zg-bIu3DpXbuw6_0>9q~rdEnAA>KKAI*ssq{U z!tI^gZTAO!Fc^=>t%K5RYvRocWe+r3*N}qZ*Yhe}r?a=c>Qr|otR#I&eEE`@5cys+ zYk5z}b=vGb`Nq<$n>>>n)o{Jf%1m;p^(C7uz3(FH!%5npX<4wHB`=)}My33&DM2Ap zP>Z1csac{_FVHpdHP7UBvLnhl&PMn-?3};)hTg7e6Ipbg;)zY#o~`0iYoF!*1&Qp7 z@XbG2NDE#!#Rhau5!qo*rUx~jb&Qp3gnYq$gnEBAeaKp0(xJK7{E!m{3UIrhI*FUcqX@O4*!wT^F4wHhj@1azoz9 z+uL&E@HV}!?W8tkwy#oX0Iuz6`>tjg|FE#el)XpH}`PK?x9&}ov;>1lnD8v|4LOkuR^(o@Hv4lb%}b?sgsoUq!d zbVHGmsbRB;MmsupLZTdXould3h(lj=pli?X&-OIWd~h$Ns^h4Vn*8eDdVS6zMVTry z=Z!*yFjc)xgTh<#a!pFXnnwGnHDn4wGtrKu*&bnydZ^nWsWg&w()6+!?$jn%HzK7r zLmGQACrqwohkgJ4^XdEx&!?T|omX|FMDb`kL&JnawVNr$M%4a7(+ILtX`50?bf^j@B&$|J9r$cTiGO>7dZPmgy}C|V z0~9HoqNQ@?U*&XX`CQ**Y7$JzmuaXR%s~@dn|#pkZx2o8z^5r0N|e-WSTY&uW>uX5 zkr;V%@$dd`wsjB_mg|r&#qcGYwNj(go(A@6%hv65=)psD;`s4;p9qql z|G?2l=+KdeN(BB1mmZ*PTXl_iZC5$~D~_B-swe-y=SyFvV^4SjU2xGwlL%pwP1+@c z6X$*FM0~sGu}6;jKq9$g8T07Dz|`7!Zvo|*@vY@Lz>T)Fu?G@_)X2J>JX7?_ohOsM zya+1|+I{R}a;Ss)k}|S^tQN|bf&>RIS-jV0>0{sO#+|gEi+#5k$+o{GGRL{h5%qm@ zq@nl0$=kcg%9HhHs*Tp*T^%e%vyjZ0)pr3^n@ZMplMJL5$ik@PfiN2QHu^~SA1$QT zo_f;wT`FghRQWKTzGk{c-jBwBj~&;oVf-vha?Lb)y~UTHnb71~4JIxuE{be=>!bR( z#42ljc;waXJo(@+Zs48#TeL)VL=Y<1BR%rk>CP)Dc9h=X)rb0voHE7!$~v^{Mxp(T z!dh+VtZkal9j*90lavY09RodLFled#np&mF&1&fuH7KG)-e|KG%tx0s8ICUTREU0{ z1K*ln^4;^_foFa2Dyhk;|KBWNz!(&xf>DBLIx}iwntXFB%q;E;owVUzr zgRIKqt1fb+>yk?^okZBTw>CM&cGDE3EmwSiwqJ_IR*Z9SBHb>K=(FDL!?kwMM~$Jf z8ckjf({Zr}viwx)D@MP)6mAZZIr&xqeWr8_*>g_AnHLvLZbg!GpiV*8+sdf7#g13? z%{xsovE)utl!X_>k00wo#xr>fiuDpcNj~Uy5!6nil}jdd68$APq1;*Jlqn7pZ24bq z+fqFSqW2=GUCHEoDb#k52SwDT=-^b_K^Hd66PZaiS(ma)y2TNbG!v0^kU9&jc`}=! z`6IHlDcU9bPioq&rya^euAAE6;;A;Z^X}>lL+398(*DHIQx6!4#2NXt8Ml01+J zMEx3-s-m-!y0pXFdZP38T|i&{(p@GSwTTZNeU$F`%Wqh{@I6;N@e(O(Ze&%QvtFa0 zJsmrCobJ8r?#c5O>A=MolMaMbU$>mMw*(TFjy*y#$dw0c|J>OnE4!Xlru|9TINOVX z5E)wkqU|x&ITKyBq-fL*G?`SM*)7!LCCg(vIiV_t_bKRLHTmoi z1-;OZ^o2#Sok{I^-G+XpzEN5ImYP2G)W|JKVe1kXJ-NhB*)e%`(T)(sKG0@8T8l@~ z*YSa3amhtb$#h=#b9Dpzm$Um87<IXS3|Za@qSsPSoEtOy$T^X^hrJ z%gIKvX|;F)ovkArb6~|F2BYPL2nI9|qwn_6oSV*_5(NW9;Tb*Y+43r=_`2lMknDBT zZf^VFOs8PrYh~s^zTJ3KeaV z#NUM0k_UgK4!621?O*H5_PvDb$V8xqE9zI&LN~^Gyq|W2_4XW+l>kJkbJaWPxFkXptIBkO zuV2z7YTl5t7BIVlYa6kza<=6|YL`7fS?cwW{fxcyvPtxrRCV$tpB<+Pv*PHs#&(p}DU7qi@QrxVh{b4d& zqWS9M@sOmt?2SFCJ&8_8a(n505BUaA-AhANKfE%M=nI+sQri&wqvCM)xv?utOTHAX zlx1PD)YH+!56X!+l}AsUpuf8RLAR@uf8Y4l5qkL1V?xZlUWV^J=FSc;CW9nP^fQTPZW~DXom)cwOy*uSv zIh{!Bw-0rr?Y-1&?(JlxV$b@%(L+7irZ-=!7rWED&gR<{61}BPTK6mK6v@~{} zXCm@QVK63kDu&MjcupuF_WpGql)TAU3ZLiptI%G(+*Io# z`VqPuz_OII%_lQDny7X}+73Kwr$#Z;RAvTQEJB@yhVdygIl{s3+?&m z&11WE@1aW`cL|+$-cISP{ltZ4Fxto?L}^vugKU%Ax27-_IU<^J+?ukP16&i!)=^kX za))xD8(JqsS~t{m1lB)LqG2>Ps%Iku%c=$_qWzMzQk(U5hJj)tu1n}#ZqMgIOL4;O< zo}wRqQc)V9!l0^(fZyViq!{B9g*>l=`*tFZrfHO&Fud807CYoJ{dPx3^ zQkLLE=uOG`*g7~U%VZ88nhXd}NTSsN|JDN!(IdxBSl`Jh^^X4Hd#+3JL> zf>n2Q_QixmQHWw!>jHaoj%i@)>ZEsl=}tWhJ~jPiwrvq%X>2Cb4STlE*7u>E^9Q}# z{M59OJzsYvN!#qN+F4)Itp8*ax9uzGyX5XA-d+N`d!xFXW!$Qb(zmX zT>{Qa!OOmmGV?O9aWacTVn@3q5%SKDo$UJ)#4q~dGU+Uu?rlt+Z_3xYElZG>PEZUO zbZ(~W#VUXr9bTX~9jNs))mN*4$DEUJks39<-(T0AyfesN(H05K{uqmr5jCAhHh~Ym z%ehIHLgfTY>YYul2ju$$`6?X|g*UC5(ny(lohj%|(e1f%tu8}SgOT;#&?>ZZ%@R3J z@i>a0>^ehX4cPu=o7yfBE1q)EO+7gbBx*{*S)kyo5H-K$<_E zXiH2@;@-*nLmT<1(G0na`HSD{73whRtW1;vS@hOiSSf?1In3>}j_3HCOpTFKhi)u7 z>3Q?#s)1=b=R9pZv$U6;6G#2B%P*&cR~#(s*!>UyZ4;9FflM3yj8;0kykg;^?)Xj9 z^mDb_{ENe&%rl3&!=yB^B3I=3;onZ*irP&*X1Kn198L{MCbwWmOJA~N^$=eeRBj7X=5gR? zC;DiAq?^#Au1bT5FRt6NbsHUi@IjG7?JJHywnPseeYk97*9c)RZ*Dw%Sg_v*P=R!v5&Xo^NM^WnqLzz zUF!S3(a~nDw+$T0_tMC->#0AyL(v0ns=(gn$!7U1?|Mz&lP<$myVbZz>dX_#Okb}W zN5ep7V+ch*lZ-Uf%BusYb#CR7`}=6a^)vBX)YfRBqS^CR5IT4Y`cLZMhR8mJnKQK= zskLox{<4)L&fX`pF>`dSHWN+3*w7nx+Ai9MrK zD5?KheZ*|Gi*=>Gub`9tJW`)oZAs@kR|eyCRS7%mlBA)V&y#G9E;DUi=O@Wc{d)#kGztsRckS9htpSZ9Cz&?YE%HU>vP3{VKdli_^R5auvf+S@MvS=F(g}(RWOeO% zpXw!vni&LbEb$KtW?l_)=i0F#QSuynAfjekq=peBZ=D;^vt; zhm);yYAe>VoP)~|3_>5AMM^OlI(9bADVKriI9c>;a zK{T*=o>ZE8(_Nc^mK4(%w0EtQm_$uBxA(>;Sd++R;Pu<7x(R*U5Vu*`HrlIGRQ@~E zeKF2$$DK(xW7PJkr7FgUOg~GVq~gqJr%gHRN1|}fwB@Y}oo_D704!povXgwpS8QA7 zrkT~YrOvBH%E{&Fr-_jjQth)fZ~u>-GoSr|RGkHBK-P4Ub!t^iZWo8Tu5}cT*W1k+62EI@Vo@lIg}+n?l!z7oiXhpOHLYKKyFigGQ#aId>lABN{9n z@eO7)d;?^BnUCv$-&5;yd-$-kRB>-h*J7+-^1YG(}68?RUM*Bk&=SsA$suYFpHHn;TiF z&2FFP_N?>+K_(Vvkduz|gsGVCPVGT*Ro2-)wWE?HUj{90(d}&S$3=$0mO9hahxb!N z*^nHdvQP`rr)=+OFxwScull{#H`wVn$#j4Nq?u$8OXT}B%sI$*{AZH=kGUTlFw)nw z)bFkByvnG&dqa;Fn*6<}QD0R$Grn3$!PuMpw1An+sSmxT{FNKU9d;NV0hVd&O!hXe zG-xPFTy~-v$|{^p){sdm+#3T~W2@DXEIn&rFCLgu77NHY4YU3qIwiGi(8&rSO9pgT8QlFIv*;9F1BTxRBdKNmkvf4`Cnd`M4 z{YyT${ukeit3hHCow+Lq)ilGDwo4`s{7G6#Qd54vumPSYJNm# zQVJ5Et_QuPeVLEW7@CkME?FU)6^Jhr3Yw>9>z7Vb@{?Sqq`8yjfi^>HwqDcE1O2ab zb_`-7d$qj|ES74AEl(p|oM5k#Z|c&6TfH11=MI+WM%F%0t3zfydwC-*#nw>o)8i*i z_Pv~)k|f$Dzt=~lpj6qayw+V^r&|YLz1ahPzMf5Q12HX|OzAIf-9nE|9PvjVew6gZ zL45n6NmTvB(vrzln+)fJKY#4R3EH~2)%1_w5y79C{p&TmxzX{XhYr(Sciu@)zVb?G z(&AuGoOfQbbDW>~*fF=MvEK=8i|joE*$kXT=#$r_DUqqLN7pS1E)mX7oItIEa{5Wi zp{vNY{L74TbhroY##)#eg0>$ur%&rsT5lKXHe0h8={GZlQ@(Oz2;GS%YNP#LreyXj zyWNN*M>!5uk5$%wzxe$`Q+1^Ni~Cc4Ok;G5Vy|GE%9FPdw?`slvx8c$P?liJ<6c0w z1k%=IDU4pN196gfK{RDasj(;iOdSv;BHK?&zHg8!lJ(DsS@5>r3)- zyp*W7tn^_PBarQ!`$TS1wBTwmT-ij_UP*GVz1V}A+6)eEX9aiJgbqU<-^ETygrOB! zmzs1$v4<6dOp;$Ofs`n@E_a~=^M<5GupKOD`IGkm_Dt<8Q~}ErV(*S=(`d{nCzDy3 zNmyF4-|bvau>rbyq0fG7P^Ms?iZs)pj=c2oN0dh_lXhUKsV~p3IcAhvQ>2}6a*eU+ zGn2YB>=g9h>bqt5S>xR2U^)h+}!3vjbOY+moABs2}2vo zQDSLY_lA$M+*!NvT=^hZWl?ov+qP|V*}==`@++>O{TEzNJDRA+G*%9Zir}ATJ*7yM zC3$Bq_hgzj{y7*vvhV$0FtH{Z)v+io)8!O&G7az4tjRKurXZ>L+d72gdDmhQMQ3-k zy4yjo-WT<&$$zvHIbPXhO?cab|`jmwr5ox7-}ti z4nd5V3rTt)oY*0{bEGA-B@*ggRRqHQRN4NikTqHAU?>TjLB=G-;Q`+Dh5#4hVer@BPJ&WTsqB#r)i z+wZ!VSFsH~(rfC#gJZhBe2Kh1#hGxgL5_LUzO>4D%eL)wbP{dX4aNGLhaNpnQ7<~V zU!%9>3~XHnqyvZKgIhKHv5(Y!T@QTYe){Hp_lYR9zUs+Y{DLOypUL|?&4<67DA=T^ zOQPG+cDYtbn%ei;k_5Md`qHVYN73=mW+%;_CGy3_(VhyO%sjYFkrp^;W7?M0Ny}D+ zsqw#J5F8RDqM8R%IJF>E>-)UV= ztchsra$|gAP|qA*`=@QJYnDC5K#S(X{j8{$WJ}3>$zfO;qj~Gxe~V?TCN4GhqiC=$ zJ5r%&<#(<(W^YP5;gmQzGPu`SmfCP?n^xUP-sda^u~`9hYAc=(tJcZR7Eiv!C0oVv z@|J8Jgja%|cS&YP%Cx6#(ljeCM#$TMI$|14FsVp>dKr~yMAIV^^|~C4t2Q`m?B%DZ zq4j%>L_BY$>~;=Nyre30Gx3nDQK*3_2CfTPBO;#)){fnkP=B}4csP@}H1bXj75SRM zn6=b&>qIH&q!^*>zgAhP!H{TC+~lhFmA`V3Z6Y_;Ql=o9kbjqDGXWTkG__S{3T$R8 zC)=4FZKOylw0q5wHu4sCDr&k>?v`F|Z)l8yItS5_((+_easQdalxfIu>m?<-UN?Pl z^89vFevNop5)-ENiQ3J#{yaCp+KtAJ!XBCJKc455TBl4)mD^`T;iQo7AX19IR+UQ4 zX$5{?o^}?gt#cm|r0F~aeJSNoT~nhUN@8yojO`>h+8D}3Hhr8%@>j}WYcqX-LW4Ti zPkeSrWgSz>vPmtsUz)AVtux;oHJ4ooIkcw~^65!csiy@?x2aXtJGDt_3&c3;+13`V zEc#M3w%SHrQrYB!h5r5h6S@Ym$@AEZFV&L!j@|S4ezCGpTG?qb@0k0FDMDqI+$IXfH zC^?xEY61#Pno>9TJJ~8Gv)7`(W=o|`%8R@fQe%5_k~i>(u!iqdu{WP484M@22Q@2t z0JzW6x4|;2^PVgir*HGL$bL@lbW*VKazwX%mR%-gNi^wTx*M@^;zL+prnZ5|U#dvf z@A+D5X)`2|;qZdT6xX2#dl~&5wVY4mOLn8X6?DbIUbD{j??|87QN3!bD zHBQtH5b0{&_A(o$x?9S~e}~hcI*;1A98;)+W?Ilnm4czLDk^_>(Fz7Z#E7&R962H< zR-{&LJA(2l*(6FFckFA!CROlc-5E74IS=|YxmTU%AyLVF&>)RC$3~pv1ML|l(jWsM z&qi>L)ELviQYV*%DVIci<1TIQJloucHtZddHlR$?3Yuf&f~1mTGIdV zya`mSCwN4WRkiDYzAK|SDRK#Dz^w9^9JgwJmwf#T>32k_Y3TJ$L&*y}WXCc~3YP<1 z!ks@;2N2Kl5z?w0bVia#YcorZXGhg(RT4U`QcVKk?s<_7b=K08;bV(n|ClmHrnSwz zJYfI2^URD|#D+GYrTUq*C5#Q5w%gyLxwdr4rHm^? zIR^DlBv}C4*uyk{$GCgyD10B z{`uauTXznT_uJ!Sa@xJ&{4!gI)k%`1Nk~GghrG-bo```~^EX!ElgdvzKxk3ndNE~? zWi)?p(SQO zwhi>2QeTYSr_q~@4+4`Jmo;TI-dRh7YKns;STe$Q$dfzl9)WZD+c!9oJ>y4z8`$i|@NyvN!?f*-4I1B;Yh3SIIZHNE87zibm~ zgHDp1LaxXSG^aKXN|NiG42oN*1+O>mASX)%dv3 zvpJFxp`40M;8A+?^}|(V4cJVX9YR7yy6R1#f^>5Jr_=1@TpRub3nh2c5qO-Op5Wt8 zKE`K1`)oMuHC-p*l5zr3GVlFhrQTViPf!DC~hw#dvKINk#&0eooT|xmm8l_+*Z`GV~viAdwRiad$ZIbAxSN~Losl#bR zT-BNMpBGJ<#;M~pb_S7I7djMfU&YUAtX$0oge(W$*=}OiluJ}*&H2mU&VQ1 z-_vPXnEAtiwuaLRAcCO|ujy!Z;L3j2GlQnwv=-CXbWZ53(s4>hQ-HPdu?I<(l@C4LV7JgQQ8deaWmP0|&T_0~T3-E(=6LsV&;jFa)%7mcZ^1`(l=} zwVQ3*`m-I&PNdq}@aFt&mfbMTG~c#K(h3fmHY%=YgNDG0G5An%rwr7E`y@EOhfHA|FS>kU6*+VK#7*4w88`&wmy`tae2~X&@sudsPZIz zQzxp+W&6x}ieM9*GGS?lo_v_t<>Kh4Bdde3c##{;*LAHfuIjX|h41DKlM=^JK3JY7 zAa!xmsS93hf7k|1dCAX`oi!FS`xEU+Y~f8qKHv18wy?N>+V4R5L1HlN!_dX_H9h)P z_C)^{$}iX8@u5OY4aG&mi}F^K=5^ zl1!a+vnEW&(T~X}D};5CbV{r6R}WE|V7d&aWTYod;F}Krh)Uqp^)laP6%E^$fHaDUjc2tH z#j;jzeo5nk0%f*S`&;f9?-%Xoz)O+OV6BxQPl*ud&IO;ey}X%#CE!wjby5^x2%M9K`1r1gu{yqoEBiLu82AI_ZB{6CvtK)^C8L5ks%O@dQUtVE^4uDe zOZJ)lA~$!baRSTrf+5by@>6T<^|$`7Ryb zVVoCnlo38WqoP?h`?|Dpqa#Rt+|ht|kO$3(JX$DF;$=)NPgz*ta*z&Bu4%`pvt@B_ zIJ0t46c^+Yn^XxjyVj(GASrcfEsWthYBJ0jV2EPo=GB_{BZV$6E|Vscb}Ja=-2Prh zq&82%h0r7EkG8C^^q9**4o>>PY2TK`%xU_Wa_-F*9gJ)z!J+D3ux+bjY)E~34l07n zC~kJwyWZqyK7VoH`0YRaGyd{l-JMQ4Ya7WHu8pYsd6Yjh>p6Hh5|?dl`K>Rv%xoKG z#_}XCiL28m(FRGSxU;3}Hu;bqjvXS$G++ z$~tWmye1(>Mv=^FRPx7xBE@SAih@Q+T$wkyq*%ZvJDQo85?e|}vwd`$SM^Ca(439o zU@_$_bk%edcr*BDIiG5#q?SB+TD+J+F~P!o56P#LA(+q}lV7;3O{p%MMw1p=x5u)& zi`3uEdFDWF`fl@g*-jVQ77mYI*j)0~^SmQZTwQ%dn@w?D4!zOk60`%`W2b;*N|Ab- zLGVJnnZbm4ephl0dJHZ>cmJu#>CPc-pXAt%s3oX0c6ld&iZDZipor0&ej&6=y=jcw zdgqtMxKjVk8_pFKCxN&R7o1DcawzmH``2Msl5y=thYw67Q3x-=Qk4U*&dy?nl8I{_Jl)mBZf!@IH5GLD++ zUV{gbr{tfZ=qjX1n!9wycqd|7CBij0WBNTI!pyc|VN_{ox;}X5bZ(yI09f=mS?acv zEz0xu>8T;h>1Ds8&k~(X`4Bdl9b;H>OnBN);Pytu3bb?#ojN1VEA@p9jb~bO!H_bivD_l#dCnvDZfVyVei|0;vU!fI=Qifl{o zJQQWOt4tBO&o)su72Vb$k#agMB>v&EtuKG|;(44tYIAAI7J)wu8s>EU33P@)&mmba zR~ZmU`&IreW(%YZbx7Xi>9eQL@zocf(QxS7;a{?+F832{FGy&^NQk^^BEj3DN00RrLn!{nea(G4bD~h7y zsQ=Pv$>xtKQyQJEauyTer(%Fg#bTqK(FQtSI?Xhr6~!PfE#bO*_Gk4%qXKoqe$c`8 zDKKnLaCdq2Yqrml>7*MP=y8Nuo(`D9p`kew@u)`DtlhKZ z3#@pveSh!a`}pijR^8J_~ar_g=f}U!R2+ecGF9socBHB5%V&y z)pEIf54-oXNtCsaLcx-$<05CW7i{90H-~smvBvsR(T_Bau&$d{(OY62TLPiW#RyN61c3km1B!D^{?O)mVHL^zJI%J4b3OJXG zMb2oEnvN?$kF-C)(O{tLre)hU$N2S&7vl(ZE(>^jIX9Os<7oBy-(rGwd{!@0(t zv|)mcom~4KZmjs4g5VQxOMlT!h=c>Au=>?c5-piR>R$qJmeX*S&Sku}rp^jE;>8R+ z;P|69q6Kn;r)$GEq+MKcL%Q5!bHz@svC4h?jsZ;tAPmiJy4Wzqxx^GSnzeqCB&LD8 zi8D2z2pl0!$MbFANvvI#JR@QFT_?3XY?$dK@NOY)R_3X>H2e}RU}_e7JH!7h#mI6! z+B;7no*A^bhlu)iiX1&Us@>azj-w>aH2<$o%f<&g!!L&XGrV2wC&Kks)Qe@-{rJO@JB_Hs1NW|ppPI7#1z@V7%nl1}4 zKn_Q0q)A%Lcf{F>hH^^98u%+fvn;v`2BBOv(sol#u2iyoo^;0^2zDAn`lEr5=c7Sx zF?KHfw(Orag@eFKd%62~x*{?1^lWQ;Xa`?4+;gEz4L-Gu9P$j&=}v9a3HOX3G=s(d z#*zX$dMBrc2>W~QHg0Vi=f z_x8$(F%icEP7WHlQLM^O>7DI?x=zdxB0EXKSdJ_d%-Fz(E%*U0*Y}*Z=4@yh%qymK zxN&sQi|>bg(E;kSs1=IS?n4?Hw&!xsA~;v z9Znw{99^^}$B?o`O>!1jWIU|xtbHmTpi5}8b~Jl6co5ea&E-HnZJn3RV>FlaD$_Ni z>lqP_85(OiRld?9KAkRCL)Kpg%!D3xVhIwu+>Sn_2Q4kvhc7Z%GT|$l?TUwIpo1BS zD1VW;g*H|Zw9IW_ujU~EV=Fdg=h6!Y&2oeL=wld~vB}-%L1gP1G|uI7lV9VICE8{= zsA}aPb9ALrjpyJW6T?nTKv3M^`QnM8(x%pPX+TKG>}7c@&2U>8^wF?ZPzN_$>P{-~Vg&Ew|^$4A|p@w<2L z9v*%4(ZONAk$W7nPU~%(<$H6`yMv-chbXtS%}Pd&jfSPOr@cMqigc~WLk-#Atva&_ zMYnXS|5RO9fO0xNR{U3a>*+2d>%{@MUc7vXXV0GF&A~Z&`t$|9`u6cSSY}(7yOnh9 z^bof=!19~7^FW!+q2J2fcyo4`0l9f_Y;IgX!HvWB>u0Bj?`ODs=N9hVy^CA7Z{g_Q_=>O59K$E59kV~VYH0~F0bVFGS zzjsUk9oS{J?o*rT>9CH$0s5eSF44D{QaBt2b$)8Y7?1}gWpgNzHr5%lFX`H4s=zhD zBkLZ7a};07Au;xc-PoHEqqD&oTLW?fpb3o}HS1gaB|}MLwx{uF7Ad%0cT@@Kr~QrL3dxizpo#lN|Ik(1k4}FmvSVkvg4#H$gT>RZ$ls;kF}QI;9~f zGdcnQr66Q}R1Y0(RvVXQsB{EO1Ci)s1p-sR1UTbQ*n;t6wrPRFHYf~htbt6!QV*2l z8Z?gb=ku!wQuWl36-!^D&7!!vO=%_I&}$2}>FiD811y zp*`eN)DG>%5%+EoTw!eeXlFS=>{H|51e{%Yn!rFH)fTED%7B_3Ac zQrd-{uQCYT3l*)}6?&UxTNzHPGeN+IEgH2QC;j)o{3ra~FMluY9Gv!N`0DG& zLw+=hzkTY^H%6hN%Juc(_wy-p>x|%DJ4D-Won6B_cdp~zJ2&y}-P?Hg!F{~{@O|99 z_wLA1Jslns?3@kV4_Y91_t(|fd)`;a4&V&pVs$#9?@Jflg~;5JPOIp|j+Mg+S$l9{ z8YxFE=p7`l7nh}GCcS!{;Am8m)-GgQYKCD+`6|qgKU^7;GTh6bF7w>5x#sPVz0~)~ zpP7rgFr_x|c{(1jXMi&-`t@sQ!t&?c$Ovaxw%>IlBo#bF1ZlkP|MlOe%G|Nc+ z%HBkF3r8+hS#!zc5)Mg3hjS1~UY4$9^wFn%+cv;nUdHCT;NzHe^2y_bSlVuuxBY({ zUNdPmU4qg<@~xd@8Exe3V=XzIpXbk?;p?xy#HT;_K@Zp+qU|TwZyxme;Hn(jN&w2W z@}^i}P4>y>hG`)K^g+g1GJcV?nkhj;+G&S^rVa%U2bTnAmY(o!)}Wc{@i;Bhtm&va zzEiZ8lsU@jl56W@ZFfAq?l9J)z|o%}YhE}X06q`QVn#d32qd(cLC3<5)hiWZ^D~yh zrBLFzF_&!#o3i2NDjB;nsy<{wqbMp)j?%l$B_wM53 zNAKa|Pd~x^E$6;^x*<~i}Qc(WX0JErVY2j#`l+VHcSgw$HptGq( z(cxgM5@Cd8&Scvu9e;94cgc(vQMWTv76BPKBqt5CF*+7~ht**uUJk8BfTzK&$R6pb z>3a1@k+}hLN$jNKol$ZCbyy9=;q%B*j^<3WeTl1P3cJt^x?3imk-we}5;v^{uQO0V zXDtR}BU6enp0s9083uHg*sEpBiRh%FbVa=h51T{v_U%mj$m7~K9&U#(t&}YluGDE$)L385AF*k+@aa3p<)0oB+TdHHL$<9`x??>?Yp6 zd=dkmzM&g}c^+vHbMTh~$bB9?A3 z?{?5V&vHS@cl@FDt}ApUx{HzYDzLpdiM7Ze z047;l#FX&_);SG3uUo=7#GxD91*43!TQ!=UCWd)`HG4>zUzVXwPUqcg9eWCN;#ov4 zQsd$uA$0Yq-kr~RRBPo1jC%xClQz~T2T&-$kjm?wok7-e8i?R~J`^%B5cOU~-V`*- zte_0CL2$T75tENzeenj|uA~B#PAL9lAH6-idtNIp>Q+mrtf9Ce65IEC@7%+qM<0&m z6uHUCtJei4vkfr|h8-bWzj-+iohZ_Rs+3PlGmj0wp?u@sQBCLeCqL34SLW?LsyTZK zQn0hBzLN+6;9hYb#?cx4bJG>8aInC(YNit zOAq%x^z8bzQ@nHQ3}@GtL!|HxUc5NR(`T>n`0*2b{P9Ql=m#I;#;v>Iq#8IUeE7+M^WELF0LV91N6xt01Za)jIYjyta4aMn+*s(rDHY&YrTE$63sWoU_(VzmDh(dPOsto&6}zd zb8xyXmGK=-q7hX_$bv2Ev`^m6u9s|{3@0{tBQ)bz%la|a+kf-bH~8q2k2Ax}xO;Jd z)9W{I@%s6$9ab`ePS=`IoPsoimE}l}$AVAv1-3_-iq}w)V+*3uu_R^_U^5cWK}b0I zJbR_%mFR>8_OF)sXC!wnvzZZF?WP|aC{!>|WR&Dcnu%!`O&58L&&kOWSb*tUoJY_3;1aEj~L$v9;omh5djWnOzg_ZeCS0JtZU zuty#YUS$f<@C|gJCGwp8Q3#6@Ql}oG^}WMe^i*8V%$$|HIQ%)Cn^aGa7~zU?!mJ>Y z#rJ$JrjTlSuts_o(H~9?jPkg)h>kW)QnO8GxpMB>FlwtDpuvu~v=Jr%<_nsCMu2pn zCCv|oqZ7!fmf4nyaMiN~dcr?iivvv=CH0I~*Jc6AKcTfe+i?N{efBly*pDeUqXj$0)17d-D6uxkeu zO-ef&pb%X+ZRpjeHub7#2g-rBvD=nP?e5*Xc>nzec<11_*OZ&*o{ln9RcM0w&Xd?X z87^(mHNDgmU)TO15g~!j(m1IxZ^XEo@Ho1WbThs1C*HcMP&<0I?$GJ7>@)xPt}WZQ z?;bzJ?|=6P{NeL&@YUDfrM%5qSeb!Q9cuu}r2ur+!+lsX#hZAr|l^9x)*I1?W| zd}l17*&>zSJbsQBFMo}1zx@_J`pFOR@S~5$X?jNEj*?()|6cArGMuo{g(kvcLku5<1kefG0OB+v= zB0&#sgbfC`$>4hYQ_lPBuBYxQ8d~HD_jI5S`njx~mN78n^!>7M5C^m%iWeHCt%?3C z&Q_Zc*(0hS1cY${X_P-Vr>(i9q+t;0XJ~;4wI}nkC2wmGiq!jg-N&ZjC$FA^OF_2# zTLk`MUEA`7gqh&YwnT=9D2<6QiX>(yshUTrw$L&z24-bQ1B)<=3 z#`2YuYX|53%}X$v3xPTW4ON#EqLPQ0GOKAdIcQzN$zm3f#=!h)k&ZNkAQW=QlN{DG z>S23C$hkBxJ667e?Xn%dpE^#(0B`OWGiye%AjQ9%a59^wAWd$r@LtNuOr zdc(@FT-DeDp8#DzqQ5=eEoI%+;yR$s?mAVG$ZZ+Izx?3~{QeJL;>)ie&ytKbl}_IZ z1wJ(}Gx|0S!Z;iy7Dk^emg|Yx)%pB%YTmpBzIyx$-#mGJnA$hId;2Cn{oo$%UcYvT z!av8~{mn1%{Mj@7@TWgH^lK?uVt00f`zaRUknZ@DGlWUUqNPU1Td5N05Fx=H^Vo(q z=-(CtSqb1?{)sKC@QM-Qx^~3?(LL=+hA)lcx@{Jj#6>D8rK>=M=M{Mdhh) zNfK$336{nTO%CqOWDM-Erl%!Wn~%#SBh#*}B7+5#>DVUCgU6e*7wP02b#D!x&Oq3b zd$~Mp50_8;Y5O5Evi*;P`@{7uhx?n?uX6ykU4J{)ysyU^Y{=z|)0vqAuaj-vc`Sdm zu(SKkyYu;rD=?&A;jgDpo*eY%Id0#%O~W?|yLkHsr#EhmzmW3=vaLdk#E&62N&`$M z&jlMI_iGJ4B1NY-m{cJb`>(lb=iyHi^IN`m)hMe5>J7*)0};@j~)*SA9kKDq3c z{gsR>mPSl$kC&`;lN1L*VTtlX7lu5dbU^ndH2kh$#F68ZLSwmX#HD7^HZ!e6Nx4MZ zYCx0z3xM*5d2QC4=_}c?)bDMhaYWj!4it;#Cr?~vu`TgrGP{C0EN01UTWu*bspD1F zz=oyvRV8~+nXlRbX(-q$h_ZChE z>DFf8R4-lBT~uRUO+K?H5M`_uD>e8~>OcQUW1B9d7*{$0c0{{7@4PeCWZphF?k6#w zmb>Zu;}ENh&gkON&Y!@Jcz%dZzIbzv7q8yp&BcnZpS-}gPhaBUyLa%z5AWgn z;ju4%_b<47dyXF*ocGhyvvSTr?xE9I`mHDF&HmGn?8Mx5v$8!)BZ7b+60`!=fSe&DF`bnq{mWMaes3r$LW3ZfHV; z90Wz%%g|m1Sr+J08&%~DKw(A{iAN^1<1&i2wmFTg+M}vF4HEHw{9XD$1$}6hm`|H6 zl;J@z0bvnYo)O{Unjh|;pT7}!m?xQ5#BZndA$zl|lY=~;#AZiJ9i82>TFMwLHq?C= zd9*!Nhpc`1`RDlYXFo;z9A;^jgAScuJHth^yZBJLZc5)_mr*hr<Xzd|_+6rk|sBcx;ZnjB!Omp_ML< z`WC?<&p{X4f>tG+cP8sl1d_kxhgF+hmcqq_jwO69%6DOv>iOgo@y~MBZQfsHnbEZ2 zi50fZj;hX*kCP{J-w9GS*oLvGHP(|y4(^gSXNp`V{3zQp2SV&Cps7bGA1M`?Ri@JJ zLPYC?Y0-zRiam_634SZ3~>!YnC&C=?-qDX3iA<(60Kc~vSC!zV^zR`1~)SPHEUQmP+Qm|U^7 zI*eLF2J}sQmbK+bICr5J0qOnfGUe2)ouzw~b7U-SW^G>JZD5Wx}9?#KYfMIzk7zOLKsOI6f2lR6sFXoFprO{ z^~w&Bw2`I5a6uk|(tK-Odr|?>{NNVqougW#UJHqi+r#J~0z{|*UW((+zG%tgJ2%?7 zi&GYLCH_Q@QeV5S*KAfGBb}n)an{RSaM8cQ$N8I^OxcGcGNIa2>vctgc6KKnWW|V} z-S*tY+lzyvegWpo-X7b2uL8D5vo*1iXDsD9nWsFPbsCwp*^8R3zY|+4y4vi)fhS*o z{nc15bMxj+q-@x>Iqw1|XEzRQKMy_>Ew?%s$y8%(kV~KkmhzjndWkj2d^~Zd3j;mr(FTm(H-Si6mc@B z>QUve(TY;ChXTYv$wxL;u6A=H#i?^6&PG0~UZu!lGkQY9GJ;*P zcQ2K}-EA}(z(#)gaWufMVpeLT(8Klit|0+Ptxf2+wE(pNby;6TfLDA`qlub&bfHml zMRdNA1uM(q*{|T` zHBk0YY~R(u=U|Kys+RT#9ap@(zvH-~FIRm0zjFUdOqeaR{M+Rs!53_T0lW6|opGSj z!P)rc+wTPUirb4QL_!K|XMB5|<<3y)BE`9k6(=?JwN=}XNSoBmJi@W_>1QFR0OQD zE!1;p0^Vj%4CR(&-wZoYC<+?szsT|iFU84>M1da=UG&1`W;a4c!DGpVTe)GAGqDh{ z-^rRuzuAu*z)71FW1B}L&S=VwSAZ(BP_Qdeen$Cx{^KbxXwDX+erhYJr-O9v;#h=H z9tA{%n%&d(L-7?-_5$S}Gs?7fQ7-nM9d^^XIX%eW>sPO!r zWN2k!-;O~cc{9r#RNTdxEgET-A4ADXwrka_Z4!MAJy9l-p8KpL^SzL>Xl{2IWFRg@ zI(qs2QysA#e)hY_0jJKCnJA&{oa`*X1b^3@{7vw$^1JiVDvuJ{_M;&l7X2~Hp?`kH(z(GW8N>whhz)Dt#`N`r$5vKu zJR9jZhd+9V@Y+>{AqYfey z@mmTmGJiQlk&uRbH>H8|jnj^3a8r1GI=t}OL&zV1-zZ2qg)ovr0>4udka{oAF)o_8<$d-Z);KU*Og4o49lR0{3p7;a~skBmDZ{y#MaYCu804 z&p)}3Z@&0)3^IQFXMcv%gFMlAsFv#b$kf-J9#~kRx!&F-O)uLF`L>%~0h*B*e=HZW zz;|!+<7=JX3RF}tH`?u-4lqq#RxJF?L0KVzp2C<-4SivxUJa&##rCx}#Hy+(s83_( z7Wvq{>sPS9(H|kwTm8(mf$VROvSw7wW1}zY1b@#DGn-_Yq=CC|tQbA9mZ@uP=)xlH zY|dB?+>>i!Gba*|zT4@|&!0XAC1yq zK03O1d09c?z;^rbH{ar;Pkw;*nHeT6538c> z<~wt=Udx$@5P|cguQj~%B=_iRbMTfZ*iL@oHk5b>F zzHdJkjhJ-sMK&s^!T+ZLrQ+xGS~6&UW=bdTbPdHI2(YWoNfjgPDewet7qBY=rhuC= z-Lk%F2cx7&4)A}~2ZgjA$!qRlNd5L_p+GabV1zp|5or`k_?0k7!;^wdVpnej2+^;T zVml=?lY2$r?7pK0P5HkxW(9V1*lEzTegz75mog!sld9c$&4R3oDx*u)wls!}cG*$A zYbs@PL4>FTy(?;;G>ISld;R7uJo@lMeD<@S;k}3N9UuM8ht+A{_rZ#9E{+H3l$J*< zm`0gAsK9n|(;A;oN-b9i2C9vo?`ao&_uv)pe$N~GL34IYe^b9KiJ!8Q6f^7wtR^&x8``SYkSP;WScgHbmQ^p4&k+7Z)?K zbpG}dpMUr4@ZT#ud3A8&U%$luXi_MkT z!2vNPZH(Mp*W$v7tVgAm2h=1E^74uUeL+vL;1Y0u{K5d*u1{ zY&uEt($aJ?FgFfG(FkGA*cRi4qosW#hbus2(ngJ}((cLOED*dR4VTjG;dCANuEw3$ zYXG&T3;~w*i#=Sz0r&9mJP5wX0ClUg_GNTOl*Hs(^mNXl{qG-78$HX}B8jhGz7+b{ zL6#4*SC?YTfV2aHXUihLw+t^QLk|*M5gWJNV~sm%!zMAfOm=UtNgB|o_t~>&hgpG_ zMT2OEZ~3sVUr)NEKVcP^&@h|QXyR~W_ei6#q!T?e4|Vw%(PTJuxLFvu#v0_37oF*z zZCk|8DeYKuIfkbR*xQWet5`Xj$+jw?`Zh#qdT#^*7|oXic7sHqe;WOr%fx* zcA)85Lwj`J*?h{PtDUKqa5n#q6)FN&MW;61hyJCv!&)1;ENzr!+j59AeDv|h__Lq? z{1El|1gFQ{RNX$?;Y`2!;yXM!IPPx`vjOiMoST31^N)su^SiH~jdj7_ zeEB6l|Lt#KwqGt2ZSl@;WjUvF6(_Ii+*K;-`(IwqaUVeTeMf^5Ijaa1q!KZN&jYUVlbkXGGqE`Bp%abg{kaMPD+z4xJg)!C0(gM4FdVyk-vf zkiEAT7oZcZeg&JpaDW8FlCf*DgrqE!1|zPD;1-t*SK5&Hw#`mG{`T8J$8uQboa;uv z%%hERoLft*E5r74O^euln4~>l2;S?acFHf8gwfI7c)VCSw@c=>Cr_8&Lk@tznHy)7 zvqkH%_b{8cRG+Pa3k@(`5Il9M>q!fsp=M%R|H!ZGM986c|H72GSA7p%*bk(L5A~Qg zOLl}6pnVX&w)#AD&QfoZ-te8<)`f0#SF(q}y*v2MayX#n47iao*&GHC+%FRs=$nk+ zCzW1u^51iC>*2`EL!WxjD?My2ZCh$N3DBK&jchyC9q@DU{IXPRwWgG;$mU&%m_1;! zYE5LfSg`M;CP4%;noFZH>!ZYJl(%ri$kPyVmGGq*EeOcZC`t$`8LFebWS%vno=$eh zCnyk5>6^BxoKsF5ZIF_~*zvofG|Cc^WEAIG=HAiT3YXD7Pu;V9q*J17G!|XvRTAN!wB?z;9Jr4Y&dD9?~edo(Tp#My}HT_HSMysiD*9QL38#h>Hh z2ajTnTMb54?-T5<1RC!T(mehti$7@EYFS&8Qc>N6IdEI z)Ztd4J`(T}J`}%9_<(4*O({H~C9+ab{hA_>y31!1jXcW4&+SxIqb*%gQC3dDF>exx=T&L|joC4YaW6GAJ z1Cb20i7E7IqhH>nYR4P|ZHMC@a= zpX@+H_tzuf$+Jq*=FN(njpS@yfk{Rv+Y*?oI%la`*xN6i5$Lyw_sejM*_usRc2i)I#~lcllBz9T-(CmT2HGNBl^9qbJvZ6qd8H`meZYN%Mut2 z2xmNPR@mvTXKP|kicxD5>g<^jQ9em}P2L9jM@2rCMFb5qwP!$>JC0TXptmgtsyxY4 z{gCL>X!~N&0j23E%S?EWY9C4l=HnK!CbMnMLF^oO=*Ok0Vye2F#ec&mJXwz9woV~U zGUQ-|wrCB$!GZ3eTj@CIz{|d-9cE+qc$Pk?;m8)Myu0i00p+iG4ek~W>bIG4wjURLz>1djQp%K*w-{~I<4K$2fmmRZj%=lWsl^}~-o!k_=;Uygk2r)Sqt z4hSk!(!O)#gSK^a%Zgpe20qy%V7}{q0tWYEKma;ls)0+2)Q+5iV3RI%(xd+b`|3t< zO1W5$jWB#CANituH8mi~lrX05B`=D{q>(0q_9FErhud`izy0_BVWxguEa}tg-nY-N z(+jP$Xq*uy)$M3HY1ATh(rWVT{NS7qpgq|5-+uWNzyId>DB$4Y;Afva#N%hL@Md$6 z4)XN7U;QJ_-@F`IQ8*Ys8vFsxQM9C`=)L!L=pet5!IByMTH+5oI(tY1Uj`4^o)Dw# zx8g&xJ(rlM+!SW*Tgi2z8D=mE0(qrg(+#An*rU`NbWEXbG1KU&H{(6(Mjg(_E?25h zhnR-!*JxkZx-Gz@2e#B9MjGI$rWKTmyeBP0hYa#}c5h7wIeNp*wy_zo@gx~G>$-OR z2422=K_nVi$I`EhNy~-(kN*}c{b2vM>sye6eayiE1;eVRlP{NZbFMJGIj=jhXHaL$ z=l<&D%k(Aajj!u=lIrz{#)}OYbQkR=&aQ&MYOrmGcZr-48INDJ_Fp3j<#f>=FX43$ zUc{LIOfkwT%W#wqTa=e_;wUGXsv}Ff%7;R8F89uI6kC;8;W(E*W zO}}e4AZBB2(v5wdvrwMP>h!o$dnggKbPaCG$7MhhudJ%-bh?{f92EiRG8MC; z-}uX{hb>wJ_S~b8bgE-kjr>wE){xk=UGq>gPsSep9O#j=ST@(nr(X=w{gtVzW1mgL zx-%=9NfT=%WyyRB*9isM;=m19qjQetnipZ&t5vhUfJZyWnyY97{}{Sb>Zd88m7vp( z@w@MV0mzZjk>;}fR~RY4ajX^!kWkJcUyHVS5gCeE(%Jlv#-`d-y#`DMQxzF2Ts&Ti zBjn%F!jF35(wP#!tKLNs`%hhVmfeY?gO&be|LLvpY}faH?6@_h^gw_LMuv|MKKc+p z`}3dUgO5KN#^^{3wLiN`eJ7Ku;L{mE@+K;l7U4MRD~;y>F) z8cbONDr&Q9h+W0q@1cC7m$&B?U-T*4U%i5{;&ui<9XjT+8gw2JQX}hE+Rt?GMZ#wuh zidIhg0w!mm9jU!1F8#0XmvM@$2CkC75PPY-k~PKIKQVMUBe9ECZ+CCqy^Cj0o~51H zK5yS%jy)T_KzuLGjsd_}K3E$(Thu`0BsIkLs4TK;dmNqZKR?W3J$d}yQ7im%F85is z1E8o6(O_G8T}^(H_TFg@sY0FEWv;oVA2I`%Gh)gCn@eXIyu3@z6!2hs%tZHS>!|)7 z=Olo$6hu18Y!8R{&Q^^5lCdd#dp*O16KdJ_#EeVV!Rb6~ zYoM2}Lw8*=R8?j5{22T$C(uFPpws201Rkc$JNY`;wj}R|Lmzz@Gl7Ter&Srji$w6F z(Hf`360}g$^NjcEXuqPIbQYGZNgaqKj>=y6eDL2yavKb06sZ{b^KODs` zyK=KtW{oe~vzz1h@csAj-ouAD+oJ2>dP8jud8?5P6w0^uX|qJ$c0EK)>$M1yw$|HF zwRMJTAKm)&zO}lm$VNxEl$`|5EFxO!zN^xjGq9(7GZIOUgBxet@`5wGe)IO=$e$b{?yn98DNdDiMtUt}b~2uAW%ZWwi9DsT`qzL% z(wh>m(y$5YQIImTMHs~*rf$X(+Ry<#YTf+qjXv70+3w{yxE1Y-x?)}D@Rc-oJ!NXB zP#xc%8xIZTj2BQrXJ#Qh*4eD41@U`gIU~*@t5QgvfLM0TeaVw}ufl_2ms>V5lXqEF zS`DGGh&Z!Q2%l+lw6BBSTgt|3*G9x$h&>0JbZ92L7w;gl1v+kyo40Nr-no?i)Kb~R zws2-Q{#;(Jt^ZR}!U@iPH!_UP*V{VxEkVHEG2J5g%gHsYBd0rX#1Ix3a=?v?a~fKrs(!q|pDL-5WGb6SDgjdu@k7ZP~9BCFj z2M}I^24V5+E_J)w9i0hWqMRsZSD&aSq(k;41}hw-PQ93Kdpg2G57pH~tXckaU#1;e zGOcpq&Lr_FF5i^7xr}6oxv2IjPYT~lw@Z(rU@u+Uc)Ohhyk-z(Nqn-%jtCjF)PTgZ zu1qT`4O8fAzM2;gM=RwFf}HDNB}RH74ir1kZH?TVXqe_AC`h6@7%eH`&ZQKgAp3>> zL+E(=ln~mZ3?9t15TvEC6&17u;Z*X)awo4sqE3|&Fby3X?PxmLkOE9zKn5;}18Gk4 zvMwedb7(loxHgv%PPa>4p7balIEf@J%sO)ZQjI;J(~qU>shQD)7sRC*3)ek5Q*dQe3Ts=&01kHjyQ4kGKvX87+1ptuWCZKkRdLA68haWz|pa0cg z;G;u?{o28ZaX_Zzu0ftBElJRl?E>cXkiGZ4McWg+AlG*S2F4V3AK(3;u8}7wKu&1d z743zBA!C-n-W&(3GD_cU*NCnGYKR_VxfoB~zI9_b zG26!0=TDyE>9=2lOECaIHPS^Ms)v|73mtMLT-^pE+mngA`@2%dwY4*(?Wt4zQ>=37 zuh*kjNHF@wHjy_EnGv~2S@K$xS9 z_IZ7)6y9#xHpG z^ch~gcnO_lnDTHrBkrLr-o5u(ju$-{P1V64`QNxJ>2lu0T?NGXxUWfrrW(*K~A3C8>ys9V_Z+D+Cl#0;*R+IQMsGoJ)@k z`c%oZtkW7zH4&WTd25G$JRgGWAotw^(ggIj3QtQJurnxjTqm6X1FoWqfFk9f)0chr zlItobogisC*=I@<@=XT>wnzH!r5*1Ppvs9ey`A)DwVUJj-uv(4r=NW`4p5@Qo&$>{ z6lKY!f5)Fx6;mns-sVEQdAZo0z!93r-}ZO{t6wc<1az<8ETg`K%pR2x5~5E2Q9D`q z?!I6BtU5b2V6JFU$`0?lyzleLZ5scV|M;81w}OS>{jL%ACt6+1GO-7i8_jTK&kCpJ zf5+VRD}Gp%y zw)LO#2^2npn{Yj!J-c}mFP=RUyJB;h%v(*}8!rIE=x z?4&PsN^DA2Tk@z46yka6eZK0R`^o6MlT17ZBWJd_A7%|4@R-dZABq5i)e9mE2ZKxV zMMfvm{B4Eh?)qbpSatxycQ`^x0N8FzYA-dQ^S+4znvKjs00kn5m5#kILL-|Nn@H)@ z>c7h1eS}ctF2M>Y0iuDT!yz~c3MUO-8K`V_R}}AUp%5Ai6p$i)*+1sWg)84$acQ73 zn?nNyeOgB9p7NSitEkc_@1U`qi;lqgw=zl~=xkYD(T4+vy0}Wc+26R9X}U^Wjt-*P z{l#7Oqf#Bv(XbF0+_lH^oAML{mOUuu^ZQRX1l1C2q&3wy!=CI>bq9IM%QxRV z!53eDH}dEU%Z1mfhl($rGU`XwNeMJ?@9$B%;+TKHFA6Pv6Dn`d`db)C#cG10@ znsVgIsVjY%?~LUY(H?U#dZef5IajZ7>CyJy+7cv`Ugf+;5GDD=T<|7^yXYut>EqO| zrO93mWFvolJRLtasJMXIVL8 zxn4p|33ZL<6;L;X&}q|(4U&iT`H5|8gejwSrf;8E$p%#?MUmi!)46~B7MMyalCwYsRn&J&yVW{jmKlpq zGe}LajfZAO0|<;Ke~I&*VA7Rv_Oqt)*CcqbG#v|XGIbQG$v0xE1}?%8wJ$&!Y^0G` zMY&X)y2BQi#nCCo_gABM5Z$RD89lb5nG5~qD~?(s;HjKyEoL!MmKfDC;6>juPAEk$ zGG^LNIzhF%zL#6=nRSbpqm;a=fsm!@FPayrtm;=3tCv&2$p;!?<)yCq?*Jciny2`# zY1mTE4$5%v-FtZW=+Vd+!vWVG$CMs!$F!XMT+2y9ISW-xG}h5*I0tX4c%6PaaAdnd zokMS=ner@40km#ceUb>0(6st>bu{OSmQn-bw<^WHl_)xk+>(%bLapmPKAIw0<0sN#vJLB7A0HL}Y^WXd8% zt41+ZELG|!4aWj`9H|b%Ac=MZswa-7z3xjQ2K643vE9EH#)11{Ih4zadFU-lhf0r1#S730qpIo^K9qvIVj3j z__?mti5$p9;AJkMk^8jswa7W{Cle-ZjahAMM=Z(*LanU#m09;RU8`B|o9#BLI{UMu zT|8h}^wg;}mNci$VEf1Mz0#Jg^|2mj8z!uBIMe9IQu19vM{A~qnfdY=mTjf?6)dkM zOT$^Wn&F$vNGxr^7#o9Xvxp70EFMR?A~=Ks&kYB7vaj0tnew^t-~B2L@LIp_q_OY% zTD;@+Z4ZUTpx%QQPF-W5C}cC2ONLyGG0ReReqO#zRawN_Y(^v76BSu_`vBA1cp58x zhhkJ4hCngn=d@|zL35I%!tAefqWQ=DQHNN- zYztC=QMOA#XjguIv7&%Z$y8cRZJk7B(yGt58R^$L)zK$3HvqM60CdD$z@v?>$jVw4 zEm>R&ZAbmf$81M86giKaI@Lo3UQ{0ztAi=-zxMz?`Ru3o^oKvhjhi>BOqGKwu$qxF z@91uI@5X+J!^ZdNYbC_hpQdt@ohF<8{j2oM;eWE0)R!xotb^Oq_N+w8NcgK}+4t9~ zpqKh%T93+`wKwZu>cTqmqyPH1|B{Y2+EZ6H;ED^WjJsRbwB`fA)@j*E@$g zXZeQlFP8%d&!r7I3@+M=V=5cM3!Lk}99)Gb=7yuZ{TbICYphqHSqxsnNlUvC>*7Zl za=bdD(6m=3gNh99cr6<>>%8`)RbQ$`{w;#XEfOBsLC4U(N4MC1Yj}v{kK$D7y@2P9 zJNNM9yKiT7IB2r%wYXesplU~)yt*B?@7$WSAYOs9KZcs!0It%zv`xpVfBZe(J%9ZM zuU@|rjE#GTzMPz%HC#!01{3TG)%VbAF4f94%u$M`UlIH!zFSc!<-=qE=-!efFxpzd z&e^{i>Ah?@`)PZbsc-mC%ieYxbTy#{Imac*U}P!x`SSFWa#R!Y3`JhccGRq**j$s} zL2wfqtXj-g@mhXN9`Y7@$5>}tOXKWA>4@zZr|Ba(c-qgtmB`JqmSqu_q}wK9UWsD_ z5SREK8)s_(WkI*gp+_ue(AB0)09G?@#SZz@36v}GAnycJzwMe+w6EulFK1duWANrug5to3XY++0)0$b`@75{}wN^{Wan36SQe z7-eO9YQ&}vi8mS-7+pQRtiU48%elOjR&ga}3O^Lhp$#Bmrm?P8dDQJ0%CRuPku#!Lm=~a++zmfWk-t@viPMh@3MS z+k;#5=bd}+9-Q|3xPFKR(J|oYIc!QD(j<*ZfTuKg8QPW1Rr;W@2%N%%1AWODP*Gq0 zw(pU*dMRa8cKv7T_HA*!Th~2g&3uXXJu`-AWNcQSkhLdvYm+ry3=lu^9z5%ZHMgyk zh5A*nP+%{e+t$(k5$599)PcwGGi zYlKcKN+`4S()_*Q`5zxY$Im`|FdU{q$3uB9U%VI_22ZYSN4fVX95Z~lW}RUzuCq11 z8$2Fst_$`1y(?x5*1F(FOAe5{R>xn+c~pMt94s?|QQykpfB?u8iYaOq^jp(O%6i>J zMdPY-EosU55QC7bRb_`u1%rmGI9X{Av6gHmmRdSN9AH>P=aW7nt2|t0I5Ajq0Mcw# zkgA*|ePaI|Np$1G4!G~IEF7v^x9{Nd-~FaJd*k#}%F2R_Dzf)Ys-x;HE#JL&-WgN< zJTWv+-gb$;rcP@+t*OSoJ7{7MSQG=MXHTBs-FM%`#l_q}I5H2#$*m*Ex06wG6O0tO zc5-j~)Qpk0r9Nxo+nxq9@HHKBtS$p8i~b{Z1L%6eK`xiiSgdn5DZ2&%IQm>%yd9ZO zwr9rym`PX$XiAHT0VY|83?`Ucs7zWXo}}(k4r`WcJR3`F=DX52D|$Cxt}cF%@FMFM z`A7J9s6k+2_YqCX&J&->TQ@+L@#RIJ4taDAIMMW*eh7VI-B-{u^8`k7zemZ!jE8e3 zXGm0(uNj+CHVsW@w=dEzPL@7Y@_pwI*v7+KN5Yk zqarA4GaRwDDW`AHI__RZ$V|SAQP{|JvT!$cje_%;{q$ox521>yrk>iE?9KM8&tkJD zX#`-xs`yf%Jl|MB#V@6Blu$alvs$**ny)l>4hv{%>L(gM=TuTff0WkV9EdP_UhOLo zLE{pso!!b3>)tAK+WNd;?kZ4Y;|Z3Wn)fQ%AnWC3tz+62E{cj&C>_tFkcVt6rwi^K z9QOCOw2n8fOP{d=&fqn{s4scvG-m2UHiC23p@;l`*Msuh0_{4g&%O{Y+hH+5VLu+-iLDAkK2I@CpPr{VG) z9pEN4!cp4f_0+E6KmSjEHx4$+><{vO8LnA}Ww{o(w3h?yei>w>InR`cYr{Cogwn|A zi;5O6v3$c@SrCSz%SY7n^^;fl(ZlV)n~Skl`26j~Vc_`|Z(qM2hvT?Qw8?#^hu*EC zfL0S3nBZs_H=(`!I$G?Ic*N zt*z0x!hdZK$j6C5)8U)%x219+Abi(T|Cf?yvGrFOR*Sx@8eO-E9b+RS%Zk2aVh<>8 zQ(qRbLqRWF#2+fYbLrFO_soEUVx6FF(0_r*tlGO$f{<0Y81$S2dqbPK^wNzyNj8+$ zQM5=LfRIfDkAvq-sVz6Dhl*$wutW31k}o8l;JyZ^H7J!c`HDfgv?lt^^qVCC*{&=0lyp&@&OI|Oq(D$WT~%)~ zIDd)KB*DYr(s1lQULp;eX=Xx%9X%X4Ey~SNqB+e?EJS)VHbIx< zQqoZYo52v7aiNcEo3&r!Z##ng|HqF4 zY63;v5cWbZZteZ@o5%S5AHEEJ_!Xp93RveMb~@uyz7CE8s1six%LcAs!#vu2tq7eh zJKADjVZ4X&&WqRQ_~zNm(Xj0h8;jM7uMduU%0|3j{IUF|caS1# z1;={Hvb&*V9_6V=D6BzY9)Hl-%%6qvi-7G+24b-^srprsE@uEV@uWM&N*jp^T1CNf zV;Q8aLPMsWo43eM>v)gCTh}!KC|qjxM*8d8E|hxbJ5dQ8T2H0$Xop1fu>UKc#J9Sz z{qOGmhxqo3&x_`Vf49Spwu~~_ejrcNY|at7B%d8*_V(>tW8j&!ti@x#5EN{>q1dj! zn{M)3(sGvb^7->or|n)I61sG3W1G^3pbtp73^!3x^sW!m>NMF+Mojra4Bz^?=yFY@ zpwgk{;FJjF;*ha_P#r?q)|4U6Hpzxbq%XFV4qM5a*-6Y)(pOoMD-hm{^r*7lp7L9! zzzP24zqqtjmZPw7d!Dd;Jwue2-SVBD@rh{1Tt2Pum2JxR%$k`c4aMPtu1B<^ftCbK zFqe|SJJ$GR%^rC2V42JBY-ut*n;SDiumUj`+=@CcFE81MqG=9jDs8%rXuKVQTH)Aa zAC?_K0{C)mS_lV1=yG$xT9q#1_z>-8|JMYjL7*KWJ4}SLPDjR=b99Ov2at3o<^drw zMhyaKgvyj~O-d*=2B165Nt3KA)htQe!86Knk8WAgNmLP>tLegC<+IYl&sy#=t0;?7 zk&t8yI8t|@w-qixRuRwYTRdmGB>)_Oiy%$qp#XZ8>eh`G@(vnpi(G!dAc<)9PYwE(;CN2P1m z6K_hAkW+|k>jr{<`*;5|h7>19#fxM&C1r$ae-zlCXq2I3KzVrhd%^+3KC+H-#AxVq z3N*_vTF}VQdH?*oXSjdo=9s}ey*3a1c=P&I>fvk}X%v`~&8qrU{?m!Da^zKR0EIj| zVpXA^r8pwmyL%{^$@cMoiyR`t=kqWn8bz)R7P*;rWRcBSo^&6dSQcj?lXP@4;;k8h zQn!l_s+Jju5xve@hc3qQf}mSEvUIXcBk>RntWPdciKQI5RDjr>dbLVgO3|onTQ3M9SAsDV}JKiD>(OVI`vUc|u1Goub^T<30-QWAf+LG_Ts=*?-GKzBL)ZFbbi zMU)#zCxy=UDoX-1I^9MpR8R_#SJVb&C&50>XDm;aGXVlB0|dazII!gez(xAd>n%e? zUWX6}OjdLT&=U!LC&avV?93oLH`}qK(bMDZQ_hU4Swd4rr`p9@>X-C^ZAnFlsWg<3 zXoRc}RjS;TX{Tb)goGQ@rt}xJVfzxT4^({jyviDs9$`4|+`WTGAAEotH*c16l1&Jj zM*F51YD0GYZmsi>A?186Ey`&$scYs82*Br>NjvAUNyf{N>~LcbbPDj4*!GLC^=4u#r*g#$-mhJ8nvqB=Cyf`1m z&9-U&lj+dBdG&HQA^a4{8q=eti!2T7a+<%30MQN_8kH!4Ybjs+HtcMA=px^ zJc(Mxft$2mGnaPS0N@yh_}*G9Ut=l3G-8*@4~@j-9;v`7%54swq3v;97958MHWA9U zP_7dfM%y$ZT5_W^3l6ajCjE-;*2)quryPbJl!*R7WJ??k)=$StP4KN-@8Idse`2KsH1`{M&jH0$FG~>`PIovW z?j>XSUFnax_nV^=zZ< zGCU1A$c@ZPDscG_NF24i;7QL(CQrLJGpv+jOTA32UDs|BB8s0I(}5jw0ei;G;6b%- z%hNsssh4R5jC#8S-LcD3l}2GLo8!nOVR^__^o!=sg6Z6hO#j&Ba3M$eROG|-rzV}Z zW$RN<*V6H^mtrg9B)MNED<4Kq}0-BT^au z{l^nLG_yml3u-*1kTP~5mu*+J`;h<;1F5u4NL-&a`R@HX+bZ>R&%0=$e$51{ev&G7 zT_eDH>xVqmoCNRPy}}^!JzaXzoO(xsEjwnd;zA*bb;)&7@Hs(}^{;tZ(jyB%o(t zq=C>jor2H-b>EeSV;wT< zmx4D*i36%`5y7X=UZ9-htNZh(KHhYc*Yp9GX7BXX`*W117MwV)u#61*wWG;)eLG_Q z_1jC(nc3<-fBia^A6%uyT0!(H4Fgi1y=@Z@Pz$5K94+bbqdKN@PdyEJN@t&ruq+E; zdt7E7#(%OQF6r5w+@=%hjH0l@V#Y-5gN5F-G^<*$Z!JD|&`gK#GFfA9VG4$<#zQ!AIp@SFg3V1Zf(j)0p^i?YK?Zx?uTG^1$C z$iB3$7f0H>r5IgeNyv26X&p3X?=;KQ@bx}Gx6^44&MQ;uU7SUx-Z zGiE9B+H@X=(=PDCeR(c@YHb?*UPKhC-y+=XD(D#ve`;OBw0Xxrp!Xf9_r0<@>H2Th z+$<7xN^rH=I*0f-o^%AtX6BgY$EhDl7!2$+|jS@ zBL&&At!m~rb(k|SYnGLG2-2aJV|vV*tU=p74eO-V`P@mgeI`R$=H^PKBFYYMT{{YN znhAGhxaYw*mK>4ylJHBW*#SH^gBQRGWN|cUUAZ|@N&QlydL4C*oQ{iuMAjPD*8-ia z)mcUuu?4Mcz<0tEtTlz9wf6xN;CPge3heA&37ud@(gjI^iim+FnRkamfKR8uGzzF~ zCqPz)glJ&&Q6ucu_=x^7X_5(@A*bY`4-QMVq*<5|wYvw0{YO9j34ZXCA0MJDx04t- zFmvIDHp7a~>^o8xaO4E;yO9c(q?+ZQ$U43i$~!|RMn#waI|ji7d2f)X*IHyafh2U3 z?QbAL85SlGPNSU;r8PN|>_vf$1 zmDlGN1GFy=f00WLgp)e~RRe1~uweUm%?j|~idEVo)!U$F4t)h|=zy(pq&d>zp0Cdt zI&qrfup$BgazKs0%0iFO1$R)m)bVqECT;}n&5}>K&mxw{cpDfkt*^5#IvmH(SHC7* zXVg|`O8gM|FXMolMGDw$W~AMM-b40xb22qjYvRCh3FR6=1XhtwyH&NN!DM*p+4k(+ zcOQ;u``e4R=^VX1$k>aQuY(RqetQbAL<7Ula(~;kvopN+;6Bc`jj2ZG>WW}j{Lyl2yGIR?b&4DLII!pgO+W_(2J{FmTO9^$-q+du z4ljbh*&o{ExqPAqN6NSCCTBqqyq3J@17}W_py5(|W)-7=%S`X7ww;o$%Y~3M2@F`~ znrx`-q{=(mz(hKnz6SfMbdCZRuIZ+LXTuz}vscA{# z@XRv96J_-BG7hCO`cdvVP3F=uG6br`e1?2o*h$Ncs-B3%vV<|gq*V5jMKI!Bk|}^k zcya}>h>)(jxvm5b^3FV1fCWUE(O#zNqS7`1VtskR`nu2TNj(o~wE_~G?P*x&fGHJa zS9XGuk|K$ZodQt_Bvq5Nu{(n%RGo&9yV>b*l|dF-OIb$%78jQ07Q{E^HkN+Z&l=5c z!nkdG`s9b7;s-ze(Qw`?awc@NJ{6}NaEQ^{--IljpuRVwUjvLXY@W(ABPR<=vi5*b zZKxk5|H(r4RPQ`^MIJBi5JYu!E0neVw`d@|mKz+9T-|EE(qHm4!IXh$Yz^-0~JKWyXN-=T->1>^BOL=Y?uu^7)w!1xrD?NGZ9Ib425AElh#JtXV4`I zoaJCWO)r9(HCs_BCRaLSUhUi*jhlDx9ir`DhGkV3c>dy9$isndmz7ta zYx9dEmVr6iRtUbAUV60y9)W^zqA{Jov2294(Xg|xGEm!e{xoFL3lrEc;sru>N0y}E zeF$*s!D8YI4Ef;!UC6%e_n)jxPq|)3zXczH(dM3*mHu06Z~QjovXE{!xHelN?rMk( zwHe$5!#oaO3ta%Nz$6sL6h;@lLut@o)@6w<1G>;_6C7k4!@ir)RYz2X8!9iz-!ted zKFun@Q!Q%H;xgzQOKj*nS;?!ryaa1{kgKav`$M(@7OiMmW==|7fUS;`g#otaF$8Sw z=_wz5zV6FAW@#Yc-h^i|Va1Aa7Rglps^v3csj_2TxCIlg_z1=Gb#J%$gQ5a4e3Oyh zo-+zbNQ46|rIN(fpca>~;SFK26C`f}KFg=^bt%@!?`kXxOig(1-j563JB9NaE!2+R zSJKu~j;hs3y#P#%WiX$7`YC?$*=J)NWj}zcfKq$km9YROvqbQo_4+L|BL{q5vRK9L zWCx-ai3Fbi>yBfimUf|f-UuhWSBEGsS$4i0Eich@tto3f=7sNiwKI;`yCYum=>Yss zXpR!Fn&>#XXefR))e_DI&p!R+!;UL9H>FQ<%V0*8aQ|^{{b-DicRU5x6Sx^ospykx zUHt1Da4t11EY_lvjZy;Y8@ah0+mIMOQ?BZP!e zw5LfIs~Wc_GhvE4DT;BT1C*I^;_XT`ux30h?Z#f`%v#6BXGBmF->ua>XGO2*KOHsf z`OtaK{w=girhzLD0b=t(KGCEvqGV)Mo(V*saJ2?xW=0+bv};zo|vCG)Q5_D6AsW0C8-UVDy^4 z7VC2+7?C{gZB>I;c}pyxTAR`4;L0<@kT*p{L53@zx6P@B+BCpJedGY7nFo}S2_8Wk zM#bkEp)G>ssGYAfO2Ue~-J3NmF$G1%_8_PlfvRUbYm?zRE5HMII4V2B$k7HXTj3>O znSdX=kNeIr$JDE#v;#f;5ru93Ff`{~oX#?yUdGf+VBXq&t~KV7;)$cnqy@9*FgnGN zS@o3-$>9`(B@6U`$P^l3Q0rHrT*rrysscdPcaVwOhbYdY4?n2o zFfhr&P|JOCqzf7q0mW8R&@Vg7Qmr{)u1vEz4K*VmC5zrkD6&Bw%{mCC!gTCEwb`|n z7+3%Tqz9y9TZ)t2^S$ke$NO@FYM(G&X_NqIyUQgkVcwt|M95(E{pwGvQh+pRD$$*! zEe(g0e<%2dU;Rb^Iq)Dni{w9nINDeJ>FA#_@4Z*m=#Y7Jl2Cw&%b2!YI%P_}kTO7U zp4k=9aA^w?-Z?wP+rv!4n}Z|r;{4!P9Ta+Vur3Zxk;xt&mGQ#J;$6->s8EQmQ>y0k zp5$OS0XrH5hf#xFYuwLkOWGYEjLs76Swe5=0W!+20|gb0u&QC>EU)Vzv1g;G`&Nf) z1jdJ*TE#e<@?O`*vpCE4&kcFRUJ_DfR9c>Qls!R3U0pLKMV2b}>THSX;h%M$kHXPt zre?mvp1H_5Wi(bdHhkXRJG*%Y*Kgj$@BZnRAUd zM<0EJSBEkfDBH^FOEiiwmIzWfwy4Z{**snL!upcN{2l##_VgK=&-N|dVdh?fk;V_i7sJuVW>8|YLz}*= zcFLQK{6V2X`J5TVlhd9J$c#Nc>)db{vqb;Rxp% z?X_(6vWU#HJSsl%0BAmr;)14}<(VE!VB4A}{Vq-TCskwp_eMWoYf#~lf1NAr*3!#O zUuSOiH8!787B~4^N@fEwM_%@E7DMn^S4f17qdaf`HSmpc8N$U)$R#VJc{p7`gW*r- z878&j<{JjaJwA{`wU2fa6@w~O^;Q)*hrdePCYC{t#e?24jm_N0vHcEag<@4bh6 z_wR=?^r&~XIogF(3N!;l8M4}EEy{)fE6)*cOtin%w$>2QRUFeW zgqh`97;Kf5^`KW@eDy8<<`@4sP718d0x?+MF)#dG4!-&06H<0W@I5drb-|@Q@iJ|QC;U3ZBuHI>bRq7mmm^GCym|d*40=ybuOaQ{ zcx@?1*;nZN?%<&uuA@PmWhV>OcF*!_-vq=4Er6slp05-xuy>Xjj#Mu^+m$pvT)!Oc zUgx2D7MWi*qQ(Ao$=MXpM~o7 z_)FVc7&r$KffA|DoSCw?p0pL8WByt4nxV>P*1Mz$?oy}c)xbbHThsiA-CbsasOvKrS++G8jO>`>|JH*fLp z{=5G;Hqk8-o#BI8#{d65$9-aY!0qFLDccO=#pPv#1?p&~8rwNmNd(Ni*YDoFh3}rf z9)f;x2zXp9Ggo!-tp+v3n0=AaGWEIoETVNJnK(K5pbeWjnX5dwq=R))gYCbSks=xk zL+0hTiV|Cgu<2OHjWqUbn>%z?WS;H|F@)^lEF%Z1YdITNiGx#dqpTX|s9~-5-Acgb zcyWbYAsegOq`00DdH$9L6()GDk7dNP$r!4A#YobomO2dSu)=S)V*Hd}N`?+Qt)H#=wuxRG2_*aqkd}us4&kIOA(L>%g z`Y@H`aLBE=-o%<%91-mfl+Z@aS-AOwzJ^&Wqg=~h(ZNj?;VwRh@yqy!XB&_Y#w9nx zgINZ0sSlfZIz~TMLcZ>t_8#(@Mx0rJtK=sg-QHv%s03Ux1S%r58qvL77iyJ?`Q3?T)= zCKuyZN)zo}%D8e6lynb*w;2aW-mPOwqY8g;d?S}~CVe0n% z_ut1m_wJ2p!!1Ybn$t(#C<5&0nP6Ja@xZON6F)v{9v93!xJZRCt3hOAv4~KDi5g+B z9lq=4bksR*MsQ81gn$(nJOMvSBe*z32MmIHXhX3uE*dogY&S4S#D$<;wK)t)=@-(t zWIN-DYR6S(wwGIVvfZ=%IXhc}ePgf8dkH?5v;dm$Iqjj3!Gg)!K*kul};ryolwWsaj4}5R=ApYwpU2Xy3SaXoGcMh9CZd|wi6dq zuA`LUs^>aWFBCT6YmB(pG}J2F00$$DC*iCt64~T6(mbQke1(U;&}k>uiTn|+M*WM- zq~&2XDy|{%IrF!KJWd*vQQj_Jj(#!g2T{Si_6H@E0~e6XHzqxpg=e!Rlg}Yz`~2Wu z{BcNRbctxAba=zN`|o{#-~aa4BX2fk>D8-Oc>e4~3(U>dESWt`mxxT?$dVRYxj+8t zPw?{Dle7(Mq{l70Q~Er`U@EE$G0LV&z@mFoSA49&%r>k4{K*sCz5C8C`;X7lD}jbt z^;Mkb7zE9}uF^({h7*gO5mr=0)HpNe%`PrxO}1S&J!c!Tj>w-L*_Vb|o@|cl%;mjp za-ExNCl2K%($N1UUho4;uJ%sSeAp&*+ET-90#6lYQI)ul(NTGPJH=# z9==ED!q(?ymJfK@0TUMIEM7bpzCrRs`a&X)ofzumd4|McFqvuk+x!6W?eM?b>dckh8CWJ@dSW!OWZ zxZc#Prc0~&-UJ#!i`?{)??!8cRi;MX3oO{yb?tJa3@ay|{4y;#8>Aq*|3>)bJ)7{s2uHN>U%znmO*G!l1(GHj*ao# zX{n!o@zvZUZ)o7F0BK>2mdwg?mD9cx*sJbZIbQRcC68T=0@FX*#LHo4my-JJxl0DT<$zA(b@?Ol#zizL+qR)A5zv3rSZ=>XP&phcgtE}g83zdf_RD9u(UoiEIq*ukVxO&qo3dO^U{W~Ro)?cqnE{AR zKJPwwh$oL99~|tbiC5d10gs*qmj#(FJB7Zr0z_ASUNlgUn-}$)PoxW6<|PS#Lq(G<%E4H zJzx{kms!OuupJ@iwACgfPYxhwD(=e|@Sn7$cBQH{@PrKLx%`Us(xq7zIo@a9cF!I$ zlVDYVIOx)f&bBoEGMEo^xu1R-T3HBrPMO_pCa%9mQq%@Kt(S`K{hF`9g z?sCu~R*8_7+N6G-5X!!@lSbPV{U4cn5b_kWMoEmlVY76wYam?vA}9UXch6_W(wTK) z4za*c=^!-+%PFFAmjkSrJNj7xkQ63mq@P9 zr4$?;C-YtB8A^c(&=b^yo>+ipVns=^QUZc0F-!THaqk1KIvT!UmIgP5OyG

!lQC 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

%Kz|}|H`sx?cpo`&0qL#xp46UjVn9& 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%0AS4?(Ydlh%Fou@Bn~q%l%JFVF7U&_9&p;MDS;@ zxz)D7tB#X;6hxB|n$MRg41uU`r}|iyf~@X%wuynn7KzcPuy{QEUi)tzEkErwn83cT zFxnxfsS@UPGMtq{Y;$)T-=D13>8qmboVnnhrPzK5`l3TPFU9q&GuZ{@wzHe;Hp{Rl zDP+ircv3Epxem2j;A>PTYM2>lLkSi`RML^o!?lhI`bN3{kqY8P=(Tx?a+)m`M*+iK z8_573{ni>C^B*oZ<)a zDa!d|$E&Xrn@rlAI}*dJFiP>F#}%;JzMTYBk~em>-+4Kv(nF^X*!fbJ7Mawbr1YY3 zC>w~@9><4EAJtGaM~Wh}5|rnX)oHJ-mfcvv8YeznN!6Log@G?M{a$ftaTXY_>V)V> zllyW?hqHS5;w!NsX69G4?04F2F9;$XpPIb!d9s1^5IzPlHJq9a%g)#@vVZwc(=!#q zmG0I>{QUNFwHZqrE6yin1P+Oh)o?n5E|f<*YpXRj#L{KTp4e{=5+Zq<&zycKrhnra zLodI?7iL^KiTVl~B*C84Z> z@o>2%%m^z^E&j1-0Ce3pJ8`ZB)h51AAL}%EocxF@@NT9{V&?{iNz(=IJwj%bo%Hrl zzdvmkWMha5wvy)VEep;^S0fI}l@hLUl02M;4q4Mpo7;)2Y!-3wiR5N^?_h9T#dYPt z+Juxc$a>Y0J>$uM)DM>dQl5WQvRM1J&#fR+RwjVPW#--o7$fM}_vqk_4h-H-2PS4b&Vo(=>ytf;Gh}f^$JQGn9cT1FTc#b0pG-RPBm~_#RZ-= zf=j%ywITUs#vNEt23dz8@akBs97SN=RT`KzO990(IKVs&Z_<7<7hySNijpZI%VDxx z!^;jRx9{_fbe&#grkT6006xs9$UgRVA;^mZthJYNU&Sk;ITbZ3xJAVd`73Ox7~XDb z10^9wRdrkC9JH{-7(*s?TF?}(vvAsHpMPP${LODq{MAeG*4dFOG@9Ld+qE#JnV(p* zJyrp4$R{0UIg?XBxYm0Iu-^STOBZ~!FZNhPt2rPO9>IwF+D8=(JKMgp<#8c8Xnmnf z1FnAG=Uv&E){ORjaVa`FfSk6-7RL9!>`seuCf+*<9ZW8E!|JUrzjIyS%eI&M1Xi<4b;TM-GwNuSHS_2gv5S2a`1 zr5W(G#K8$bMl0{kua(sLz5rt1S~g_WNy;#{pXT%W0`4~_DD5u_W3AJ-0L{di@Upt_ zlkIE%T4tOoSrWDRMaEtGzIWWQog)n`i@8Q%cRj>^gLp1WH7g_Gh&X84D%L6id1Cm z5@dY5bIRYM+b6DbWKa-oggaj#E#;d+aaIfvaV6Y2YZ8fYPyi6Pm@sH2x9j+LH8;Ax zeOr#L#J-@^_N=$%%efdGVT6Rh7+V}w0J6)Twn1aimwbf4BPT{`7Ndwm!xJ(93N-?F z5yaGBP|QuoXW1h6N-h4u_)MswoT@vq2H6?_R*47hJ+tVJ1Y2L0*I8Ff}_TmAJkc zanY-{Hfg)P{)+eBV2S81@uJC#OM3PNk)?iLi3ngith9wj`$Zq2{UBazg{1k2#a_hK z>`CFUg1atBaixk7Gysq-4FJw0MhIN92yBKTQZ|PB12>VEZx{46mbv(BLU2*!>aVpr z;e7w`QDnH91{*?b)2pvvSX+W;MGu#ehugV7V*ohZ{t0vu9YN6>nrcJpJ)&q0&AwB> zbic+`n6BuOw<)~syO+W1Ozh8>uC7%rnx3q-!lZj_4cWxX>WepDV<-Ja(MxwY5@|0(8#iQUr+YHNk71zxOwCPtpwYLS$*q)Mf@Y*qOBw8hryhpbWB2#gJ zZCW!UV-T-37S{k!19TR0dC^*V<8!sh&cNQdH8?x*+!lj^+61n&;rMz?L(Kie=kMbX zAz!Qmgiwh^0*7q3BjP`We#@5`OFNOVR7G8{Zr`FYN=GkYVz}6EZH{tM+Q^Y1>Ok1g z1(@3$K;o`U%tM{bjC*>57NpxYvqoSRwtTCtCC#wA4D37)N)kbwHZTkW1_bB0Rd{+4 zj)xZ*`(k^IQm8AcjDDn~Dyn;L2YbiEfrG~&1A@EA8}nt~sX#ck+i3RjnR;Vp3p)ry zvuqUhy|TU{!iQ&6GxhWY;$Jj$$s>cpU*eO?Sk$d#B+02G_ebh+=EhS>(r?C zwj)lDTHGMtd;8@$cI#!$@8GiD?MiCRWW;3+f%{w@FuIDNmS4nm*cYLj^M3S#@(YGm zciO3D)pnWs5?k#Wpl!0_*Qn+g(oR+_??G-6w3D6@ZM#krT3mO$5U5qUcU?Bf>yY*- z^ODh$URVQXTe=>>wlro1X=v)>95D}ZY>d zHW*&ZHoR1$|8C%1D2I?F)_OFI+GdbFz_omLhz|$}(r5d7w0yixd5(--f_SsB zAS;oWB=i+=Ght>9I`2fzWe{640BS&$zZ9OQ*w^!ItZ&}C+IeNVDc8V>`c{15yO*Zy zSO$20Eqj`|$Kb==MgGuM=iia!#h&a~ioSC@$gScI>aRc(^UNS&P+tb&*qb+skz+7* zjLeQjjXd$nnj$M>0T4RGKJSyyUcoL8?6D-sZtHdBV}?>a)igt#tmC-_W~1{?bnk0a zO28FXO6!)%@6L!h)$x=oRT0+rz zxq=TVpP>O(q)l1r71bs|_Qw7Z;N1}W>Z=#_v!DNRE(`O*t$zb;-r@2)-|YnAxtg!N z7tj1&bC*8FFFT3qsg*umFw*+__qepzzUKWc50Ux`a zpA_7!hd$F2O~MsFk&BZ&cK*Y6L-tW7mUg3CB7rpVmBCV67Rvs@$_~1HV~GEzp2e(m6y%Zii72z| zgEpSqLu$V+LS2f4Y%xYrM?1(=j8S;5byOo1vb|(EUppxua1WudkZ}Cf3j(oF)>}K> zNNT`qlYqcCJLN#8dEqo}gUQ>Xpwhn>&zi!^Qi0`4lBo@uR+<1AxJD(z$UY*c4<@}2 zT5h6{48jLrar)`(yRFp^6SHegim~}W^>_!~vvM7zP=#EZkRR^Qvyci(8^|F!lM3r? z_@B4KHPeUw=pigttH7r*+{e*4*%Whjig z4>hZ{?>LdAG$1QY5?uD*#JHZ7pdXBIoe!p zbnRGVt|8}3dkdT{w4pYO^9=Ic>TIEt6VNtQY09>sJzv3!1w3yb)ZxNw>B{`%Y<$J} zIX*W*@T(525%HqPp_31;i-vsZzHHZUmbYyC>sK!)eRw8>gnHin$^3K4La80BfQx$X zeoAIEHIW}}cJYo(mCVH%|7xf{65dNmM(E4s`#wF5lQZ|HV zGhnpl^E4zmiM1C^*llhJ>ZbfC97ZuLSyk_m?^9M126~ghZK}w4S2F*bD-1ehvs}H0 zM+jYifgDON;aH^1a=EgXJ%%ZYg-a1#61?oxh@l@Ii*DJ-IBY4(C+LzM@)~*u6-d}d zflc^3_S@f#r)KQU$*dXg`K(PBp<&a{z3PcE*>Lpj=3U5{UC#0uIG1oPhO(-eDiV!C zN(TlYZcc&Mx}L;{YHZ+e9DEe*C5XLu3Y^32%F1si6{bZKB{AdCyk4Wsnhr99^97!^ zv^k%ZVxo?giiF6NHxN!SqFlZse<=Y*gKK#20QR;5X%mMLVgV5{^H$WX3zVm1xs0%H{>!9D0rBMaqH6C;{s<$I1VfBU1K*qb+RYz=1t zzoC6GhSyhI-y&_#CFp>(%*s^P($z@QpUc+GDtK2Ig5+2YE4o^2(g`ILUfVy>2eRV# zAQYV4x;Iu!uAN%ht`oL?-TrV`($`h(YR0u@Ly6;XHHYfp?5z3fOscWyAE8;orPC$^ zZYM!J;e3Y(0^GrmBWQnkaKS=AzG~uTW9JL1-uX2C*`*_E;JPZfY@wDK}Pl;YlpYRb{c&H@rOIIyQ(h_d&aYsTf~M>+`7L~-X{zUsN>9S`dqY5 zV`FAlHY8=t`FY|nKe5uG+5@bUPUgY^2KHtg2$?b9SACd$7OhG?*0?54gh#(-CjR-j zi@no39@C#|P?~*);F0C#ve_D7*ZX+c23KE1!AG1X&fB6h>!utaxXa;`C3(ipST-3j z^h8TmYrYXue0*-=WtZ^a*#ubM%ynXuLaEp@j;$GHJT>@Bx6-MT`lGD!v! z6*i~BRn~0=>+^+88BNDT=!rYBcpm>grK6d6^|XT0 z_mZynmpwIEZ%(F2S*ZA3`do3Qu|q|B{k(g{^{uzoJ72?GS!&TDBUj5&#+Hy@eN6`k z9jc+*!r-(42%y`n+yZv<2&nh0FxBO5_Xli*!!I7ZwdEwnyTihICqS-^!j!`((=%}4 z`HE$aQ4PU?nFN-zWb@@}%SL0xgCz}D#7n~;@*Obt_ou9lTOy<0B_YP?TBOIq74xYyhm5`jfUSG zENrI9wrVR1r?=>+OY+bZR}7!YA4#m5Gq`6=Be9hxVl~MblHpOP+8L%Cb&CQa3$6?a zF{l_VO}V4Rs1CRz$pm0k_rR#pYt%k7-%rBu&Wu7}l|C>S4x!dBZNxOEUXmbJ0|S=i zZu|**5?tf9+22mw1Q^Pi8bMudGadL=#uN2(V4xwe&VKvbPxFNnGnIh^!sw;$*aly3 zMBQ|=X0Sty>WNp8#q8G{%YoXpw>ks7y=v0!v7V{$t*#9=!oN+M!C9E;Qc&Uc$y$JyYnA-(1~N zqge=y(*9X6E`;y&$QL}Xuj8?v5UlHjYf$c|_FsODEg|b*w5g-oshb|WGVW}~#Dc<5 zEQP(Z1GlwZK-RyjaYL3&L4$bq)GJq@6T3u$;*>w{Tm0jL4N2{&dN|J@+oixW@W4>s4xaq1I;#v z5}o1MM@Sp{-#wG(dv>yTPp6LxG0e)dJlC+?p>j;_q z?Kf*1Ro|8+F;+@dfx0ND7r$t8lK$++MDGE6L{m@hNqsXEx&Tp3i<%-#jDRdx0e-db)s|6Rn~u)=I^1!A!5M_c z+KIG4om(G>y}hove0Oga$E=o#K;P3oHXZPb_FevMrXtk&S%cL~b&A-@UjtP>U7TH2 zjRpULx$KNiVQc#U{|?!g3f?=us^!Zgt+I&pmL0F64jjrZ8P`C+eEDKY&e&OIXczDs z!GgB|fd+R7b_x#VtYuQl@1bJlfJGL@?Kx`~;>TE^3Z0dPzttXGTmj%jS@EFxyq@*j*stNJ{C#>1vd?ZDDRj2wc@kO41g8PEp9NgtJEDy z)@-Dc!K?M4NATVj^vl&45x%0W+nJ9cfRw+ghfaT41zlR4vb#x;tOLfa%T~(N)|`SM z8V$>&9byL1FdB`_gtpQy4x5DBlzyev-RA=iDpY>6oOdD{l6T|TMkt%u(Cu(tHO!Czl=CSl6tw$LoO>E|UMWHAv_(|9q=<0*(sp z>Ihz&6*$nRE0q=yxOJw%P@46~cZIpnRIGMOeGh5fZkoM%^~V182R|8l5weYEhreWH zU02&==-Eq*(WT?)4tTAK`+mNi^?Ny%QIuJ*H!fnxMmQT3*VUxxJ6!jmJ~w;;-gP(e zOr1F2^ml@-x7gz>Zy|?Y1#u0{>t#64icj2oaPQ`IAJ;dpudi+c)~{c`zG%5OH%-i( zy*)DSl{xP=ul=OJIvsum>Tufn3wMO+(#Jit`~vuZ;77aNk$UT;!tZa9cd;w}&Wd5; z8YD=dS7>&j_ajZnfTsrLiyd@PaVxfrjHsM#0^8fUDw%ZG>?WMhG_aKsj!Sa58h*JM zLW}BGC886o@aR6F-4~oY<|c`O`#KC?)(6|(zf1kev#|N1_I&PYd}D##6L;?d>MJas z{D4qc4Un4fn8^kuW=LFNKsDh}`;$I959+L!XA@XQPvE<>mvR!a-dpKDca!OIJNwS% z!>-9Aq2F@mP6#Y01co4v!31fY=2L>@4Oz<8bJ zE#neA#u(%Is^jRLd4n zQ!q3%s!O7v_y!y7cDj5usuA50l?5|3LNzmBB0#xxa@O<#H{%`j!UNb;IrVDLJyXa|dTUI<{p0`R~4}cZag|nTXR_kcrG8UQch{-0D45 z<8m@{84;|t1>q$tX5Qdt6<_c4uZ&%m>qIJ*t@Y*9l}4-lgHKejFs?tn0!xEXy_WL# zWj3f+<#dcTXOjbM2ztU+39nq1pgx+Q&3k)vWi8!~MeV}WN^jCP1Gc1*#FA;(HU^mV zT|DC@3_%WpKMm*+i!%PM&+JFA!muxkYPHq*zmFR`(Z0@CgoqSj%^NJhYP52a&PdHyLClPUs8x9M6CD%iq|Gm#@XjPK`BZTV=Q{$!*2&AARLg9W)uOkjz^^l5euZL0qnHz@YG?W^ zskJ!fYH(p>^!p4kuYz_~^ZqqDyubqfOqW)cFN}pQ;s@PKlSOp|eJv%0O=dvt6*T%{ zFVTkvv*p)~J8u;9{RLXR4?n^Tq$68DYfN7xv_p*Iw%Ee^qz&mkTeZsi7olk%!LsVq zVDZ=r-~@HNGR)wK_6Gy3*=G@!ib;SG-r1_E{A>`p7)`JHd+a77w%%LVeVYV2s2qaU zA92tJoWmUnaAwG6~ZWU{$y*u7{ob1myM0Mi$Z`+F7j>KGAg@Mp61AgnVOSZMS znGUe3J?s8Wd|xClR=0-CxZ+`uXT#c*`L|J4gzjETP5}cR&G%TsHX@%2TqhE0rq!wB z=&|-Eoh|bqrcP3$^WdAZq6As5Tixskjk#R}5$pG;fD(P|wRURLD8=H`4h(kf{|*dN z#P;ZlD3=!z%5b0_U~(@53Ps@{rf6T!4qENk8ibJ@AdKRLrGZ7?z+n{Ig8@BpiwgGn z0)%MrWUaP`?US}MNc;?Y&JjVW5JX$b-WYxEi@_|d7XuSr227k2zSjdJsRM6j z_rcXph@|@Lqxm2H_-9RDT1RowP}uK%y$!^;bLU;V@{6)+xFWjxAQRwH_PPPf7tj@| zcf3kPp2gpvyA#X3I{^412kz&ll<(`aQ=g&g>vzGs3(%G7kNZc%zy$=hzr*#6;J>ZC zIDdV8%Z>cv)2}Lsu=~9p&z{C!?k{Q}D}f1vn@fUw(l%PF*L_N;K-K*JL-|yWW9>!Y z*YYi(l?E?m2#xom&ntfhjeaGlg0BEJ-!s@~6A=u%FC8v)hWufMUe~Z=modKsBmTv} zUvo2{U=1MZr1O*iw8aOg;gNtD_pe-$`!&&ZplSW<87~;6oJpaawK7X1xV5y_x87d2 z@D$l6$ciDePvt>m2#I4o<4C$?u{C1_Q@c zTg@(Tq=B&F z;s7#+RX^Sq-?M>|goQJXOgpydgltfx+aubmTuM2`iCY87R5U(f`45AF2&@}gJ8 zRQ+X1j9N6SszE0+-ZJ`}!GAgjFMD@d4nXHDisLdBlVu>-o7aeo*<2CM!;FG{r{s;h zd{ovmLh%+b45L^zLUSYvMW3vjOL+9XhRQIT=~UTpj_FWWf5dvx6^;t)h4Z*}d@p(& z1GsJTPFw<#d=`4DK_B#!9eAz+^^jwl9wpQDc3&a%IB*`j&Bro}yHn>Ea+IXGTSk zVgGh%-xnNQ|5+X3h5q^5FufCmhxWz(!RY!uWo(u63;uQIA@{B?Zw&~)UF?GDk*V*F zCPHT&bHc7R+!J>AB8&&D&T^CP6UaGm@#e=l3t?t83vH+N zMKt(<@<`rQ5T1a089Dk5BrlPqLS|c?P0G|!XXJ(vV|mKLrUL~tyyv#1|91(p0Ye~O zP$P}!eFVtgh!-Gz4a9-R*43JAmh7}kLKlsLj%%#s;m(4V?=kMBJ+KF2caAcf@NUK$ zPqPg!!=^w(WHWP}{p_NPV{GuNNx zb|b@UIz7&|EWf~XsNgExHSy;st;KQ$>XZr7rgTv;j3fT689Ue80nPw|Py)5XX%sjj z%P zaT-qIhh2!$Fu527zyaA_XFMRe9UdcG#0G`I3#HI~q}!6%@pes`=(++5y; zk%~^`D}rWStb;c5Gew}1(fgaSDE+KTZdN+-97cEWU26t$ym+XsF8pxuHEjUsLOZw1 zRTeAnTj1JN_s-?Zhu(k*>RT+!OElqF7nfp#ZJz$G897TRoGM-H3Dt~C})|H<;|Bi*QPz$OYL z#vSv%#z-U#?Xj=Uk64z^#(x{PUkr0S1SWYC(l;doOj+$n(gT_N8Dq_ukuRtQyIT~_ zG;~X`a^XwKE%{{==I*nFzJz`0E88rsPq;@LB3~Hba0_yg`2&Q_N!Ow=n$K;zb))tW zsYKw8h7okS5QFd%I1z{}xNT11*(8Y80Q$K0T~CB!&XWYtz9>xniCGIF?tJ<(!oqiD zNL0D9ku|LnJwmWjOD`gLwS^!MppRyP6eoPg6vw0;EJ`${QQ6HFn|NO9Byu}n*kpep zaDnk!I{CcynTPo)yzuIUZKn|?off4cJgbe%_9B&BY?C$5tAN@vDII%IF$no=LfEYWU$feRm0CPSlp622wB`qERX{Ww7AjBK{^rjqm%=UI{rM98 zZkL0I1#*|*3%tgT5^MWx#_cT-HqkRXQLC9A#1?*fdf9TtA=$8M8*_-QO0;LZ<48U( zJuQPJ!X*J}P)3Z)ofV#D3nFsc@3Y*XUuTbXmz%R}!oRhgtL~(ZaA~{Vz-^j!3@LkJZZIU^1@~o9QbVAaMjv>FJr;cW)Rm}$5{$RX60ylS2YoX zsPPH8Ys6?D6{Q`T|0yywgJ1*f>e<82oKf7c3s zhbzhky?F}z@o&B;YqrwU%SgR``O@0b7|dQv+Pt~tBy{9bA!s{aU^S!7iSCx`x!I+x zo_}d(a#1{Hwf(aF(@hA_+Vg5s@P#t!*JZD6y3*R4A6;=GoT5AKU57BsK6dwzZDG`R zH~XJ$=~l;WDMj=?&v?5SM{71Jxh}v7?D<>rJmvdL^{nm%e1$t%Tdh0oGyL@)m)6aC z@^Q*VV`)f|i3AY(+SSZxm@bLX`Eg<6l>2?1RoAR#X~Ah>b{zh{9ptt6aro1q)WKP)0$_Mc(k@EY52<>g5|HDTv-P4ag9W zH5^=YpPdPnJloi@0LZ5TbJQ@S-U7}ts21i=5d}DYQUXD(K;BY72UsQK+&>;10|-eN zX3o_Dj6x|OfD7re#AVEM4YdmDRNq?u z128%KuuF$2#%Y1n6x&8bXzZy$sPM6X}otZ79 zx{&hLMl6WFrCbymlyl0;BJO5a?RH~)a#^&*-s`}KMQ;gw+f<&TX871ytO&`kFmVov zr>`*Cc^QzmU()u9Rj?fd-?mo0sHQPdvz^e>r5BgB?moNd`pQ(j>s8$M)01~>;M{*# zFW!TOj2&iPytW7Ll2R)urqsWF{W`bGg>7;x_0BEjeC6G=$>Ak7Cl*2WPx4T@|183= z<6bk!P4MvNc1fz!4Hzg!3DNrMmE7!#U-358l`xQ*0jZQeV;vk`)1xnIIGWftZ-LNU zqIrhdRq4fT~0mMlrf=G+sC*9xTCEw?q_qdTSoms3v*rn1Ni|hqD&kzHsa18ZIS5S)?moM z)Z_mRsr#$!RRyV`G!w?xIxZtYAoS-pJE0vfCD|$j>m|lP`d;lH<5$#a;h@OZ&Exuhga^IdXKDgJ&6^(oChWn~#yf1CIo85~gn_XS|^!s?M*MC&$ zi|3ym_#IjIpMLtK!7_A6X^8FBtCu5qA9pEiXLW5$FRW(}XI($>zOF8HZMnqq*}$8H zZE@cxe4Cs+Q?#i`696qA_nGC--|F-Y>&sCmpt5P6-M`Y$a5IJ34o>{m;Jtji_N`Ly zC+JX)g9x9qa?@u>;t1Ztg|`WM?JuFW-x~&iU|(%SDmcebqPNceyqYzYpx#H_g@g6x zF|V##Vc-j_g>{At{M|DC>90cUsV}mxOMBER*SmJzTD8*d-TsYYd)Fe8eP>)F&6&2& zmx(t@W^ZJ#G+W7fTu z1}`&lnaALaYu`g;v)X#tHVN*X{-UXw%d53+-KZHI%%obhEQG8_l+nEgj+6NtDIWwx zMsQ{=OEnpth@llSB#9|nSh0Ec{(T&9S4jS32=0y>ItC`5#?k&KmS!l5u?_~ zeRLPyAdq&zCr=iw`tmPM83p~D)vioO+o~~weR4b}^lNy*Ak!FR4OPa7c0fn0E0|HZt-)cLi#Unt+U3;~U(o>B>A3cORV>sL5FP~Lj-x6 zin8s~R4;(2PSEcNkL)ZbNRVnm0wsyij{4`UudG1$)1;I9g(ned7 zF9gIDr}WLC#>^zISF>et1(QjJoS;R+gC!-qMYu_m(rx_2EG%a`LJBCcy=OM1!;+&o zVVrsxO(?G9rY^6pv%s@jMH&=*Du0~ep(SLqw2w3$5)65xwVmTQYsTML_#WTQOqZm9 zon22C)b`p%$ZX-I14~*MS+&t-&L?SnI$~FKbWs!ljsrVZ~qeX(1zP6|NMx z)gYLZt)R`%MWU(JES5xJ_F4^9*Lo^Q1)UZ6l(7m5-Ek-qPyUkNCYqJ3pqA55$wLaK z)u^+A@yVmPgsBT(fxMfVsoB50C;`Jq+Ta-syc$dxf^OY7Mmm0O-UfsSs@1@XPq)n* z8B8V2>}q}n6(p}ajIo@KImn6s8b@aG8LgZ7}Tvr^Y>r2=Q&Sjq)7WXw$h zx^sQjO`Faok1iw()xY#(>8mzTMEcsS>)!Q{J~NT?i-6m+LuFy<6F!QLUL%$!c`a9} z4$9SnRbono)`>`QP78%5s^AkBdu%d*iPb22?-R2d6|1{7u&rpX@6Hts>cs4IKnDsk z5jq0Rc@E76dwvAgk$b0S@7zbd4Z0w+P_Zy|G?nP=1HCTtf*S!%{Rs?eU|g$W2w ztv_c#FVYl(hk}8&9M4XUz*{4l8PQWf(GCJY-~cjL|G`gWFQ^JD42+s~NuuFv~gmN3D^d|bXGny_y7uFtGfIK7rcDd6(QW0I!MSQ zF7O?D&=wO<`VOOi(;0oj72+4Xx!0=m7CU4eRPj;0l;#pCiQZMRN%vwO@3`~y;Z8pc zc%#X9G%_z{RzAJ;M>%kf85%Uaw@L7Pvdv!It4mcqJQ=WjHVzru!&I~;NQ+${Q>?pVA%SN7~ulA>bQ5q8^ z_}IU9L-{?DFO+gG=@6=I0(wKls%b_LMJz z=^%U*x!wQz%P*}VfQ%aMwW(!4;fT_ZEq_K&F2P)WduvzVjkmP1s-0^c`N8L5?cku5 zzAhPNDbhO@o9D)ak}gNs1Gd+Drku84P8OGdiFOvYp8pa&F74#r)3)eGwl!oHFAF-B zN61T+mVV2P0Zm`lx~2R$ce=wNNy;M)b{0Yj(5J6bVzJep`X;l=#a^a~uHq>aW< z`4-JL`qH9j8PJb@koa1IQz3Jg*H{A)TEX0Q0y5(ZsB9e;FQ!l#njY(SX9!H z@le8X>yz^fYHOlQPe{yxxWl=5OX@9_4hfEXi)UkO{t^ZqN%47bF8$eZ6#$m^sNdZ} z0_dXvHw+29*nyyDpTX6R40BHMv4i65(JFT7sZi zFQaUT*lD>T;IiC?eH4+?y3vSL&?{5*2a)LOhZ z!Gx}7Rg~Y6y?gb^hr1wR?bA>2uCy}~mS51@@0Pv%y*wJNIGpw~S1kP!!3Bn;5%{)h zUoo!2Yo^#^LHJqV>>YsiGhplGckW-XB`$6rZ2t7cOZ(kdFUD>b)lDrgdwCqf6q+49 z37e@LOd=w4;o zy&2s;s~VI?6JM-rfD;_k+40eI?y}|=z*RPJhuJjDqNGV5pG@>w*|1TwxwP!lq6I{X zM?_cbd-jOMLP=h`wm_x^SBU9SODMAd4EuJS{5l6ahAFi&~fwhD?n40(zq`q4OQleaSywus=s5i~n?S~)g< zm@;zspN9rS^ANF%f^=YK{87FJgH4xH>;dA=xToL({w<_}KE#Z9H=KtPkb%S%xFl`| zPF(gS{S@d^wnvinX%`sG^h~2!USBjX&{8D!l!YRy8c&_lLBqQuh&Oh%bkhJUZl!NEl57K4wKHBB5_2&n(X%O6fhk2VqG$n_ zWRSx0pbH6-JOo7+1unDFjTUKxPL<9?EEa_4?_(7F+uwa|KmX-#N@<_H)SyTo%{xVt zS+~FIb>TppftUWCsou5J`Nx2sa&)Q+lWEr$f4$<)@3&Dvq4RgB5`TB1`qd3L9n{xt zf408reYz4rN7*0EQ1tu+x~ndq&FQn(v&G+qJp{s@zfuopLfP%dzxgTyv~$H*FJEM) z>-mO50(h0?8Ao^zJlDE!`Th2*2RO?1Q)5>s#JwJ)eYDG;t-&%aIg$3;?=}aYrzkk@ zt?+cP&p<|VWYU+|^5dT|6X09-RUXZE(C#Nln$T;wx!-m1GsxRy(6GYWanX!&w0kwU zP@Blwycdl+>oQY+2Rjy*GT^x;T>T?rtkUAW)}r z!*U$a?dY5*k}W($BG5_(T>U0vGh-ZAtxdePBk6q%RuFR;;xXZ^=)#6N(COo|7dl z6Zaop_Mf)>v#(y+Z@zpv0yt>Fm}a&k<9-X=Z&Tq$b6j3?CW>A9b=t;BmJmtqncPuY z_UpdZ&wic6Cd$5_9~PdWji1TB^k5C2YZ>pw_3K)^4WeD7${};*OOQa@HU!W?e~Vtk zq_Iiw5Ln4(|E~6GM-5;115aBgGn`m*iM}wf7%>DAdf~MUvxJ3u!`;r{FA@(*zHCNd zDr1EyC@`fANjYQDDj5RfXf8djYqE_VxqApr7#aLrHfq7&K|UE=(YQA3ZEiyEP~TzbqK&{+GV((PZ^^`C)*dS|SPf-v<#z<@G0)85 z5)ly!EGr3xNp$nL|CCE_j$23OzM6o)n>?PdOyH?UZrqZ)V;cRZtPmI{Ey1=s&Jik~ zNq-o1PR~FG+hm8;GHB}20fXmY!epQ@;n*k`8OZg+rE`=7@fY14!J}b)kDnP~1sK=f z+14x%5a-LQnA#wnJhM~25OKMh<#cNfG3(YjI;w$HckqnLE+ZWYcxTIQ8o#cC`*jMh z3>@$+WmI|v8VqUIQs@=UW7GZ|Y@jAiw$nH8A=uRILU;~3h(VRpnm{|iT zs{BMDOpqmyT>^Z<s_J#7^nYMjbk@ zGn8~j$zVFtT;C94glbSW8x(VQsp_iV{7K-6CTS!-`p(rKxYlX7Xv6VLJ8FC8w==`; z0MlfljJk;6{DfKZs?^7Qu|TG<+>@`k56@lNS!?&qw`tc8j<6~_vQ zJuwgxMPikBw51@izz<(<)@*$nyi5=)9?0j^Ai1X$^47T%`xQbOiKz*;ltmhYLgf`)9r#z!pjv32* zGR9Dt-Ro4=!Mmrhrf$j8q&Ani!TE zGFTNAfmEh*FyKw_PJ-sv+M+O_$mS;lqDRu|iJodxSDS+XBdE0AVDEM=IxelXSR8CZ zz8DUgWOsRPtNAy5F;xc)(ScWz0|9+(+WI&xH_jOpqAB;VEvUB^guWr6VX{)Q)S7uP z5zrOXsuG>QE2GLTBm^ch9jz}YBV-_~GDyAFH1dnm&oSTc)VnHst`S#?XwQj}dV_Zow{xj`LUaN*mR|kz^w6R-2Kej;62k+|Pi2EunKuduhkUi+_D1xc$`7xksqrGzT) zLgOmMA@9drAyJKq-Cud5?T4gRm$=~gwTa$Wglmj1F#t8z?|woxpUbhrPELZm1cLT^ zw;5;JC@Z#$vK710Rgwag5FH0H*^H$T?~c_I4_k{bbWnh$VjF_cO-3HN#iLyYp)vNa zCIKA)f!=lHdqZEvL?)a?uIajIi29P6^Jnl=>#`=`3{jJZ{z* ztc{mxy%@|{gs52syrjJ6?k29>Uz8b&s#CSde6%%8Hc#*>?5j3eoiDorf6tdZwyg*3 zlO{fRGeM73f>hA;EyJ|<%Bvwv9t$4z26d5f{`Z8prQIa}Mw$G*)eE&)gIKra?T7Y|P@S42Nt=9<>D&F_xE2QXclyhsXCG?XUj!C-(K%FWw#u(#UuD z=vU`6cwZ9pd=0fz)$Nr#5&3bGz#E^H>kqqz?Kz0ey78n8FYq<>jcCpe0a&PtDi$4aU2Y&^5^>=_&X+HL zrwR*-d47BswtK9LEPut{IDHX-U>)zsreyPmKVJNGS-qqbOrD8vu=3&6aDCslHN$RF zH(I3$a%pa4xyvPz+8Kk@o_J%wu>^YxV%F2Ozpy1UyinZcP0#_^;*1|f13{m?C`dcz zvcBD#jIxg{Dp zt|(Ww4)9YtEp5b28s z<%x}_cFi~D-DE96(Jaf>v~V!sSDdFLl|vhhPXmINz>1e4hYBqHSs6x}46i>JwTFiX zV|G6K!zpP-Iz-`M##0HAk!Mys4XF#2rgNEZsw-d6ItN=CGzRJwoTL#An44b;S2wG4 z4mP(S`~DApEQ6E<9D%FfSk+z(%mTwjxO!bQuAjx7ej2VxvtHe$hFf?4UPixLVEIpI z0U8UR)gkqPCPi_9Kbj1zSGN(-bgo*kCWlwtzIXTj{i`c3>U6VzmX^5F##*OrtghM^ zB`(C|?J%z2*E0TYRoZWFA8ASpHNf#qZ!-K-5obKwBE`Q~xU8ak_t{7w6~4A7(NcGcNYp z)%>NR#FD{g^5m!=A&0JY(rgm+mZcCW2lHo^s_bQ$a6@Ig>(ZcxI}SqMW_6)IuKpR;|t!vq5Np1EX;-xq$0kGj9DagSw4PWlWJBp%eJ;$UJ`rl z`m*QyaGS;j(u8TOKp<|vbH^4Xx+ci*Zi9?*E={1zo#Vf4ER8Ar2JA^9?!M!5Z~Fve zF74T4Y?aFxIdTBL%YZ0*+^{20+LCSLeDkFNfP%oZ&KZJ%+-U?j!U2*Ff?3^w)81=O zSY+Huffgu7jsB#}Nm)&+m(W-WMK7F+Ka`;i*_52ptXeY$`sA9T90gG}X!*oxD5r&9 z?R(U=jJjz*X{>|sN ztottf4tsgaxT{@daBfP;Y8NjouG9zME`s9=C=pAYP@69FXT`I9P#55S2}H{OqLf~J zvil2>P}_EjNzUl@x(V3M8UJCb92?3Af>sBWYTL{<7sN+cg_+%??xMfs+-T3f791 z1|9I??>4tvKF)lZS?Bp4WJL9czBO1?p3nGjCmn~ZWPCsXUEj|r*vhM0wQp7jFG)Y! zCNw?%-!gV`s~1A;Lhrz0<@F?V5^}C(*O(dGJI(~TQ+_RVV|&lm=Vwzz`zJIz zP2uQVR?KMzH4f3Nw{xn+-P&l&S3}z+oA_-Bk0+6_+=yLfUq`z;i604zG?|R;icyA# zM(klL!*;=JebQ1EEsmEVGAsSV4NH z>)q(_PQ@0|blRWgiowC{@6#_|+b=%-ayn0&w&K`X#{FU1Gt^NVYM^K|ohjbI8k)7G z7WO>&1>ZA*ia<~B($cXKfc2sT7JLz5;-o$W8@ zm5(}gn_{jeVUwLg=su)PWZTgz>@*BM+!Zp(m9fvR1rOSW`9t7fr*kRmUP1kU5dm#? zx}~Lp8(Wj$w#;DG6Zj%lgIhL}ehfY%`q?j7n!J#DS}-gm=bLX;5{*mwee1%YITm0*5w{ z9y2+}03L&Y+t&wwFfviv@X`p{r%4-o_ChRF|JD6E?G#DLnWWqiE14t~Az!hy?XYGO z)~Bb!6@1%FjHLmYnBdImvYd+AE1TLzGTD$fBUEp}k}{5<*hS!T?@rhfZr_zmGp7JT z9Bt8&Ryt3bD#K)BDuUYzAz(Hm$z7Z+2N^b}+ID?;6#~2OgoVMH2rdR5j(}Yanv3it zehfHGodBGVGjPJJ1$muVCTMi~>XXR~I2Ayac86EwiU$6Hhlto9n=LlY z{vNpWtJlJ3EO%F$`>D5CO5_asd@^Ejk_R%PiG&Nj1w#u4d&A3b4J0nEu(w~Uo2QokZmsny9s*15{^I}#u8&PuW-Hz+o`OlZg4} zo9I^Js~ZA4Eu96-y3`Q-E$iTo=v=;%$k4Bu(Whdt;hXN|zR=C$42oIba?hQtteM>@ zW!O%d)b0`2NeRhk?af_ix9YcB1w$q_+;i!R`u)j>&L`*6*VACaibm43b&phA^HOPB z3}+YkmQS{Q&og1vruXGG)MW#G(;mLLW!1S0jF~2oSFy5)E`L1aGiAUe4iua#!(YQbr{$|4oklqW+0o^0`I<15Nk`j-v$YYoyN;w{Om%H8?*MphiHLPy zpcki-@iK3eJDb^-jnit_ryXz=6IJwrhNae6dA*bxwT5jNUzea-`DSzA<9Wl|K+d7h z-2iVJ>4i1zqYoLS!W8wvLr$%Gty@%6#(q<_3#&58<&7_lW-{=LTc-Ul|LSk;@$n&N zI80ixdjIU%OZpnPpS6TjvuBfUB{ltDE%WY@=CBw_v9^b zb7@RvgbS`uf>V(6?%0kNy!}Evo(QPxHIWpbhvzoxJSX zx?Cmzn`{+`xC1J7qCI3v+p?~2>dO}5m=2(t!GcwA_@T&T=tgS`TiSd=b}U_(&Uo); z;$w|4!~X(TWmqL;gpb}0KuFN*+}bgT-)P(EkmO~&e!Ab%nW<6&zND-YR_cy@%*q| zPl(ZQVyYeQ4!XS=OW3h`!SXDniQ8i-$ix~)zs{u%Nq7p5osb66#)Wq)MbLILIXEGU z?Gj9FosdOX%>%Af)VpCgg)NksW~S2(Xq%D_0fvEtvDkFYUJ0;{cBk; zm(%Iy^T*q0_oqMqjs5o1FHEz*!ETdsU9^_V+sFKVD#QXJ8Ld5)vEHRf(`Ennt}5(m z)J^bO?vCHhnM7GYn>J*Ba{JK_er*4n|MkDH|Lo8H)IR&-3;XN8{jvSzXFs=BFW%UD zAG~KDe(-_4cgxJbcgrX|JkD(Qk@fJ$NBi&o`+sA<`|W28HZV2YcQ(pTq3M`f-KH}* z0(H;bqc-0J&S})&Jjp)4LWiULQ+WaHNhnd7omi-8Y%{Eh?5PzImaB$DZHl~MW@q*i zR6Ny+)xu@nE8E_{c}AYOC9DL*T6A9=9LoOU^RMkszV{9L=y4kdFul3`J-qj5eeG!R zU}{4B+$qwwQXyKr`E&?-XA2%K`LgLT=r-K@_Xx!APZ ze~)0~mKo22EoA+-2rh@V#UD;(ktdK7K=w4t`&jKZpZ6Sjfj;Y zhmwJbT)M#*7=?MG0jxh011Sa`uyKp|C|KI=M{Xp~?)$z(a z7~$x0F-PW`^>9H0Drfu6A%!K)Hu7PIA?Lrw-4%ZLqo0noW)F|;1S-4K@{@P>C64mm zhGZ?5CkaEfQ^9$)wDjJM=sR3(#>Lb;f%@LuP8nWn&O+7hFa>}8w?DN1@?ZSZ+rVeD zkKTWVE!ad2#@5Rh^zhxU%CsM1pOelrK_GzfY=kVSOiEpN`TEUm7VD*b^xj7!<8|a5fNA!-DI{V6nxYB4#14p`@Jj{iL~vhXLEpxajJfzbGwA8XV5~- zOX z$Z}kBt0a+2QUQsfY!rjJ3a(Yh0A^*ZciCZ1XE3JE@YG{(A{C-vI`Gq#2{LpSmIm?Y z*2G&6ni!ekRq5O8(@nRTVr;_(W}ABW=*~73ASVx?o8z&NOLZALcyU>aYvx@vBHQOd zLJHq;Z&O;sfb288%-_17`h2N5Cak9oIE;M<;?TU8!^7go&4+6unFHSNCFEK(5B!3h zA%@%c<~1wMcE-hMZK~Pq2LsvK9qSfna3`T))_%M%aAcyA78uw)&dT)?1lLWaf*|nf z!b~n2m!{~*;AmreD4YW$i8(EBfQRWE3ZfUmnyBAdK@v;8UT-VEs!Pv`c9)7hm#@nSvt+(28Dx8lpl$Zro+&JZ&A zqL7nTQ|PC>O(Q+^z8Jun4uzQmuoz1Wj$3;!ZK8-}rODG8;)AD(uNO z4rXXjqZ@1pLRfP?(^=r8GEA|{WAbU^G-X(~Wgf-X7cXAg-+ceaW0{O@q1H{Uw(aEk zR5l^32%@L1G#kr?;!Gee_S(HndtnBGZi&p4-k2<oWrvVk@kgl%*zS7=#`Cf0Hj{4LaW*9X$H*gZJ%|k3Y0;edA;M_8)!M{`AlO z#6JGUH|@Rm-WwfQ1{7~6}xG0^h$ZQt^3llNucGfg9zCCjke8*H|s8CxuY6wB_U*KXiOk;V?1BA1V1fA0{X$Yd|(}IiQ2uZi4 zK{8o3Gf0zyNU-v4{5kso!K~#z8wNQF_mQOkM5ZGUq?c`mztf;dpV;k8Jx-?027jK9xwefjx<(%LBQw3^jyqfYYtA zCpB0>U6C%U%nzs`bTwhWlEg@wf>&ASC(vxg&d-u+%qk2N#D17@(ER`WZ~u+`C;#{# z*dKoPn+Zd{{E0ny(FTK$Al!$Yc;M9W-uUBU9>$C0uin_}+l}`gW&jWRD(-*{u3HC` zk7GdbAcxRdIY7C}d5F&NnU#^YIbefLaKZx|>U+`J@3Z6La=*HHmdmoK-9*#o($SRQ zs6+LrWkJ~KY)s=G=b0Q!l5R?J(C@wX9_{_x-$DOxf9qTJjc^tB7mVNNS2P23dJicYX#_(obd3Ju}v2RSSyXdfW=QBXxW3 zTr+nA30q9=sNFN!R!e7&?)Tte*Am8)9b08pbaIzq3-43yAt5Jp4l6R-lAKOc`cl#9 zFk56UEF>ac=M|bQ(0Yr(WjVkR=-cb~6%n3jQc{Bq(qhmulT;0&Zl4jvA}G}Nt5!Rv zxF0ZWN-SMA6eSZby@FODJ{q`q8RH=z*lyce5I*niLho97>kEye;+cp>hbBKt7^|6p z(oJtx;odc5iMvEe+?jaMABMG9d8$~{t~oKA;LJk;T^oy7+2@mt#cj6O>P13Tow=-G zR+=M-y^S~v5iz33=7wy?Bm~)U0vPIuGsS3Kon<%AGG=X-Z4yvb30$H3NrIJAH3knA z2C8CBeCIbO%nIk8LU>vxK&o3Y(B>egT_Vf~65-sj-#SjvYS}_43j*wn3IaHwysb`p zrDhGh!c=LDu&TqKv?;@DQHs$~ej0=w@^(y~Gw?F=qKem7TkN&>#G;1E~z1!Z z>fxdrGNRZep|d848V7JM{hfew`c7ZM5rcK3JDu#LPDo;l2HG+28S$fW-VK7GJsd2 zM znYb(^S=dDIK<`1O8L0je1kbq%leM;1YXjx&_F2ynZ#3gOvlbhbZby)2&eO|6arb47 zG{`F7$L}0`_=+nTj31U+g&^}O-s2e#1Ta$;g5QT>J%ZCj#vl)XX%5`-I9>$*akgJ| zs<+F-=$+CDsPm{W6v4_Iq1GGLs;=E3l>ux68=HK@3m{My(2H{C!c8h;v_qU;zkXvs z{_)R7Rx!j5u133vQG8>O3qyHoL?0Olsu<6Qv3v&# zSk}S#;vSBzbGDT?#7{N=y=2_1V-@~pM&XZEwT_qnfee;)B*oBMs|oZYNt%lc)j8x8Kf@F_fFClD)MKx2+;qLG&|ptlu@S3x3I;c=i={Rg-}8SU~2BrX>chUne+I ziBSuj`x;K!Th-g8ly&HB$96^ToEO>-bns^t&KQQ_?GKC~q$%bCr6nhfVJ!{C&}wLj zhB*%oy?P)1Rg6evj~6nfkp=JtAf@w@A&yo}4OrT|_r(?bM9}Yi=X>({8*iU7h(5o4 zdcHvV$x?WUKMZ!)0;ehf=ZlW-hBoNt9o^I}^%-vWPW$8fSHfvF52FmPh zqJRhcijSRn+B%X&aGGEg*9f28Z_$-AsoE})+?&YYY*1@qj59#r zgrzcRwfx8&EuLwHuy#j`J?8gx2iYbU)0h|US%ZZgk28mZuqcWY7fKtwG|(YfEy60A z%^`(ydUT_v*<&ZBNzSJd$3=Y$wSi2hoYPsS9&H*k4ZJNDh|YC+<5hG1B+*_8pHW^u zFCrAMQgPW(>KTjhflN7#ARmX)g?XIrOT}!ylw=S4MS+rztl^jc_GO@yk@KejNxNQTDm!ysp-cagvRtG|_h z`cMCfJagxkC)8G!`qYL{o~a(KR9H#3q7($!THDSB9;QYMr7Qzb>I_|6 z6+KTfl8xPaLiaVzxXtgW?CNmk1Jfz9;srHhTE#ylI0b!5@TUW>uump=V2cpjPz@IG z_Z>rq4Zu`@VaF$oqX{ro5$76}>W09p!WssuT4!!q8c!R_CS)F-nve`Sqrgt`4 zXhNMJndBua)$;aU1IG9C1y#=DrHf<3}rGgu_5zQd=c6fqUE*>zoWqxZBXWIu*3ZS zDm`P(^nm_=r^6g%AY0l+;%~9yR%9OW3;KzAe5TA2$&nP) zJO0g45-BlH@|x-^8=#2fKJci4ZIPqzc;VR|*ZDTr@cXHoqrK4wjbv=4RglK&ROops zbIOrMayI2vBxfbKEIO}qBsmB_#YlrzV>Sx5#&j80iB56X&;D9nn<>#wH^$Cq$?e|L z0EDLc>8BX(8AA?6LAy^8QHe!fMvO#5!BGUM=-u~^%Y_10l?dHgBa0Ozif}+1h6{`_ za#qD!MwyXikRe!OzEOTk>_CH_Ylx{Eu{2IHIZX6~v&XBj3h`c5ULh;VMgszy)+2*< zr=w8VM&xUMi|_1#mr-{VW@&T+`JkV~VO$z?TSy+V<4}&o5@BYA;Kmzd8augN}NU% zL`NM?zc{BsBzO_+>~V+Vx}=f8`#C*XVyB@|#u({?tpc5yH_EYq8e)7L7bh86KBedQo0dyIYZ$C?W=$B4q1o8J#I!nQ<8 z49_MM4ULfqNx|;r+ZuR6SkXZV$D~yh@LUF}xTXt7}>S7Cn(#eSlHG|8sY2p zt$YuQ+kCg6;ty@igKLK_egD-r(rA_lfu5JjhmBrnK}xofULh)dH7bII9{6Hf8NDOJ z0B}ZGCejqT&-W($i8OS7@4dXgkrQvrU;O1?%l&)z-AVf>Ml@?i$fBennl@peB%nhM zY(ffwpRj{rT1`AuuXPWMOo#4VFIDrkvvfk8Gl9+Rt88Q}JEn7IW?P975=OsEXHPh| z$*fugd0ZZ~AI=ik%#WuHO9wkth(}3uOR}&x$+Ob^FlteGCh-cQb7IA=VS;TC-~SDJ zYdeolc5G8_qLeCWd#~=xo}9rO_Kz;bKN0si{jm@bHon>*r3H0N#~l64<6CU9c@ng4 zzZc9~_fe7T5ae4p_6$=layrcz<%GQm+CbGc@j;6@i3UFuofq<(fiLJzs$))*61S*i zIZ`_TqpsX0(@@}MXCBts)K?=y7qJN+EELqjl546*JY?0L5&F@Q0Pf`UXDKtH8)|~y zATyj(P)QHN75JN6zhJNO7+(`?^*VxjjV9x8JqulSU+)JOnIa%xgQhy`P{?4Yn_w!Q z#WQQ@OgyU^I}`yQX39n*m9M3ZNwI8qmyo^DS!lm6!e(LQC-!D=eycC=UzQ7rlZJ)2 zm-H*GgBu8d1t20C{|?hh&kTTPTLL!hkLMr=msodWI=$LVcmK|2n- zAvNasg6`29wyaKfoPfoj5k);C62ZN?a$7NG^-wKpmGOSpqnA78)LS@+KJ0@e4~&f? zv*{d4RfPU_KNBDiN{VKN@-Mjv2DDP=iudGWnpVihkoq)8l%uvWGV$fV`C6Zojk$pv zdNO~ZRE5yhu*P#cWwEiS*1W4O@eXve2#Bh^%(h<#A)a0UXuBgeY5Ddn{_f+p^PI{z zzx8c-^|jaB@xZ9B5?v~HWHzZhoaVh>!JJlnLglU=-wW#p(|Q_tm`2t>wns}qkVPjJUwP46}T#ETq z_gyO`T9k|^%4iADwy}ZvPJ{v(_0Mqv{ko&Rq)j%rAir?X9~egLP%UhQ*#Mw3V-aVV zS>WAcwf_7X9T`aZkY^+i$NiT4WZ&51Zl0^}b?2lB7xGR-ACBl`XCV%}tD=I>J;0&8 zTQhL+#-Ios9;7+6H_AiG6$Yu0N{EAa%rB8+bz#rS@Q;<>5%@D>-M|b3NK-IYw9(n= zy(82ci*me2DNc6tHs;U|jF`7Ef^QlXP%TQ{0E;Yuo1l&2LuxJ*mR@rO zPxBGl9Q+F4z}&Jk#6wgdZg&K$QSVT)V_*{7Ge&vTZd~)Ca3|Ec%x4rW0g6g0+rXWs zHA$Ampw1AyYl)`3d-py0+RNXT$Q;QTh)yeN-#n0Qybc(ND)6!?w|E-8wzz;QQuUth zKXd*Ck@Ex*+3hr*X!zD!@5;Bn{fhryQ-)bzp@>vU^3d`x6gG8#+)AF8u)t92+@MJZ z3Ndh1MO8|0rsvwv_AorGP~cS5u)KEedsan;PclO`QT`OI)bC?%ZlAbnkM#JACpw4>XClTOUkp~TY&Iv|zeq4{jrw0jkm`~SJ4Xd{G zOPKOukY0M-aztGzo)DrNc2vSkd0j%5d3qn?+}p8cTY?NEL1|Ylw{aEbCCiXW>0lU@ z8gbd7kAzK0ZWYSb=N@Ffe9hqyTXzFf$9_;ATP!R6(-`8pN{wOR&vlvMhkSGU#CAbzhx=y3CN+4XP*M;wx6DOjI zNe%3zY_|j?0&m$GaB~_cc)|2^gEMxX892mzcAp#G_jYz!=>0}$a9x5eSQy28;Wie9 zfdb8eglf_2E{idTlO78tAjhd(Q}XNbMAL74^A-8Q4__~GR1h*UG;nol^?5d?92KOc zq-S^P&@^JB9OrLwy6H)3D{yx*N&81`MMSfk~}(Dn%!ZEOn2BzPvlX7>{VGBiZ42J~cfJJN1dvmZ_8`vLhw7R)?Zn zybxHhLr`IutS2dBj@zOt|^Q7Z)YDpkI-?1% z8-=_?=y9h@$Wl@k>-4Y&u~+L`DUQzLk~T?9=82T(XrSE1ZAJ0bvT3lZLE?c} zHfW9+N-iZ;nWYY2yodwKIibe}HQEJg9os4vdeBxmCS)XF*kHXocF21I*ts^HzeFRw| zpzj=U0T5=QNT@Lc$tDLf8EY7-E#7jD ztZ5v@X}mxfc)$35;VS75Dw2_lEPxwf&@Qsu<~&6p$uw}B)45~@2!dZb5$1eOr@}P{-{)u%Ku>*m4h+||}=Q31^F_Jwwv+1|Uw(ZwlH;Mh+hnQZ-*T4B~dHt<-Mz2fqiWP;iZARHC?a2mhU<1vcV9HNp|319hVC`)E4R zGSqM4!yMc*zUUw%&N(%qV#YX{Fm*)q8tMlYVLRiu)a;~MU7)_nkZYb+y@n-J84FnJ zni!=B54Ob!*DBl2s6cI$sgq8XAG#q7QHb8rGdDLnz>X$Ne>Ean%X|>c2kolTc3V5| z!Xg=%v47;wUFb;o1FY}L0LC4ei0BlY=833b$8q2x$#LonwpAus82d*jt0J(OT5Q~M zB^pS4OBQ634RJvDSTYq>o|Z91A?ltLQDvI~5@G&EU6Q(3Rdgbr<{imWq;y0*7)*ah zortJ2ZOfo@?6u=lGiZdveRP>7TAWVOkf-(pl}yTp9!kDvyqAe2KBp#puEzpx-C&>g zO4FTG)A=Y-QHRKL+p%oVS@2p{O+PlAcx*gBhc7?H5G;h zLBjw~22hu1B=3c6hxRoLx*v@CBy3Q&+OCWbP6Sy6z_wl2YB(k^2Tf_0vON`Jt z+{jp#YY+N9mqAFQiH+}Xz4NYo=eyr?+72R_!noIV2b#v!Wp>C&0z^_5P?k!DwL|pX zJ{jN1dN_!+$q0vzh+HodvyIx^CO)x@=xpI+qOWF|x?d}2q*AY}Ji~PuG0Ccw#ZzTx zO?Et!(xZa_Ta?acDp5(vg}v;J@NX9ONpS@QyN-%Hn)<0==3?$Ykp>n;Z6a9 zPLAU`8wIoa_!;1Kg4Vq)@JXYB$1hSF+lJca%ef~k8&fRltD)&I+(wM_5r`l+L9ljB;t5M!xt9+LJf#Rj&$;i zl(0&yw(`F1ZdAv>{!fYQ6d5^R)BY^edJ?o{d}xu|4Du~}T*g;YYMEu-gy=*#A{nE^ zH~3`^f|cSobQ*G_2@5$i_JhHzl-J&V+O5%;e;tLfW0fuxqpUt<%W1Vbo#E5bxs}x- zj#*eX1Trs^8%{)9VmJm#o;0{MEg>SZZYb-i7H-45xu<;H~ z&SL+?bLWDfA>25`fQ$nT_X?EG4RRJc@_X$-iM|U;uKJbYI7o>wRV5ZtN@X0f8lmtJ z{frMBE+i_^-okzKXqP-hN`WhSuKsQfoJm1h2|*LffFKV&fI)HfXP)?I0_u!InJg8k zu-4L0jm5rv&)f)jFO;$d8eV|rWWqizH1rnj$&nbQS)

-~6t8@B6P!4sFsUH-fXV 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%0Da<19eEs=%u7&k-7B&Ln))q;ULn|VU!jI|U? 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 z zcsQNFQ9qGAKhvT&DRMms{hb%SV5QE<6>naL?GF9>D=T-?wHJQDrzTC^l3o8gNqP0v zk_$n%wM^fLhng|!E^z{Ou|8>;#5%Ioy|VoGo%7CSM_|TT;SQ`4&*$u@c0T)cyXcoy zYLV7;+pJFa-Sm2DFj-vsa;@`3=Cbr-Ebsg*x$S>WDv~^V#gOIQP0&d5k)@EE(Pq79 z^wO=Ivy`Fn+0?ulvu5qs_~PbQ4P(86@0uO=znwTXea6a5-OcrO#}!P0mkKgm*tzQ9 zucuQ(In$R&%I^3r2i&Lf_e_&Q#e4&O!=;nToUM*2n1R(ff-3L2&8t_J9QM&{?o7VM z@-ESA!hCs__mdqB?u)W~3N9U0c^k{C#LZ>} z>z{6He{X%Pgw=5m)6dQai+6tkZW;T{YF{K@G-Iagu3fv&HXWA*-KB1{W#gZdY!7cQ xTehsJ-an6DslPWi_Sdf8e6{u4*j@(yvtRT!^Gq(s;VPh722WQ%mvv4FO#rGhwHN>Z literal 0 HcmV?d00001 From 7cead4a621058858a51fdfa1a43ea2abc2c0bee2 Mon Sep 17 00:00:00 2001 From: Roy Date: Mon, 2 Nov 2026 22:16:34 +0900 Subject: [PATCH 04/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=ED=88=AC?= =?UTF-8?q?=ED=91=9C=C2=B7=EC=B2=AD=EC=B7=A8=C2=B7=EC=9D=98=EA=B2=AC=C2=B7?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=ED=99=94=EB=A9=B4=20Figma=20=EC=A0=95?= =?UTF-8?q?=ED=95=A9=EC=84=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사전·최종투표: 카테고리·댓글 수 배지, 선택지 카드 색·테두리·간격, 최종투표 배경과 위치를 Figma 기준으로 맞춤 - TTS 청취: 말풍선 색·여백·글꼴, 화자 이름 색, 진행바 두께·손잡이 크기를 시안대로 조정 - 의견: 상단 배경, 탭·정렬 칩, 카드 스타일과 작성 시각(N분 전) 표시를 반영 - 결과: 인사이트·참여 현황·입장 분포 카드 수치와 주요 의견 배지·인용 표시를 반영 - 문구는 Android 기준으로 통일(생각이 동일해요, 댓글 작성율, 의견을 남겨보세요...) - 작성 시각·인용 표시 테스트 추가, 의견 변화 문구 기대값 갱신 --- .../Model/ClassPerspectiveRow.swift | 3 + .../Reducer/ClassBattleFlowFeature.swift | 2 +- .../View/ClassBattleFlowView.swift | 218 ++++++++++-------- .../Model/ClassReportHighlightRow.swift | 2 + .../ClassReport/View/ClassReportView.swift | 65 +++--- .../Tests/Sources/ClassBattleFlowTests.swift | 23 +- .../Tests/Sources/ClassReportTests.swift | 7 + 7 files changed, 193 insertions(+), 127 deletions(-) diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveRow.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveRow.swift index 29d9293d..8c212dad 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveRow.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveRow.swift @@ -1,4 +1,5 @@ import ClassDomainInterface +import PickeCoreUtility public struct ClassPerspectiveRow: Equatable, Identifiable { public let id: Int @@ -6,6 +7,8 @@ public struct ClassPerspectiveRow: Equatable, Identifiable { public let likeCountLabel: String public let commentCountLabel: String + public var createdAtLabel: String { perspective.createdAt.relativeKoreanString } + public init( id: Int, perspective: ClassPerspectiveItem, diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift index 1dcb5db0..7b4dd032 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift @@ -255,7 +255,7 @@ public struct ClassBattleFlowFeature { ?? voteHistory.postVote.map({ $0.optionID != voteHistory.preVote.optionID }) else { return nil } - return changed ? "생각이 바뀌었어요" : "처음 생각을 유지했어요" + return changed ? "생각이 바뀌었어요" : "생각이 동일해요" } public var displayArtworkURL: URL? { diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift index 24a7b991..c582482b 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift @@ -44,7 +44,7 @@ public struct ClassBattleFlowView: View { private extension ClassBattleFlowView { var isFinalVote: Bool { store.step == .some(.postVote) } var isPreVoteLanding: Bool { store.step == .some(.completed) && store.showsPreVoteLanding } - var voteBackground: Color { isFinalVote ? .black : .white } + var voteBackground: Color { isFinalVote ? .gray900 : .white } @ViewBuilder func loadingScreen() -> some View { @@ -93,13 +93,13 @@ private extension ClassBattleFlowView { height: isPreVoteLanding ? max(420, min(510, proxy.size.height * 0.63)) : isFinalVote - ? max(370, min(446, proxy.size.height * 0.56)) + ? max(330, min(405, proxy.size.height * 0.48 + 15)) : max(315, min(390, proxy.size.height * 0.48)) ) voteIntroduction() if !isPreVoteLanding { optionSection() - .padding(.top, isFinalVote ? 36 : 40) + .padding(.top, 40) } } .padding(.horizontal, 16) @@ -182,24 +182,14 @@ private extension ClassBattleFlowView { @ViewBuilder func voteIntroduction() -> some View { - VStack(alignment: .leading, spacing: 10) { + VStack(alignment: .leading, spacing: 0) { HStack(spacing: 8) { if let category = store.voteCategoryLabel { - Text(category) - .pretendardFont(.medium11) - .foregroundStyle(isFinalVote ? .beige200 : .primary500) - .padding(.horizontal, 4) - .padding(.vertical, 2) - .background(isFinalVote ? Color.clear : .beige200) + voteCategoryBadge(category) } Spacer(minLength: 0) if !isFinalVote, let commentCount = store.voteCommentCountLabel { - Label(commentCount, systemImage: "bubble.right") - .pretendardFont(.medium11) - .foregroundStyle(.gray500) - .padding(.horizontal, 5) - .padding(.vertical, 3) - .overlay(Rectangle().stroke(.beige500, lineWidth: 1)) + voteCommentCountBadge(commentCount) } } @@ -207,14 +197,48 @@ private extension ClassBattleFlowView { .pretendardFont(.bold24) .foregroundStyle(isFinalVote ? .white : .gray800) .fixedSize(horizontal: false, vertical: true) + .padding(.top, 20) Text(store.displaySummary) .pretendardFont(.bodyMedium) - .foregroundStyle(isFinalVote ? .beige200 : .gray500) + .foregroundStyle(isFinalVote ? .gray300 : .gray400) .fixedSize(horizontal: false, vertical: true) + .padding(.top, 12) } .frame(maxWidth: .infinity, alignment: .leading) } + func voteCategoryBadge(_ category: String) -> some View { + Text(category) + .pretendardFont(.semiBold12) + .foregroundStyle(.badgeFilledText) + .lineLimit(1) + .frame(height: 16) + .padding(.horizontal, 4) + .padding(.vertical, 2) + .background(.badgeFilledBackground, in: RoundedRectangle(cornerRadius: 2)) + } + + @ViewBuilder + func voteCommentCountBadge(_ count: String) -> some View { + HStack(spacing: 2) { + Image(asset: .commentMessage) + .resizable() + .frame(width: 16, height: 16) + .accessibilityHidden(true) + Text(count) + .pretendardFont(.labelSmall) + .fixedSize() + } + .foregroundStyle(.gray300) + .padding(.horizontal, 6) + .padding(.vertical, 4) + .background(.white, in: RoundedRectangle(cornerRadius: 2)) + .overlay { + RoundedRectangle(cornerRadius: 2) + .strokeBorder(.gray100, lineWidth: 1) + } + } + @ViewBuilder func optionSection(isPreVoteDraft: Bool = false) -> some View { ZStack { @@ -226,11 +250,10 @@ private extension ClassBattleFlowView { if store.options.count == 2 { Text("VS") - .pretendardFont(.bold11) - .foregroundStyle(.neutral800) + .pretendardFont(.semiBold11) + .foregroundStyle(.gray800) .frame(width: 28, height: 28) .background(.secondary200, in: Circle()) - .overlay(Circle().stroke(.beige50, lineWidth: 1.5)) .allowsHitTesting(false) } } @@ -241,20 +264,21 @@ private extension ClassBattleFlowView { _ option: ClassBattleOption, isPreVoteDraft: Bool ) -> some View { + let isSelected = (isPreVoteDraft ? preVoteDraftOptionID : store.selectedOptionID) == option.id Button { if isPreVoteDraft { preVoteDraftOptionID = option.id } else { send(.optionTapped(option.id)) } } label: { - VStack(spacing: 8) { + VStack(spacing: 0) { Group { if let url = option.imageURL { AsyncImage(url: url) { image in image.resizable().scaledToFill() } placeholder: { - Circle().fill(.beige400) + Circle().fill(.beige600) } } else { - Circle().fill(.beige400) + Circle().fill(.beige600) .overlay { Text(option.label ?? "") .pretendardFont(.semiBold13) @@ -265,30 +289,29 @@ private extension ClassBattleFlowView { .clipShape(Circle()) Text(option.title) .pretendardFont(.headingSmall) - .foregroundStyle(.neutral600) + .foregroundStyle(.gray800) .lineLimit(2) .multilineTextAlignment(.center) + .padding(.top, 12) Text(option.representative) .pretendardFont(.labelSmall) .foregroundStyle(.neutral300) .lineLimit(1) + .padding(.top, 2) } .frame(maxWidth: .infinity) - .frame(height: 106) + .frame(height: 109) .background( - .beige300, - in: RoundedRectangle(cornerRadius: 3) + isSelected ? Color.beige400 : .beige300, + in: RoundedRectangle(cornerRadius: 2) ) .overlay { - RoundedRectangle(cornerRadius: 3) - .strokeBorder( - (isPreVoteDraft ? preVoteDraftOptionID : store.selectedOptionID) == option.id - ? (isFinalVote ? Color.secondary500 : .beige700) - : .beige500, - lineWidth: 1 - ) + RoundedRectangle(cornerRadius: 2) + .strokeBorder(isSelected ? Color.secondary500 : .beige600, lineWidth: 1) } + .opacity(isSelected ? 1 : 0.88) } + .accessibilityAddTraits(isSelected ? .isSelected : []) .buttonStyle(.plain) .disabled(!store.canParticipate) } @@ -312,7 +335,7 @@ private extension ClassBattleFlowView { systemImage: "clock" ) .pretendardFont(.regular13) - .foregroundStyle(isFinalVote ? .beige200 : .gray500) + .foregroundStyle(.gray300) } } .frame(maxWidth: .infinity) @@ -343,12 +366,12 @@ private extension ClassBattleFlowView { LazyVStack(alignment: .leading, spacing: 6) { ForEach(rows) { row in transcriptRow(row) - .padding(.top, row.hasTopSpacing ? 16 : 0) + .padding(.top, row.hasTopSpacing ? 14 : 0) .id(row.id) } } .padding(.horizontal, 16) - .padding(.top, 24) + .padding(.top, 8) .padding(.bottom, 40) } .onChange(of: store.currentTime) { _, _ in @@ -453,8 +476,8 @@ private extension ClassBattleFlowView { .frame(maxWidth: .infinity) .padding(.vertical, 8) } else { - let bubbleBackground: Color = line.side == "B" ? .beige500 : .white - let bubbleBorder: Color = line.side == "B" ? .beige700 : .beige500 + let bubbleBackground: Color = line.side == "B" ? .beige400 : .white + let bubbleBorder: Color = .beige600 HStack(alignment: .top, spacing: 6) { if line.side != "B" { transcriptAvatar(row) @@ -463,15 +486,16 @@ private extension ClassBattleFlowView { if row.showsHeader { Text(line.speakerName) .pretendardFont(.headingSmall) - .foregroundStyle(.gray400) + .foregroundStyle(.gray800) } Text(line.text) - .pretendardFont(.labelSmall) - .foregroundStyle(.gray700) - .lineSpacing(4.8) + .pretendardFont(.regular13) + .foregroundStyle(.gray400) + .lineSpacing(2.7) .fixedSize(horizontal: false, vertical: true) .frame(maxWidth: .infinity, alignment: .leading) - .padding(12) + .padding(.horizontal, 8) + .padding(.vertical, line.side == "B" ? 8 : 6) .pickeCard(bubbleBackground, border: bubbleBorder) .overlay(alignment: line.side == "B" ? .leading : .trailing) { if row.isActive && store.isPlaying { @@ -545,28 +569,27 @@ private extension ClassBattleFlowView { } } .padding(.horizontal, 24) - .padding(.top, 16) + .padding(.top, 24) .padding(.bottom, 8) .background(.white) - .topDivider(.beige600) } @ViewBuilder func audioProgressBar() -> some View { - VStack(spacing: 8) { + VStack(spacing: 0) { GeometryReader { geometry in ZStack(alignment: .leading) { - Capsule().fill(.beige600).frame(height: 4) + Capsule().fill(.gray300).frame(height: 3) Capsule() .fill(.primary500) - .frame(width: geometry.size.width * store.audioProgress, height: 4) + .frame(width: geometry.size.width * store.audioProgress, height: 3) Circle() .fill(.primary500) - .frame(width: 16, height: 16) - .offset(x: max(0, geometry.size.width * store.audioProgress - 8)) + .frame(width: 11, height: 11) + .offset(x: max(0, geometry.size.width * store.audioProgress - 5.5)) } .frame(maxWidth: .infinity, maxHeight: .infinity) - .contentShape(Rectangle()) + .contentShape(Rectangle().inset(by: -8)) .gesture( DragGesture(minimumDistance: 0) .onChanged { value in @@ -577,13 +600,13 @@ private extension ClassBattleFlowView { ) .allowsHitTesting(store.hasListened) } - .frame(height: 16) + .frame(height: 11) HStack { Text(store.currentTimeText) Spacer() Text(store.audioDurationText) } - .pretendardFont(.medium11) + .pretendardFont(.regular13) .foregroundStyle(.neutral300) } } @@ -650,7 +673,7 @@ private extension ClassBattleFlowView { .padding(.top, 12) .padding(.bottom, 16) .frame(maxWidth: .infinity) - .background(.white) + .background(.beige200) } @ViewBuilder @@ -666,11 +689,11 @@ private extension ClassBattleFlowView { opinionChangeLabel, systemImage: "lightbulb" ) - .pretendardFont(.medium11) + .pretendardFont(.semiBold12) .foregroundStyle(.primary500) - .padding(.horizontal, 6) - .padding(.vertical, 4) - .background(.beige200) + .padding(.horizontal, 4) + .padding(.vertical, 2) + .background(.primary50, in: RoundedRectangle(cornerRadius: 2)) .fixedSize() } else { Color.clear.frame(height: 20) @@ -687,10 +710,10 @@ private extension ClassBattleFlowView { @ViewBuilder func voteRatioBar(_ stats: ClassVoteStats) -> some View { GeometryReader { geometry in - Capsule() - .fill(.beige300) + RoundedRectangle(cornerRadius: 2) + .fill(.beige500) .overlay(alignment: .leading) { - Capsule() + RoundedRectangle(cornerRadius: 2) .fill(.primary500) .frame( width: geometry.size.width * CGFloat(min(max(stats.options.first?.ratio ?? 0, 0), 100) / 100) @@ -712,15 +735,15 @@ private extension ClassBattleFlowView { .foregroundStyle(.gray800) } .frame(width: 40, height: 40) - .background(.beige200, in: Circle()) + .background(.beige600, in: Circle()) .clipShape(Circle()) Text(option.title) .pretendardFont(.medium11) - .foregroundStyle(.gray800) + .foregroundStyle(.gray500) .lineLimit(1) Text(row.percentageLabel) - .pretendardFont(.semiBold13) - .foregroundStyle(.gray800) + .pretendardFont(.semiBold12) + .foregroundStyle(.gray500) } .frame(width: 64) } @@ -733,7 +756,7 @@ private extension ClassBattleFlowView { perspectiveFilter(title: option.title, optionID: option.id) } } - .background(.white) + .background(.beige200) } @ViewBuilder @@ -743,14 +766,16 @@ private extension ClassBattleFlowView { ) -> some View { Button { send(.perspectiveOptionTapped(optionID)) } label: { Text(title) - .pretendardFont(.semiBold13) + .pretendardFont(.labelMedium) .lineLimit(1) - .foregroundStyle(store.selectedPerspectiveOptionID == optionID ? .primary500 : .gray500) + .foregroundStyle(store.selectedPerspectiveOptionID == optionID ? .primary500 : .gray300) .frame(maxWidth: .infinity) - .padding(.vertical, 14) + .padding(.vertical, 12) .overlay(alignment: .bottom) { if store.selectedPerspectiveOptionID == optionID { Rectangle().fill(.primary500).frame(height: 2) + } else { + Rectangle().fill(.gray100).frame(height: 1) } } } @@ -765,7 +790,8 @@ private extension ClassBattleFlowView { Spacer() } .padding(.horizontal, 16) - .padding(.vertical, 16) + .padding(.top, 16) + .padding(.bottom, 6) } @ViewBuilder @@ -775,11 +801,10 @@ private extension ClassBattleFlowView { ) -> some View { Button { send(.perspectiveSortTapped(value)) } label: { Text(title) - .pretendardFont(.medium11) + .pretendardFont(.medium13) .foregroundStyle(store.perspectiveSort == value ? .white : .primary500) - .padding(.horizontal, 12) - .padding(.vertical, 6) - .background(store.perspectiveSort == value ? Color.primary500 : Color.white, in: RoundedRectangle(cornerRadius: 2)) + .frame(width: 58, height: 30) + .background(store.perspectiveSort == value ? Color.primary500 : .primary50, in: RoundedRectangle(cornerRadius: 2)) .overlay { RoundedRectangle(cornerRadius: 2) .stroke(.primary500, lineWidth: 1) @@ -792,12 +817,12 @@ private extension ClassBattleFlowView { func perspectiveCard(_ row: ClassPerspectiveRow) -> some View { let perspective = row.perspective VStack(alignment: .leading, spacing: 8) { - perspectiveHeader(perspective) + perspectiveHeader(row) VStack(alignment: .leading, spacing: 4) { Button { send(.perspectiveTapped(perspective.id)) } label: { Text(perspective.content) .pretendardFont(.regular13) - .foregroundStyle(.gray500) + .foregroundStyle(.gray300) .lineSpacing(5) .lineLimit(3) .frame(maxWidth: .infinity, alignment: .leading) @@ -811,37 +836,40 @@ private extension ClassBattleFlowView { perspectiveActions(row) } .padding(.horizontal, 16) - .padding(.vertical, 18) + .padding(.vertical, 12) .frame(maxWidth: .infinity, alignment: .leading) .background(.white) .overlay(alignment: .bottom) { - Rectangle().fill(.beige200).frame(height: 1) + Rectangle().fill(.beige600).frame(height: 1) } } @ViewBuilder - func perspectiveHeader(_ perspective: ClassPerspectiveItem) -> some View { + func perspectiveHeader(_ row: ClassPerspectiveRow) -> some View { + let perspective = row.perspective HStack(spacing: 8) { PickeAvatarView( imageURL: perspective.author.characterImageURL?.absoluteString, fallback: perspective.author.nickname, - size: 40 + size: 36 ) VStack(alignment: .leading, spacing: 2) { Text(perspective.author.nickname) - .pretendardFont(.semiBold13) + .pretendardFont(.semiBold12) .foregroundStyle(.gray800) - Text(perspective.createdAt, style: .relative) - .pretendardFont(.regular11) - .foregroundStyle(.gray500) + Text(row.createdAtLabel) + .pretendardFont(.labelXSmall) + .foregroundStyle(.gray300) } Spacer() Text(perspective.option.title) - .pretendardFont(.medium11) - .foregroundStyle(.primary500) - .padding(.horizontal, 5) - .padding(.vertical, 3) - .background(.beige200) + .pretendardFont(.semiBold12) + .foregroundStyle(.badgeFilledText) + .lineLimit(1) + .frame(height: 16) + .padding(.horizontal, 4) + .padding(.vertical, 2) + .background(.badgeFilledBackground, in: RoundedRectangle(cornerRadius: 2)) } } @@ -858,7 +886,7 @@ private extension ClassBattleFlowView { Text(row.likeCountLabel) .pretendardFont(.labelSmall) } - .foregroundStyle(perspective.isLiked ? .primary500 : .gray500) + .foregroundStyle(perspective.isLiked ? .primary500 : .gray300) } .disabled(perspective.isMyPerspective != false) Button { send(.perspectiveTapped(perspective.id)) } label: { @@ -870,7 +898,7 @@ private extension ClassBattleFlowView { Text(row.commentCountLabel) .pretendardFont(.labelSmall) } - .foregroundStyle(.gray500) + .foregroundStyle(.gray300) } Spacer() } @@ -889,7 +917,7 @@ private extension ClassBattleFlowView { } HStack(spacing: 12) { TextField( - "의견을 입력하세요", + "의견을 남겨보세요...", text: Binding( get: { store.draft }, set: { send(.draftChanged($0)) } @@ -928,12 +956,12 @@ private extension ClassBattleFlowView { ) { Button { send(.reportTapped) } label: { Label("리포트", systemImage: "list.bullet.rectangle") - .pretendardFont(.medium11) + .pretendardFont(.bold11) } .buttonStyle(.plain) .foregroundStyle(.primary500) } - .background(.beige50) + .background(.beige200) .accessibilityAction(named: "뒤로 가기") { send(.backTapped) } } } diff --git a/Projects/Feature/Class/Sources/ClassReport/Model/ClassReportHighlightRow.swift b/Projects/Feature/Class/Sources/ClassReport/Model/ClassReportHighlightRow.swift index 33188428..0182c49e 100644 --- a/Projects/Feature/Class/Sources/ClassReport/Model/ClassReportHighlightRow.swift +++ b/Projects/Feature/Class/Sources/ClassReport/Model/ClassReportHighlightRow.swift @@ -4,6 +4,8 @@ public struct ClassReportHighlightRow: Equatable, Identifiable { public let text: String public let isLast: Bool + public var quotedText: String { "“\(text)”" } + public init( id: Int, title: String, diff --git a/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift b/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift index 50673f6e..fb333874 100644 --- a/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift +++ b/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift @@ -149,7 +149,7 @@ private extension ClassReportView { func serverResultPages(_ result: ClassResult) -> some View { let highlightRows = store.highlightRows ScrollView { - VStack(alignment: .leading, spacing: 22) { + VStack(alignment: .leading, spacing: 24) { if let insight = store.insight { insightCard(insight) } else if result.insightState != nil { @@ -158,7 +158,7 @@ private extension ClassReportView { VStack(alignment: .leading, spacing: 12) { sectionHeading("클래스 참여 현황") - reportCard { + reportCard(padding: 17, horizontalPadding: 17, spacing: 16) { participationBar( title: "배틀 참여율", rate: store.serverParticipationRate, @@ -168,7 +168,7 @@ private extension ClassReportView { color: .primary500 ) participationBar( - title: "댓글 작성률", + title: "댓글 작성율", rate: store.serverCommentRate, caption: "댓글을 남긴 멤버", detail: store.serverCommentCount, @@ -180,7 +180,7 @@ private extension ClassReportView { VStack(alignment: .leading, spacing: 12) { sectionHeading("입장 분포") - reportCard { + reportCard(padding: 17, horizontalPadding: 17) { stanceDistribution(result.stats.options) } } @@ -236,23 +236,23 @@ private extension ClassReportView { @ViewBuilder func insightCard(_ insight: ClassAIInsight) -> some View { - VStack(alignment: .leading, spacing: 8) { + VStack(alignment: .leading, spacing: 6) { ClassAIInsightHeading( title: "AI 인사이트", - titleFont: .semiBold12, - titleLineHeight: 17, + titleFont: .bold11, + titleLineHeight: 15, iconSize: 20, spacing: 6 ) Text(insight.text) - .pretendardFont(.regular13) + .pretendardFont(.bodySmall) .foregroundStyle(.primary500) .fixedSize(horizontal: false, vertical: true) } .frame(maxWidth: .infinity, alignment: .leading) - .padding(12) - .background(.secondary50) - .overlay(Rectangle().stroke(.primary100, lineWidth: 1)) + .padding(13) + .background(.secondary50, in: RoundedRectangle(cornerRadius: 2)) + .overlay(RoundedRectangle(cornerRadius: 2).strokeBorder(.primary100, lineWidth: 1)) } @ViewBuilder @@ -290,13 +290,13 @@ private extension ClassReportView { progress: Double, color: Color ) -> some View { - VStack(alignment: .leading, spacing: 5) { + VStack(alignment: .leading, spacing: 0) { HStack { Text(title) .pretendardFont(.semiBold12) Spacer() Text(rate) - .pretendardFont(.semiBold12) + .pretendardFont(.headingSmall) } .foregroundStyle(.gray800) HStack { @@ -304,8 +304,9 @@ private extension ClassReportView { Spacer() Text(detail) } - .pretendardFont(.medium10) + .pretendardFont(.regular11) .foregroundStyle(.gray300) + .padding(.top, 6) GeometryReader { geometry in ZStack(alignment: .leading) { Capsule().fill(.beige500) @@ -313,8 +314,8 @@ private extension ClassReportView { } } .frame(height: 6) + .padding(.top, 4) } - .padding(.vertical, 2) } @ViewBuilder @@ -322,17 +323,19 @@ private extension ClassReportView { HStack(spacing: 24) { ZStack { Circle() + .inset(by: 6) .stroke( store.serverDistributionTotalCount == 0 ? Color.beige500 : Color.secondary500, lineWidth: 12 ) Circle() + .inset(by: 6) .trim(from: 0, to: store.serverDistributionProgress) .stroke(.primary500, style: StrokeStyle(lineWidth: 12, lineCap: .butt)) .rotationEffect(.degrees(-90)) VStack(spacing: 1) { Text(store.serverDistributionTotalText) - .pretendardFont(.semiBold15) + .pretendardFont(.labelLarge) .foregroundStyle(.gray800) Text("총 참여") .pretendardFont(.medium10) @@ -340,24 +343,24 @@ private extension ClassReportView { } } .frame(width: 100, height: 100) - VStack(alignment: .leading, spacing: 10) { + VStack(alignment: .leading, spacing: 12) { ForEach(Array(options.prefix(2).enumerated()), id: \.element.id) { index, option in - HStack(spacing: 6) { + HStack(spacing: 8) { Circle() .fill(index == 0 ? Color.primary500 : Color.secondary500) - .frame(width: 7, height: 7) - VStack(alignment: .leading, spacing: 2) { + .frame(width: 8, height: 8) + VStack(alignment: .leading, spacing: 0) { Text(option.title) - .pretendardFont(.semiBold11) + .pretendardFont(.semiBold12) .foregroundStyle(.gray800) .lineLimit(1) Text(store.serverDistributionOptionCountTexts[option.id] ?? "") - .pretendardFont(.medium10) + .pretendardFont(.medium11) .foregroundStyle(.gray300) } Spacer(minLength: 2) Text(store.serverDistributionOptionRatioTexts[option.id] ?? "") - .pretendardFont(.semiBold11) + .pretendardFont(family: .Bold, size: 12) .foregroundStyle(.gray800) } } @@ -368,17 +371,21 @@ private extension ClassReportView { @ViewBuilder func highlightRow(_ row: ClassReportHighlightRow) -> some View { - VStack(alignment: .leading, spacing: 6) { + VStack(alignment: .leading, spacing: 8) { Text(row.title) - .pretendardFont(.semiBold11) - .foregroundStyle(.primary500) - Text(row.text) + .pretendardFont(.semiBold12) + .foregroundStyle(.badgeFilledText) + .frame(height: 16) + .padding(.horizontal, 4) + .padding(.vertical, 2) + .background(.badgeFilledBackground, in: RoundedRectangle(cornerRadius: 2)) + Text(row.quotedText) .pretendardFont(.regular13) - .foregroundStyle(.gray700) + .foregroundStyle(.gray400) .fixedSize(horizontal: false, vertical: true) } .frame(maxWidth: .infinity, alignment: .leading) - .padding(12) + .padding(16) } @ViewBuilder diff --git a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift index 29a7f300..9876e2c0 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift @@ -543,6 +543,25 @@ struct ClassBattleFlowTests { #expect(state.perspectiveRows.map(\.commentCountLabel) == ["3", "2,300"]) } + @Test + func 의견_작성_시각은_N분_전_형식으로_표시한다() { + let item = ClassPerspectiveItem( + id: 1, + author: .init(userTag: "member", nickname: "참가자", characterType: nil, characterImageURL: nil), + option: .init(id: 1, label: "A", title: "찬성", stance: "찬성"), + content: "의견", + likeCount: 0, + commentCount: 0, + isLiked: false, + isMyPerspective: false, + status: nil, + createdAt: Date().addingTimeInterval(-150) + ) + let row = ClassPerspectiveRow(id: 1, perspective: item, likeCountLabel: "0", commentCountLabel: "0") + + #expect(row.createdAtLabel == "2분 전") + } + @Test func 이미_제출한_의견은_빈_재조회_응답으로_다시_작성할_수_없다() async { var state = battleState(room: .mocks[0]) @@ -1189,7 +1208,7 @@ struct ClassBattleFlowTests { status: .completed, opinionChanged: false ) - #expect(state.opinionChangeLabel == "처음 생각을 유지했어요") + #expect(state.opinionChangeLabel == "생각이 동일해요") state.voteHistory = ClassVoteHistory( battleTitle: "토론", @@ -1207,7 +1226,7 @@ struct ClassBattleFlowTests { status: .completed, opinionChanged: nil ) - #expect(state.opinionChangeLabel == "처음 생각을 유지했어요") + #expect(state.opinionChangeLabel == "생각이 동일해요") } @Test diff --git a/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift b/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift index 84d3b479..f866f9e2 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift @@ -28,6 +28,13 @@ struct ClassReportTests { ]) } + @Test + func 주요_의견_본문은_따옴표로_감싸_표시한다() { + let row = ClassReportHighlightRow(id: 0, title: "주요 의견 1", text: "첫 인용", isLast: true) + + #expect(row.quotedText == "“첫 인용”") + } + private struct ResultStub: ClassResultInterface { let result: Result let feedback: ClassAIPersonalFeedback? From f451f7328146796df79af7686587c2ab0617310d Mon Sep 17 00:00:00 2001 From: Roy Date: Mon, 2 Nov 2026 22:16:37 +0900 Subject: [PATCH 05/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20Figma=20?= =?UTF-8?q?=EB=8C=80=EC=A1=B0=EC=9A=A9=20=EC=BA=A1=EC=B2=98=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=99=80=20=EC=B6=94=EC=B6=9C=20=EB=8C=80?= =?UTF-8?q?=EC=83=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 실제 클래스 경로 화면을 Figma 프레임 크기·1x로 렌더링하는 캡처 테스트 추가(PICKE_CLASS_VISUAL_AUDIT=1에서만 실행) - 실기기 하단 safe area를 재현해 하단 버튼 위치를 대조 - Figma 추출 플러그인에 클래스 탭 프레임(12378:23897) 추가 --- .../ClassFigmaParityCaptureTests.swift | 258 ++++++++++++++++++ Tools/FigmaClassAudit/code.js | 1 + 2 files changed, 259 insertions(+) create mode 100644 Projects/App/Tests/Sources/ClassFigmaParityCaptureTests.swift diff --git a/Projects/App/Tests/Sources/ClassFigmaParityCaptureTests.swift b/Projects/App/Tests/Sources/ClassFigmaParityCaptureTests.swift new file mode 100644 index 00000000..a496dbc5 --- /dev/null +++ b/Projects/App/Tests/Sources/ClassFigmaParityCaptureTests.swift @@ -0,0 +1,258 @@ +// +// ClassFigmaParityCaptureTests.swift +// PickeTests +// +// Created by Picke on 11/2/26. +// + +import ClassDomainInterface +import ComposableArchitecture +import FeatureAssembly +@testable import Picke +import PickeDesignKit +import SwiftUI +import UIKit +import XCTest + +/// Run explicitly with PICKE_CLASS_VISUAL_AUDIT=1. Captures the real Class route +/// screens at 1x and the Figma frame size so they can be diffed against the +/// FigmaClassAudit plugin export. These images are evidence, not snapshot assertions. +final class ClassFigmaParityCaptureTests: XCTestCase { + @MainActor + func testCaptureClassFigmaParityFrames() async throws { + guard ProcessInfo.processInfo.environment["PICKE_CLASS_VISUAL_AUDIT"] == "1" else { + throw XCTSkip("Set PICKE_CLASS_VISUAL_AUDIT=1 to capture Class Figma parity frames") + } + let directory = try FileManager.default.url( + for: .documentDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true + ).appendingPathComponent("ClassFigmaParity", isDirectory: true) + try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) + + let phone = CGSize(width: 375, height: 812) + try await save(flowView(step: .none, selectedOptionID: 2), size: phone, name: "13337-56744-pre-vote", in: directory) + try await save( + flowView(step: .ttsListening, selectedOptionID: 2), + size: phone, + name: "13337-56966-tts", + in: directory + ) + try await save( + flowView(step: .postVote, selectedOptionID: 2), + size: phone, + name: "13337-57185-final-vote", + in: directory + ) + try await save( + flowView(step: .completed, selectedOptionID: 2), + size: phone, + name: "13384-57726-opinion", + in: directory + ) + try await save(reportView(), size: CGSize(width: 402, height: 1090), name: "13757-17445-result", in: directory) + } +} + +private extension ClassFigmaParityCaptureTests { + var room: ClassRoom { + let source = ClassRoom.mockClosedSociety + return ClassRoom( + id: source.id, + name: source.name, + joinCode: source.joinCode, + battle: source.battle, + deadline: source.deadline, + memberCount: source.memberCount, + role: .member, + status: .open, + allowsAnonymousOpinion: source.allowsAnonymousOpinion, + requiresComment: source.requiresComment, + ownerParticipates: source.ownerParticipates, + memberCapacity: source.memberCapacity + ) + } + + @MainActor + func flowView( + step: ClassBattleStep, + selectedOptionID: Int + ) -> AnyView { + var state = ClassBattleFlowFeature.State() + state.room = room + state.viewState = .loaded + // A pending load request keeps onAppear from replacing the seeded state with network calls. + state.loadRequestID = UUID() + state.selectedOptionID = selectedOptionID + state.session = ClassBattleSession( + classID: room.id, + myRole: .member, + closeAt: Date(timeIntervalSince1970: 1_790_154_000), + closed: false, + commentRequired: true, + canParticipate: true, + participationCompleted: false, + battle: ClassBattleDetail( + id: 10, + title: "촉법소년 연령을 낮춰야 할까?", + summary: "날로 잔혹해지는 청소년 범죄, 당신은 강력한 처벌을 원하십니까, 아니면 기회와 교정을 원하십니까?", + thumbnailURL: nil, + description: "날로 잔혹해지는 청소년 범죄, 당신은 강력한 처벌을 원하십니까, 아니면 기회와 교정을 원하십니까?", + shareURL: nil, + userVoteStatus: nil, + currentStep: step, + participantsCount: 28, + audioDuration: 268, + options: [ + .init(id: 1, label: "A", title: "예술이다", stance: "예술이다", representative: "사르트르", imageURL: nil), + .init(id: 2, label: "B", title: "변기는 변기다", stance: "변기는 변기다", representative: "플라톤", imageURL: nil), + ] + ), + midVoteEnabled: false + ) + state.hasPublishedPerspective = false + state.voteHistory = ClassVoteHistory( + battleTitle: "촉법소년 연령을 낮춰야 할까?", + preVote: .init(optionID: 1, label: "A", title: "예술이다"), + postVote: .init(optionID: 2, label: "B", title: "변기는 변기다"), + status: step, + opinionChanged: true + ) + state.isAudioReady = true + state.isPlaying = true + state.audioDuration = 268 + state.currentTime = 127 + state.transcript = [ + .init(id: 0, startTime: 0, speakerName: "한나 아렌트", speakerImageURL: nil, + text: "범죄의 잔혹함과 지능화에 나이는 더 이상 핑계가 될 수 없습니다!", side: "A"), + .init(id: 1, startTime: 10, speakerName: "한나 아렌트", speakerImageURL: nil, + text: "'촉법소년'이라는 제도가 오히려 아이들에게 법을 비웃고 범죄를 저지를 면죄부가 되고 있습니다.", side: "A"), + .init(id: 2, startTime: 20, speakerName: "한나 아렌트", speakerImageURL: nil, + text: "피해자의 눈물을 닦아주고 사법 정의를 세우려면 촉법 연령을 낮춰 단호하게 처벌해야 합니다.", side: "A"), + .init(id: 3, startTime: 40, speakerName: "루소", speakerImageURL: nil, + text: "아렌트 선생, 청소년은 아직 뇌와 도덕성이 성숙하지 못한 존재입니다!", side: "B"), + .init(id: 4, startTime: 60, speakerName: "루소", speakerImageURL: nil, + text: "성인과 똑같은 엄벌의 잣대를 대는 것은 사회가 아이들의 선도 가능성을 포기하는 것과 같습니다.", side: "B"), + .init(id: 5, startTime: 80, speakerName: "루소", speakerImageURL: nil, + text: "전과자라는 낙인을 찍어 감옥에 보내는 대신, 교육과 환경 개선을 통해 사회로 돌아오게 돕는 것이 국가의 의무입니다.", + side: "B"), + .init(id: 6, startTime: 120, speakerName: "한나 아렌트", speakerImageURL: nil, + text: "처벌받지 않는 악은 반드시 재범으로 이어집니다.", side: "A"), + ] + state.voteStats = ClassVoteStats( + options: [ + .init(id: 1, title: "예술이다", imageURL: nil, voteCount: 119, ratio: 59.5), + .init(id: 2, title: "변기는 변기다", imageURL: nil, voteCount: 81, ratio: 40.5), + ], + totalCount: 200, + updatedAt: .now + ) + let author = ClassPerspectiveAuthor( + userTag: "raccoon", + nickname: "사유하는 라쿤", + characterType: nil, + characterImageURL: nil + ) + let option = ClassPerspectiveOption(id: 2, label: "B", title: "변기는 변기다", stance: "변기는 변기다") + state.perspectives = (0 ..< 3).map { index in + ClassPerspectiveItem( + id: 100 + index, + author: author, + option: option, + content: "제도화가 무서운 건, 사회적 압력이 '선택'을 '의무'로 바꿀 수 있다는 거예요. 네덜란드 사례를 보면 우려가 현실이 되고 있죠. 제도화가 무서운 건, 사회적 압력이 '선택'을 '의무'로 바꿀 수 있다는 거예요.", + likeCount: 1340, + commentCount: 23, + isLiked: false, + isMyPerspective: false, + status: nil, + createdAt: Date().addingTimeInterval(-120) + ) + } + let store = Store(initialState: state) { ClassBattleFlowFeature() } + return AnyView(ClassBattleFlowView(store: store)) + } + + @MainActor + func reportView() -> AnyView { + let source = ClassRoom.mockClosedSociety + let owner = ClassRoom( + id: source.id, + name: source.name, + joinCode: source.joinCode, + battle: source.battle, + deadline: source.deadline, + memberCount: source.memberCount, + role: .owner, + status: .closed, + allowsAnonymousOpinion: source.allowsAnonymousOpinion, + requiresComment: source.requiresComment, + ownerParticipates: source.ownerParticipates, + memberCapacity: source.memberCapacity + ) + var state = ClassReportFeature.State(room: owner, instanceID: UUID()) + state.sourceViewState = .ready + state.insight = ClassAIInsight( + text: "처음에는 뫼르소의 태도를 비난할 수 있다는 의견이 많았지만, 대화 후에는 행동과 감정을 분리해서 봐야 한다는 의견이 늘어났어요.", + highlights: [ + .init(content: "슬픔을 겉으로 드러내지 않는다고 해서 타인을 사랑하지 않는다고 판단할 수는 없다고 생각해요."), + .init(content: "감정은 개인의 자유지만, 사회적 관계에서는 최소한의 공감 표현도 필요하다고 생각해요."), + ] + ) + state.classResult = ClassResult( + classID: owner.id, + closed: true, + closeAt: owner.deadline, + stats: .init( + activeMemberCount: 32, + completedCount: 28, + participationRate: 87.5, + commentWriterCount: 24, + commentRate: 75, + postVoteCount: 28, + options: [ + .init(id: 1, title: "낮춰야한다", preVoteCount: 18, postVoteCount: 18, postVoteRatio: 64), + .init(id: 2, title: "교정이 우선이다", preVoteCount: 10, postVoteCount: 10, postVoteRatio: 36), + ] + ), + myReportState: nil, + insightState: "SUCCEEDED" + ) + let store = Store(initialState: state) { ClassReportFeature() } + return AnyView(ClassReportView(store: store)) + } + + @MainActor + func save( + _ view: AnyView, + size: CGSize, + name: String, + in directory: URL + ) async throws { + let scene = try XCTUnwrap(UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.first) + let window = UIWindow(windowScene: scene) + let host = UIHostingController(rootView: view.environment(\.colorScheme, .light)) + window.rootViewController = host + // The 1x window sits above the home indicator, so mirror a device bottom safe area. + host.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 34, right: 0) + window.frame = CGRect(origin: .zero, size: size) + window.makeKeyAndVisible() + defer { window.isHidden = true } + host.view.frame = window.bounds + host.view.layoutIfNeeded() + // Report charts animate after data arrives. + try await Task.sleep(for: .milliseconds(1200)) + host.view.layoutIfNeeded() + CATransaction.flush() + let format = UIGraphicsImageRendererFormat() + format.scale = 1 + let image = UIGraphicsImageRenderer(bounds: window.bounds, format: format).image { _ in + window.drawHierarchy(in: window.bounds, afterScreenUpdates: true) + } + try XCTUnwrap(image.pngData()).write(to: directory.appendingPathComponent(name + ".png")) + let attachment = XCTAttachment(image: image) + attachment.name = name + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/Tools/FigmaClassAudit/code.js b/Tools/FigmaClassAudit/code.js index d88dd103..dea06cd7 100644 --- a/Tools/FigmaClassAudit/code.js +++ b/Tools/FigmaClassAudit/code.js @@ -6,6 +6,7 @@ const targets = [ { id: "13384:57726", label: "의견" }, { id: "13757:17445", label: "결과" }, { id: "13956:18883", label: "홈" }, + { id: "12378:23897", label: "클래스 탭" }, ]; const sourceNodeId = "2027:2"; From 64707f081948254501ab0e338e56fd025223c3f1 Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 01:07:45 +0900 Subject: [PATCH 06/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20Figma=20=EC=A4=84=20=EB=86=92=EC=9D=B4=C2=B7?= =?UTF-8?q?=EA=B0=84=EA=B2=A9=202=EC=B0=A8=20=EC=A0=95=ED=95=A9=EC=84=B1?= =?UTF-8?q?=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PickeDesignKit: Figma 줄 높이(글자 크기 대비 배수)를 맞추는 pretendardFont(_:lineHeightMultiple:) 추가 - 사전·최종투표: 상단 여백 비율과 제목·설명·선택지 줄 높이 조정, 홈 인디케이터 기기에서 마감 문구만 Figma처럼 하단 영역에 걸치게 함(SE 계열은 기존 여백 유지) - TTS: 화자 이름·말풍선·재생 시간 줄 높이, 재생바와 컨트롤 사이 간격 제거 - 의견: 요약 열 폭·간격과 막대 위치, 탭 높이 44, 카드 글자 줄 높이, 입력창 높이·테두리·배경을 시안대로 조정(보내기 버튼 탭 영역 44 유지) - 결과: 섹션 제목 14pt, 인사이트·참여 현황·입장 분포·주요 의견 줄 높이와 행 높이 조정 - 캡처 테스트: 창 하단 safe area가 이미 있으면 추가하지 않도록 수정(375×812 iPhone 13 mini에서 Figma와 같은 조건으로 캡처) - 검증: Class 테스트 238/238 통과, 13 mini·17 Pro 캡처에서 주요 요소가 Figma와 ±3pt 이내 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../ClassFigmaParityCaptureTests.swift | 4 +- .../View/ClassBattleFlowView.swift | 121 +++++++++++------- .../ClassReport/View/ClassReportView.swift | 34 +++-- .../Token/Typography/PretendardFont.swift | 17 +++ 4 files changed, 112 insertions(+), 64 deletions(-) diff --git a/Projects/App/Tests/Sources/ClassFigmaParityCaptureTests.swift b/Projects/App/Tests/Sources/ClassFigmaParityCaptureTests.swift index a496dbc5..dc88c5d2 100644 --- a/Projects/App/Tests/Sources/ClassFigmaParityCaptureTests.swift +++ b/Projects/App/Tests/Sources/ClassFigmaParityCaptureTests.swift @@ -233,10 +233,10 @@ private extension ClassFigmaParityCaptureTests { let window = UIWindow(windowScene: scene) let host = UIHostingController(rootView: view.environment(\.colorScheme, .light)) window.rootViewController = host - // The 1x window sits above the home indicator, so mirror a device bottom safe area. - host.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 34, right: 0) window.frame = CGRect(origin: .zero, size: size) window.makeKeyAndVisible() + // A window shorter than the screen sits above the home indicator, so mirror a device bottom safe area. + host.additionalSafeAreaInsets.bottom = max(0, 34 - window.safeAreaInsets.bottom) defer { window.isHidden = true } host.view.frame = window.bounds host.view.layoutIfNeeded() diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift index c582482b..de744a59 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift @@ -93,8 +93,8 @@ private extension ClassBattleFlowView { height: isPreVoteLanding ? max(420, min(510, proxy.size.height * 0.63)) : isFinalVote - ? max(330, min(405, proxy.size.height * 0.48 + 15)) - : max(315, min(390, proxy.size.height * 0.48)) + ? max(330, min(405, proxy.size.height * 0.5 + 15)) + : max(315, min(390, proxy.size.height * 0.5)) ) voteIntroduction() if !isPreVoteLanding { @@ -106,7 +106,7 @@ private extension ClassBattleFlowView { .frame(minHeight: max(0, proxy.size.height - 110), alignment: .top) } .safeAreaInset(edge: .bottom, spacing: 0) { - voteFooter() + voteFooter(bottomPadding: voteFooterBottomPadding(hasBottomSafeArea: proxy.safeAreaInsets.bottom > 0)) .padding(.horizontal, 16) .padding(.top, 12) .background(voteBackground) @@ -194,12 +194,12 @@ private extension ClassBattleFlowView { } Text(store.displayTitle) - .pretendardFont(.bold24) + .pretendardFont(.bold24, lineHeightMultiple: 1.3) .foregroundStyle(isFinalVote ? .white : .gray800) .fixedSize(horizontal: false, vertical: true) .padding(.top, 20) Text(store.displaySummary) - .pretendardFont(.bodyMedium) + .pretendardFont(.bodyMedium, lineHeightMultiple: 1.4) .foregroundStyle(isFinalVote ? .gray300 : .gray400) .fixedSize(horizontal: false, vertical: true) .padding(.top, 12) @@ -292,9 +292,10 @@ private extension ClassBattleFlowView { .foregroundStyle(.gray800) .lineLimit(2) .multilineTextAlignment(.center) + .frame(minHeight: 20) .padding(.top, 12) Text(option.representative) - .pretendardFont(.labelSmall) + .pretendardFont(.labelSmall, lineHeightMultiple: 1.4) .foregroundStyle(.neutral300) .lineLimit(1) .padding(.top, 2) @@ -317,7 +318,7 @@ private extension ClassBattleFlowView { } @ViewBuilder - func voteFooter() -> some View { + func voteFooter(bottomPadding: CGFloat) -> some View { VStack(spacing: 8) { if let error = store.errorMessage { Text(error) @@ -334,12 +335,19 @@ private extension ClassBattleFlowView { deadline, systemImage: "clock" ) - .pretendardFont(.regular13) + .pretendardFont(.regular13, lineHeightMultiple: 1.4) .foregroundStyle(.gray300) } } .frame(maxWidth: .infinity) - .padding(.bottom, isFinalVote ? 10 : 20) + .padding(.bottom, bottomPadding) + } + + func voteFooterBottomPadding(hasBottomSafeArea: Bool) -> CGFloat { + if isFinalVote { return hasBottomSafeArea ? 6 : 10 } + // 홈 인디케이터가 있는 기기에서는 Figma처럼 마지막 줄인 마감 문구만 그 영역에 10pt 걸친다. + guard hasBottomSafeArea, store.voteDeadlineLabel != nil else { return 20 } + return -10 } @ViewBuilder @@ -485,13 +493,12 @@ private extension ClassBattleFlowView { VStack(alignment: line.side == "B" ? .trailing : .leading, spacing: 6) { if row.showsHeader { Text(line.speakerName) - .pretendardFont(.headingSmall) + .pretendardFont(.headingSmall, lineHeightMultiple: 1.4) .foregroundStyle(.gray800) } Text(line.text) - .pretendardFont(.regular13) + .pretendardFont(.regular13, lineHeightMultiple: 1.4) .foregroundStyle(.gray400) - .lineSpacing(2.7) .fixedSize(horizontal: false, vertical: true) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 8) @@ -549,19 +556,21 @@ private extension ClassBattleFlowView { } } } - audioProgressBar() - AudioPlayerControlView( - isPlaying: .constant(store.isPlaying), - onBackward: { send(.seekBy(-15)) }, - onTogglePlay: { send(.playbackTapped) }, - onForward: { send(.seekBy(15)) }, - onReplay: { send(.replayTapped) }, - isReplayEnabled: store.hasListened, - tint: .neutral900, - playbackRateTitle: store.playbackRateTitle, - onPlaybackRateSelected: { send(.playbackRateSelected($0)) } - ) - .disabled(!store.isAudioReady || store.pendingCheckpointID != nil) + VStack(spacing: 0) { + audioProgressBar() + AudioPlayerControlView( + isPlaying: .constant(store.isPlaying), + onBackward: { send(.seekBy(-15)) }, + onTogglePlay: { send(.playbackTapped) }, + onForward: { send(.seekBy(15)) }, + onReplay: { send(.replayTapped) }, + isReplayEnabled: store.hasListened, + tint: .neutral900, + playbackRateTitle: store.playbackRateTitle, + onPlaybackRateSelected: { send(.playbackRateSelected($0)) } + ) + .disabled(!store.isAudioReady || store.pendingCheckpointID != nil) + } if store.hasListened { Button(store.buttonTitle) { send(.submitTapped) } .ctaButtonStyle(.primary, size: .large, height: 52) @@ -606,7 +615,7 @@ private extension ClassBattleFlowView { Spacer() Text(store.audioDurationText) } - .pretendardFont(.regular13) + .pretendardFont(.regular13, lineHeightMultiple: 1.4) .foregroundStyle(.neutral300) } } @@ -670,8 +679,8 @@ private extension ClassBattleFlowView { } } .padding(.horizontal, 16) - .padding(.top, 12) - .padding(.bottom, 16) + .padding(.top, 8) + .padding(.bottom, 8) .frame(maxWidth: .infinity) .background(.beige200) } @@ -679,11 +688,12 @@ private extension ClassBattleFlowView { @ViewBuilder func voteStatsSummary(_ stats: ClassVoteStats) -> some View { let rows = store.voteStatsOptionRows - HStack(alignment: .center, spacing: 12) { + HStack(alignment: .top, spacing: 0) { if let first = rows.first { voteStatsOption(first) } - VStack(spacing: 20) { + // Figma는 높이 76 열에서 막대 영역(35)을 아래에 붙여 배지와 11 떨어진다. + VStack(spacing: 11) { if let opinionChangeLabel = store.opinionChangeLabel { Label( opinionChangeLabel, @@ -692,14 +702,17 @@ private extension ClassBattleFlowView { .pretendardFont(.semiBold12) .foregroundStyle(.primary500) .padding(.horizontal, 4) - .padding(.vertical, 2) + .frame(height: 20) .background(.primary50, in: RoundedRectangle(cornerRadius: 2)) .fixedSize() } else { Color.clear.frame(height: 20) } voteRatioBar(stats) + .padding(.horizontal, 8) + .frame(height: 35) } + .padding(.top, 10) .frame(maxWidth: .infinity) if rows.count > 1 { voteStatsOption(rows[1]) @@ -726,7 +739,7 @@ private extension ClassBattleFlowView { @ViewBuilder func voteStatsOption(_ row: ClassVoteStatsOptionRow) -> some View { let option = row.option - VStack(spacing: 2) { + VStack(spacing: 0) { AsyncImage(url: option.imageURL) { image in image.resizable().scaledToFill() } placeholder: { @@ -738,14 +751,15 @@ private extension ClassBattleFlowView { .background(.beige600, in: Circle()) .clipShape(Circle()) Text(option.title) - .pretendardFont(.medium11) + .pretendardFont(.medium11, lineHeightMultiple: 1.4) .foregroundStyle(.gray500) .lineLimit(1) + .padding(.top, 4) Text(row.percentageLabel) - .pretendardFont(.semiBold12) + .pretendardFont(.semiBold12, lineHeightMultiple: 1.4) .foregroundStyle(.gray500) } - .frame(width: 64) + .frame(width: 70) } @ViewBuilder @@ -770,7 +784,7 @@ private extension ClassBattleFlowView { .lineLimit(1) .foregroundStyle(store.selectedPerspectiveOptionID == optionID ? .primary500 : .gray300) .frame(maxWidth: .infinity) - .padding(.vertical, 12) + .frame(height: 44) .overlay(alignment: .bottom) { if store.selectedPerspectiveOptionID == optionID { Rectangle().fill(.primary500).frame(height: 2) @@ -821,16 +835,17 @@ private extension ClassBattleFlowView { VStack(alignment: .leading, spacing: 4) { Button { send(.perspectiveTapped(perspective.id)) } label: { Text(perspective.content) - .pretendardFont(.regular13) + .pretendardFont(.regular13, lineHeightMultiple: 1.4) .foregroundStyle(.gray300) - .lineSpacing(5) .lineLimit(3) + .padding(.horizontal, 2) .frame(maxWidth: .infinity, alignment: .leading) } .buttonStyle(.plain) Button("자세히 보기") { send(.perspectiveTapped(perspective.id)) } - .pretendardFont(.labelSmall) + .pretendardFont(.labelSmall, lineHeightMultiple: 1.4) .foregroundStyle(.gray200) + .padding(.horizontal, 2) .buttonStyle(.plain) } perspectiveActions(row) @@ -855,10 +870,10 @@ private extension ClassBattleFlowView { ) VStack(alignment: .leading, spacing: 2) { Text(perspective.author.nickname) - .pretendardFont(.semiBold12) + .pretendardFont(.semiBold12, lineHeightMultiple: 1.4) .foregroundStyle(.gray800) Text(row.createdAtLabel) - .pretendardFont(.labelXSmall) + .pretendardFont(.labelXSmall, lineHeightMultiple: 1.4) .foregroundStyle(.gray300) } Spacer() @@ -902,6 +917,7 @@ private extension ClassBattleFlowView { } Spacer() } + .frame(height: 24) .buttonStyle(.plain) } @@ -915,30 +931,39 @@ private extension ClassBattleFlowView { .padding(.horizontal, 16) .padding(.top, 12) } - HStack(spacing: 12) { + HStack(spacing: 8) { TextField( "의견을 남겨보세요...", text: Binding( get: { store.draft }, set: { send(.draftChanged($0)) } - ) + ), + prompt: Text("의견을 남겨보세요...").foregroundStyle(.gray300) ) - .pretendardFont(.bodyMedium) + .pretendardFont(.medium13) .padding(.horizontal, 12) - .frame(height: 44) - .background(.white, in: RoundedRectangle(cornerRadius: 4)) + .frame(height: 52) + .background(.white, in: RoundedRectangle(cornerRadius: 2)) + .overlay { + RoundedRectangle(cornerRadius: 2) + .strokeBorder(.beige600, lineWidth: 1) + } Button { send(.submitTapped) } label: { Image(systemName: "paperplane.fill") .foregroundStyle(.white) - .frame(width: 44, height: 44) + .frame(width: 40, height: 40) .background(.primary500, in: Circle()) + .contentShape(Rectangle().inset(by: -2)) } .buttonStyle(.plain) .disabled(!store.canSubmit) } .padding(16) } - .background(.beige200) + .background(.beige600) + .overlay(alignment: .top) { + Rectangle().fill(.beige700).frame(height: 1) + } } var plainNavigationBar: some View { diff --git a/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift b/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift index fb333874..1fa23394 100644 --- a/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift +++ b/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift @@ -157,7 +157,7 @@ private extension ClassReportView { } VStack(alignment: .leading, spacing: 12) { - sectionHeading("클래스 참여 현황") + sectionHeading("클래스 참여 현황", textStyle: .headingSmall) reportCard(padding: 17, horizontalPadding: 17, spacing: 16) { participationBar( title: "배틀 참여율", @@ -179,7 +179,7 @@ private extension ClassReportView { } VStack(alignment: .leading, spacing: 12) { - sectionHeading("입장 분포") + sectionHeading("입장 분포", textStyle: .headingSmall) reportCard(padding: 17, horizontalPadding: 17) { stanceDistribution(result.stats.options) } @@ -187,7 +187,7 @@ private extension ClassReportView { if !highlightRows.isEmpty { VStack(alignment: .leading, spacing: 12) { - sectionHeading("주요 의견") + sectionHeading("주요 의견", textStyle: .headingSmall) reportCard(padding: 0, horizontalPadding: 0, spacing: 0) { ForEach(highlightRows) { row in highlightRow(row) @@ -201,7 +201,7 @@ private extension ClassReportView { if result.myReportState != nil { VStack(alignment: .leading, spacing: 12) { - sectionHeading("내 AI 피드백") + sectionHeading("내 AI 피드백", textStyle: .headingSmall) reportCard { if case let .ready(feedback) = store.feedbackViewState { Text(feedback.summaryTitle) @@ -245,7 +245,7 @@ private extension ClassReportView { spacing: 6 ) Text(insight.text) - .pretendardFont(.bodySmall) + .pretendardFont(.bodySmall, lineHeightMultiple: 1.4) .foregroundStyle(.primary500) .fixedSize(horizontal: false, vertical: true) } @@ -298,6 +298,7 @@ private extension ClassReportView { Text(rate) .pretendardFont(.headingSmall) } + .frame(height: 20) .foregroundStyle(.gray800) HStack { Text(caption) @@ -306,6 +307,7 @@ private extension ClassReportView { } .pretendardFont(.regular11) .foregroundStyle(.gray300) + .frame(height: 15) .padding(.top, 6) GeometryReader { geometry in ZStack(alignment: .leading) { @@ -333,12 +335,12 @@ private extension ClassReportView { .trim(from: 0, to: store.serverDistributionProgress) .stroke(.primary500, style: StrokeStyle(lineWidth: 12, lineCap: .butt)) .rotationEffect(.degrees(-90)) - VStack(spacing: 1) { + VStack(spacing: 0) { Text(store.serverDistributionTotalText) - .pretendardFont(.labelLarge) + .pretendardFont(.labelLarge, lineHeightMultiple: 1.5) .foregroundStyle(.gray800) Text("총 참여") - .pretendardFont(.medium10) + .pretendardFont(.medium10, lineHeightMultiple: 1.4) .foregroundStyle(.gray300) } } @@ -351,11 +353,11 @@ private extension ClassReportView { .frame(width: 8, height: 8) VStack(alignment: .leading, spacing: 0) { Text(option.title) - .pretendardFont(.semiBold12) + .pretendardFont(.semiBold12, lineHeightMultiple: 1.4) .foregroundStyle(.gray800) .lineLimit(1) Text(store.serverDistributionOptionCountTexts[option.id] ?? "") - .pretendardFont(.medium11) + .pretendardFont(.medium11, lineHeightMultiple: 1.4) .foregroundStyle(.gray300) } Spacer(minLength: 2) @@ -380,9 +382,10 @@ private extension ClassReportView { .padding(.vertical, 2) .background(.badgeFilledBackground, in: RoundedRectangle(cornerRadius: 2)) Text(row.quotedText) - .pretendardFont(.regular13) + .pretendardFont(.regular13, lineHeightMultiple: 1.4) .foregroundStyle(.gray400) .fixedSize(horizontal: false, vertical: true) + .padding(.horizontal, 2) } .frame(maxWidth: .infinity, alignment: .leading) .padding(16) @@ -765,11 +768,14 @@ private extension ClassReportView { } } - func sectionHeading(_ title: String) -> some View { + func sectionHeading( + _ title: String, + textStyle: CustomSizeFont = .semiBold13 + ) -> some View { Text(title) - .pretendardFont(.semiBold13) + .pretendardFont(textStyle) .foregroundStyle(.gray800) - .frame(minHeight: 18) + .frame(minHeight: (textStyle.size * 1.4).rounded()) .frame(maxWidth: .infinity, alignment: .leading) } diff --git a/Projects/UI/PickeDesignKit/Sources/Token/Typography/PretendardFont.swift b/Projects/UI/PickeDesignKit/Sources/Token/Typography/PretendardFont.swift index 702eceba..9de53c0b 100644 --- a/Projects/UI/PickeDesignKit/Sources/Token/Typography/PretendardFont.swift +++ b/Projects/UI/PickeDesignKit/Sources/Token/Typography/PretendardFont.swift @@ -43,6 +43,23 @@ public extension View { ) ) } + + /// Figma 줄 높이에 맞춘다. `lineHeightMultiple`은 글자 크기 대비 배수(1.4 = 140%)이고 절대값이 아니다. + /// 줄 사이와 첫 줄 위·마지막 줄 아래에 남는 높이를 나눠 더하므로 배지처럼 배경 높이를 따로 정한 텍스트, + /// 높이가 고정된 컨테이너 안 여러 줄 텍스트에는 쓰지 않는다. + func pretendardFont( + _ textStyle: CustomSizeFont, + lineHeightMultiple: CGFloat + ) -> some View { + let naturalLineHeight = UIFont.pretendardFontFamily( + family: textStyle.fontFamily, + size: textStyle.size + ).lineHeight + let extra = max(0, textStyle.size * lineHeightMultiple - naturalLineHeight) + return pretendardFont(textStyle) + .lineSpacing(extra) + .padding(.vertical, extra / 2) + } } public extension UIFont { From 09b195951f89191db7423b7fc147266c4a848492 Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 02:20:08 +0900 Subject: [PATCH 07/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EB=82=B4=20=ED=99=9C=EB=8F=99=20API=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Android develop(b127754)과 같이 GET classes/{classId}/result/my-activity(cursor) 추가 - 받은 추천 수·댓글 수·내가 남긴 댓글(커서 페이지) 엔티티와 DTO 매핑 추가(댓글·의견 ID 없는 항목 제외) - ClassResultInterface에 fetchMyActivity를 추가하고 저장소·유스케이스·Mock·테스트 대역 갱신 - 경로·메서드·커서 쿼리와 응답 매핑 계약 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../ClassCoordinatorFeedbackTests.swift | 7 +++ .../Tests/Sources/ClassCoordinatorTests.swift | 14 +++++ .../Sources/ClassFlowVisualAuditTests.swift | 7 +++ .../Entity/Result/ClassMyActivity.swift | 27 +++++++++ .../Result/ClassMyActivityComment.swift | 30 ++++++++++ .../MockClassRepository+Features.swift | 7 +++ .../Result/ClassResultInterface.swift | 4 ++ .../Model/DTO/ClassMyActivityDTO.swift | 20 +++++++ .../Model/Mapper/ClassMyActivityDTO+.swift | 32 ++++++++++ .../ClassRepositoryImpl+Result.swift | 20 +++++++ .../UseCase/ClassUseCaseImpl+Result.swift | 7 +++ .../Sources/ClassResultAPIContractTests.swift | 60 +++++++++++++++++++ .../Sources/ClassOwnerDashboardTests.swift | 7 +++ .../Tests/Sources/ClassReportTests.swift | 7 +++ .../Sources/Class/Result/ClassResultAPI.swift | 2 + .../Class/Result/ClassResultService.swift | 9 ++- .../Result/Request/ClassResultRequests.swift | 5 ++ 17 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassMyActivity.swift create mode 100644 Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassMyActivityComment.swift create mode 100644 Projects/Domain/ClassDomain/Sources/Model/DTO/ClassMyActivityDTO.swift create mode 100644 Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassMyActivityDTO+.swift create mode 100644 Projects/Service/APIEndpoint/Sources/Class/Result/Request/ClassResultRequests.swift diff --git a/Projects/App/Tests/Sources/ClassCoordinatorFeedbackTests.swift b/Projects/App/Tests/Sources/ClassCoordinatorFeedbackTests.swift index 55b1fdaf..5c757c21 100644 --- a/Projects/App/Tests/Sources/ClassCoordinatorFeedbackTests.swift +++ b/Projects/App/Tests/Sources/ClassCoordinatorFeedbackTests.swift @@ -243,6 +243,13 @@ private actor SuspendedFeedbackGenerator: ClassResultInterface { throw ClassAIFeedbackError.generationFailed } + func fetchMyActivity( + roomID _: Int, + cursor _: String? + ) async throws -> ClassMyActivity { + throw ClassAIFeedbackError.temporarilyUnavailable + } + private func suspend( request: @escaping @Sendable (CheckedContinuation) -> Request ) async throws -> T { diff --git a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift index 743454df..16cac6f1 100644 --- a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift +++ b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift @@ -3007,6 +3007,13 @@ private struct FixedClassResultUseCase: ClassResultInterface { func retryInsight(roomID _: Int) async throws { throw ClassError.unknown("이 테스트는 인사이트 재시도를 사용하지 않습니다.") } + + func fetchMyActivity( + roomID _: Int, + cursor _: String? + ) async throws -> ClassMyActivity { + throw ClassError.unknown("이 테스트는 내 활동 조회를 사용하지 않습니다.") + } } private actor SuspendedClassUseCase: ClassResultInterface { @@ -3071,6 +3078,13 @@ private actor SuspendedClassUseCase: ClassResultInterface { throw ClassError.network("이 테스트는 인사이트 재시도를 사용하지 않습니다.") } + func fetchMyActivity( + roomID _: Int, + cursor _: String? + ) async throws -> ClassMyActivity { + throw ClassError.network("이 테스트는 내 활동 조회를 사용하지 않습니다.") + } + private static var fixture: ClassResult { ClassResult( classID: ClassRoom.mockClosedSociety.id, diff --git a/Projects/App/Tests/Sources/ClassFlowVisualAuditTests.swift b/Projects/App/Tests/Sources/ClassFlowVisualAuditTests.swift index 6f9c8152..eaaae8c4 100644 --- a/Projects/App/Tests/Sources/ClassFlowVisualAuditTests.swift +++ b/Projects/App/Tests/Sources/ClassFlowVisualAuditTests.swift @@ -3153,6 +3153,13 @@ final class ClassFlowVisualAuditTests: XCTestCase { } func retryInsight(roomID _: Int) async throws {} + + func fetchMyActivity( + roomID _: Int, + cursor _: String? + ) async throws -> ClassMyActivity { + throw ClassAIFeedbackError.temporarilyUnavailable + } } private static func visualResultUseCase() -> VisualResultUseCase { VisualResultUseCase() } diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassMyActivity.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassMyActivity.swift new file mode 100644 index 00000000..7256b4e9 --- /dev/null +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassMyActivity.swift @@ -0,0 +1,27 @@ +import Foundation + +/// 클래스 결과의 내 활동. 받은 추천 수와 내가 남긴 댓글을 커서 단위로 받는다. +public struct ClassMyActivity: Equatable, Sendable { + public let classID: Int + public let receivedLikeCount: Int + public let commentCount: Int + public let comments: [ClassMyActivityComment] + public let nextCursor: String? + public let hasNext: Bool + + public init( + classID: Int, + receivedLikeCount: Int, + commentCount: Int, + comments: [ClassMyActivityComment], + nextCursor: String?, + hasNext: Bool + ) { + self.classID = classID + self.receivedLikeCount = receivedLikeCount + self.commentCount = commentCount + self.comments = comments + self.nextCursor = nextCursor + self.hasNext = hasNext + } +} diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassMyActivityComment.swift b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassMyActivityComment.swift new file mode 100644 index 00000000..ee4273df --- /dev/null +++ b/Projects/Domain/ClassDomain/Interface/Sources/Entity/Result/ClassMyActivityComment.swift @@ -0,0 +1,30 @@ +import Foundation + +/// 내가 다른 의견에 남긴 댓글 한 건. +public struct ClassMyActivityComment: Equatable, Sendable, Identifiable { + public let id: Int + public let perspectiveID: Int + public let perspectiveTitle: String + public let stance: String + public let content: String + public let likeCount: Int + public let createdAt: Date? + + public init( + id: Int, + perspectiveID: Int, + perspectiveTitle: String, + stance: String, + content: String, + likeCount: Int, + createdAt: Date? + ) { + self.id = id + self.perspectiveID = perspectiveID + self.perspectiveTitle = perspectiveTitle + self.stance = stance + self.content = content + self.likeCount = likeCount + self.createdAt = createdAt + } +} diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Default/MockClassRepository+Features.swift b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Default/MockClassRepository+Features.swift index 506c3a73..31f59977 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Default/MockClassRepository+Features.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Default/MockClassRepository+Features.swift @@ -117,6 +117,13 @@ public extension MockClassRepository { throw ClassError.unknown("모의 클래스 기능이 설정되지 않았습니다: fetchInsight") } + public func fetchMyActivity( + roomID: Int, + cursor: String? + ) async throws -> ClassMyActivity { + throw ClassError.unknown("모의 클래스 기능이 설정되지 않았습니다: fetchMyActivity") + } + public func retryInsight(roomID: Int) async throws { throw ClassError.unknown("모의 클래스 기능이 설정되지 않았습니다: retryInsight") } diff --git a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Result/ClassResultInterface.swift b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Result/ClassResultInterface.swift index dac4b067..4719fb1a 100644 --- a/Projects/Domain/ClassDomain/Interface/Sources/Repository/Result/ClassResultInterface.swift +++ b/Projects/Domain/ClassDomain/Interface/Sources/Repository/Result/ClassResultInterface.swift @@ -13,6 +13,10 @@ public protocol ClassResultInterface: Sendable { func fetchPersonalResult(roomID: Int) async throws -> ClassAIPersonalFeedback func fetchInsight(roomID: Int) async throws -> ClassAIInsight func retryInsight(roomID: Int) async throws + func fetchMyActivity( + roomID: Int, + cursor: String? + ) async throws -> ClassMyActivity } public enum ClassResultRepositoryDependency: TestDependencyKey { diff --git a/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassMyActivityDTO.swift b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassMyActivityDTO.swift new file mode 100644 index 00000000..0e8544e5 --- /dev/null +++ b/Projects/Domain/ClassDomain/Sources/Model/DTO/ClassMyActivityDTO.swift @@ -0,0 +1,20 @@ +import Foundation + +struct ClassMyActivityDTO: Decodable, Sendable { + struct Comment: Decodable, Sendable { + let commentId: Int? + let perspectiveId: Int? + let perspectiveTitle: String? + let stance: String? + let content: String? + let likeCount: Int? + let createdAt: String? + } + + let classId: Int? + let receivedLikeCount: Int? + let commentCount: Int? + let comments: [Comment]? + let nextCursor: String? + let hasNext: Bool? +} diff --git a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassMyActivityDTO+.swift b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassMyActivityDTO+.swift new file mode 100644 index 00000000..74b860a7 --- /dev/null +++ b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassMyActivityDTO+.swift @@ -0,0 +1,32 @@ +import ClassDomainInterface +import Foundation +import PickeCoreUtility + +extension ClassMyActivityDTO.Comment { + /// Android와 같이 댓글·의견 ID가 없는 항목은 버린다. + func toDomain() -> ClassMyActivityComment? { + guard let commentId, let perspectiveId else { return nil } + return ClassMyActivityComment( + id: commentId, + perspectiveID: perspectiveId, + perspectiveTitle: perspectiveTitle ?? "", + stance: stance ?? "", + content: content ?? "", + likeCount: likeCount ?? 0, + createdAt: createdAt.flatMap(ServerDateParser.parse) + ) + } +} + +extension ClassMyActivityDTO { + func toDomain(fallbackClassID: Int) -> ClassMyActivity { + ClassMyActivity( + classID: classId ?? fallbackClassID, + receivedLikeCount: receivedLikeCount ?? 0, + commentCount: commentCount ?? 0, + comments: comments?.compactMap { $0.toDomain() } ?? [], + nextCursor: nextCursor, + hasNext: hasNext ?? false + ) + } +} diff --git a/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Result.swift b/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Result.swift index cbf51ed7..aacd394b 100644 --- a/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Result.swift +++ b/Projects/Domain/ClassDomain/Sources/Repository/ClassRepositoryImpl+Result.swift @@ -52,6 +52,26 @@ extension ClassRepositoryImpl { } } + public func fetchMyActivity( + roomID: Int, + cursor: String? + ) async throws -> ClassMyActivity { + do { + let payload = try await client.send( + ClassResultService.myActivity(roomID, cursor: cursor), + as: ClassMyActivityDTO.self + ) + return payload.toDomain(fallbackClassID: roomID) + } catch let error as PickeNetworkError { + if case let .response(response) = error { + throw Self.resultError(code: response.code, message: response.message) + } + throw ClassError.from(error) + } catch { + throw ClassError.from(error) + } + } + static func resultError( code: String?, message: String? diff --git a/Projects/Domain/ClassDomain/Sources/UseCase/ClassUseCaseImpl+Result.swift b/Projects/Domain/ClassDomain/Sources/UseCase/ClassUseCaseImpl+Result.swift index 6b6d188b..cdbf3fb9 100644 --- a/Projects/Domain/ClassDomain/Sources/UseCase/ClassUseCaseImpl+Result.swift +++ b/Projects/Domain/ClassDomain/Sources/UseCase/ClassUseCaseImpl+Result.swift @@ -24,4 +24,11 @@ extension ClassUseCaseImpl { public func retryInsight(roomID: Int) async throws { try await repository.retryInsight(roomID: roomID) } + + public func fetchMyActivity( + roomID: Int, + cursor: String? + ) async throws -> ClassMyActivity { + try await repository.fetchMyActivity(roomID: roomID, cursor: cursor) + } } diff --git a/Projects/Domain/ClassDomain/Tests/Sources/ClassResultAPIContractTests.swift b/Projects/Domain/ClassDomain/Tests/Sources/ClassResultAPIContractTests.swift index b9d987a4..f32d4345 100644 --- a/Projects/Domain/ClassDomain/Tests/Sources/ClassResultAPIContractTests.swift +++ b/Projects/Domain/ClassDomain/Tests/Sources/ClassResultAPIContractTests.swift @@ -163,4 +163,64 @@ struct ClassResultAPIContractTests { #expect(insight.evidence.first?.sourceID == 50) #expect(insight.highlights.first?.content == "대표 의견") } + + @Test + func 내_활동_경로와_커서_쿼리가_Android와_일치한다() throws { + let first = ClassResultService.myActivity(5, cursor: nil) + let next = ClassResultService.myActivity(5, cursor: "c-2") + #expect(first.path == "classes/5/result/my-activity") + #expect(try first.url().path == "/api/v1/classes/5/result/my-activity") + #expect(first.method == .get) + + let encoder = JSONEncoder() + guard let firstParameters = first.parameters, let nextParameters = next.parameters else { + Issue.record("내 활동 요청 파라미터가 없습니다.") + return + } + let firstJSON = try JSONSerialization.jsonObject(with: encoder.encode(firstParameters)) as? [String: Any] + let nextJSON = try JSONSerialization.jsonObject(with: encoder.encode(nextParameters)) as? [String: Any] + #expect(firstJSON?.isEmpty == true) + #expect(nextJSON?["cursor"] as? String == "c-2") + } + + @Test + func 내_활동_응답을_매핑하고_ID_없는_댓글은_버린다() throws { + let json = """ + { + "classId": 5, "receivedLikeCount": 12, "commentCount": 3, + "comments": [ + { "commentId": 71, "perspectiveId": 9, "perspectiveTitle": "처벌을 강화해야 한다", + "stance": "찬성", "content": "동의해요", "likeCount": 2, + "createdAt": "2026-11-02T10:00:00.000Z" }, + { "commentId": null, "perspectiveId": 9, "content": "버려질 댓글" }, + { "commentId": 72, "perspectiveId": 10 } + ], + "nextCursor": "c-2", "hasNext": true + } + """ + let activity = try JSONDecoder().decode(ClassMyActivityDTO.self, from: Data(json.utf8)) + .toDomain(fallbackClassID: 99) + #expect(activity.classID == 5) + #expect(activity.receivedLikeCount == 12) + #expect(activity.commentCount == 3) + #expect(activity.comments.map(\.id) == [71, 72]) + #expect(activity.comments.first?.perspectiveTitle == "처벌을 강화해야 한다") + #expect(activity.comments.first?.createdAt == ServerDateParser.parse("2026-11-02T10:00:00.000Z")) + #expect(activity.comments.last?.perspectiveTitle == "") + #expect(activity.comments.last?.likeCount == 0) + #expect(activity.comments.last?.createdAt == nil) + #expect(activity.nextCursor == "c-2") + #expect(activity.hasNext) + + let empty = try JSONDecoder().decode(ClassMyActivityDTO.self, from: Data("{}".utf8)) + .toDomain(fallbackClassID: 99) + #expect(empty == ClassMyActivity( + classID: 99, + receivedLikeCount: 0, + commentCount: 0, + comments: [], + nextCursor: nil, + hasNext: false + )) + } } diff --git a/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift b/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift index d66a31c2..c866a13d 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift @@ -276,6 +276,13 @@ struct ClassOwnerDashboardTests { func retryInsight(roomID _: Int) async throws { throw ClassAIFeedbackError.temporarilyUnavailable } + + func fetchMyActivity( + roomID _: Int, + cursor _: String? + ) async throws -> ClassMyActivity { + throw ClassAIFeedbackError.temporarilyUnavailable + } } private func serverResult( diff --git a/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift b/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift index f866f9e2..970c4f68 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift @@ -63,6 +63,13 @@ struct ClassReportTests { func retryInsight(roomID _: Int) async throws { throw ClassAIFeedbackError.temporarilyUnavailable } + + func fetchMyActivity( + roomID _: Int, + cursor _: String? + ) async throws -> ClassMyActivity { + throw ClassAIFeedbackError.temporarilyUnavailable + } } private func result( diff --git a/Projects/Service/API/Sources/Class/Result/ClassResultAPI.swift b/Projects/Service/API/Sources/Class/Result/ClassResultAPI.swift index 62b20f2d..88e336f9 100644 --- a/Projects/Service/API/Sources/Class/Result/ClassResultAPI.swift +++ b/Projects/Service/API/Sources/Class/Result/ClassResultAPI.swift @@ -3,6 +3,7 @@ public enum ClassResultAPI { case myReport(classID: Int) case insight(classID: Int) case retryInsight(classID: Int) + case myActivity(classID: Int) public var description: String { switch self { @@ -10,6 +11,7 @@ public enum ClassResultAPI { case let .myReport(id): return "classes/\(id)/result/my-report" case let .insight(id): return "classes/\(id)/result/insight" case let .retryInsight(id): return "classes/\(id)/result/insight/retry" + case let .myActivity(id): return "classes/\(id)/result/my-activity" } } } diff --git a/Projects/Service/APIEndpoint/Sources/Class/Result/ClassResultService.swift b/Projects/Service/APIEndpoint/Sources/Class/Result/ClassResultService.swift index d282d4a7..1dc16841 100644 --- a/Projects/Service/APIEndpoint/Sources/Class/Result/ClassResultService.swift +++ b/Projects/Service/APIEndpoint/Sources/Class/Result/ClassResultService.swift @@ -7,6 +7,7 @@ public enum ClassResultService { case myReport(Int) case insight(Int) case retryInsight(Int) + case myActivity(Int, cursor: String?) } extension ClassResultService: PickeDataRequest { @@ -20,17 +21,21 @@ extension ClassResultService: PickeDataRequest { case let .myReport(id): return ClassResultAPI.myReport(classID: id).description case let .insight(id): return ClassResultAPI.insight(classID: id).description case let .retryInsight(id): return ClassResultAPI.retryInsight(classID: id).description + case let .myActivity(id, _): return ClassResultAPI.myActivity(classID: id).description } } public var method: HTTPMethod { switch self { - case .result, .myReport, .insight: return .get + case .result, .myReport, .insight, .myActivity: return .get case .retryInsight: return .post } } public var parameters: (any Encodable & Sendable)? { - nil + switch self { + case let .myActivity(_, cursor): return MyActivityQuery(cursor: cursor) + case .result, .myReport, .insight, .retryInsight: return nil + } } } diff --git a/Projects/Service/APIEndpoint/Sources/Class/Result/Request/ClassResultRequests.swift b/Projects/Service/APIEndpoint/Sources/Class/Result/Request/ClassResultRequests.swift new file mode 100644 index 00000000..0b4c0391 --- /dev/null +++ b/Projects/Service/APIEndpoint/Sources/Class/Result/Request/ClassResultRequests.swift @@ -0,0 +1,5 @@ +import Foundation + +struct MyActivityQuery: Encodable, Sendable { + let cursor: String? +} From 7f4eec01a0d85c9d579e7e3c9ca77b47fa78a4ba Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 02:23:13 +0900 Subject: [PATCH 08/21] =?UTF-8?q?=EC=9D=98=EA=B2=AC=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=20=EC=8B=A0=EA=B3=A0=EB=A5=BC=20=EC=84=9C=EB=B2=84=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=BD=94=EB=93=9C=EB=A1=9C=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Android ReportPerspectiveUseCase와 같이 REPORT_409 응답을 PerspectiveError.alreadyReported로 변환 - 그 밖의 오류는 기존 네트워크 오류 그대로 전달(일반 배틀은 try? 호출이라 동작 변화 없음) - 중복 코드와 다른 오류 코드 저장소 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Interface/Sources/PerspectiveError.swift | 3 ++ .../PerspectiveRepositoryImpl.swift | 16 +++++-- .../Sources/PerspectiveRepositoryTests.swift | 46 +++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Projects/Domain/PerspectiveDomain/Interface/Sources/PerspectiveError.swift b/Projects/Domain/PerspectiveDomain/Interface/Sources/PerspectiveError.swift index b4e612ae..21820860 100644 --- a/Projects/Domain/PerspectiveDomain/Interface/Sources/PerspectiveError.swift +++ b/Projects/Domain/PerspectiveDomain/Interface/Sources/PerspectiveError.swift @@ -15,6 +15,8 @@ public enum PerspectiveError: LocalizedError, Equatable { case backendError(String) case serverError(Int) case unknown(String) + /// 서버 `REPORT_409`: 이미 신고한 의견. + case alreadyReported public var errorDescription: String? { switch self { @@ -25,6 +27,7 @@ public enum PerspectiveError: LocalizedError, Equatable { case let .backendError(message): return "perspective 처리 실패: \(message)" case let .serverError(code): return "서버 오류 (코드: \(code))" case let .unknown(message): return "알 수 없는 오류: \(message)" + case .alreadyReported: return "이미 신고한 사용자입니다." } } } diff --git a/Projects/Domain/PerspectiveDomain/Sources/Repository/PerspectiveRepositoryImpl.swift b/Projects/Domain/PerspectiveDomain/Sources/Repository/PerspectiveRepositoryImpl.swift index 9e0d5d72..91ca1171 100644 --- a/Projects/Domain/PerspectiveDomain/Sources/Repository/PerspectiveRepositoryImpl.swift +++ b/Projects/Domain/PerspectiveDomain/Sources/Repository/PerspectiveRepositoryImpl.swift @@ -120,10 +120,18 @@ public final class PerspectiveRepositoryImpl: PerspectiveInterface, @unchecked S } public func reportPerspective(perspectiveId: Int) async throws { - _ = try await client.send( - PerspectiveService.reportPerspective(perspectiveId: perspectiveId), - as: PickeEmptyResponse.self - ) + do { + _ = try await client.send( + PerspectiveService.reportPerspective(perspectiveId: perspectiveId), + as: PickeEmptyResponse.self + ) + } catch let error as PickeNetworkError { + // Android ReportPerspectiveUseCase와 같이 중복 신고는 서버 오류 코드로 구분한다. + if case let .response(response) = error, response.code == "REPORT_409" { + throw PerspectiveError.alreadyReported + } + throw error + } } public func reportComment(perspectiveId: Int, commentId: Int) async throws { diff --git a/Projects/Domain/PerspectiveDomain/Tests/Sources/PerspectiveRepositoryTests.swift b/Projects/Domain/PerspectiveDomain/Tests/Sources/PerspectiveRepositoryTests.swift index 6df12f23..feb7a7ac 100644 --- a/Projects/Domain/PerspectiveDomain/Tests/Sources/PerspectiveRepositoryTests.swift +++ b/Projects/Domain/PerspectiveDomain/Tests/Sources/PerspectiveRepositoryTests.swift @@ -104,6 +104,52 @@ struct PerspectiveRepositoryTests { } } + // MARK: - reportPerspective + + @Test + func reportPerspective_duplicateCode_throwsAlreadyReported() async throws { + let json = """ + { + "statusCode": 409, + "data": null, + "error": { "code": "REPORT_409", "message": "이미 신고한 관점입니다" } + } + """ + let repo = withDependencies { + $0.networkClient = StubNetworkClient(stubData: Data(json.utf8)) + } operation: { + PerspectiveRepositoryImpl() + } + + await #expect(throws: PerspectiveError.alreadyReported) { + try await repo.reportPerspective(perspectiveId: 42) + } + } + + @Test + func reportPerspective_otherErrorCode_keepsNetworkResponseError() async throws { + let json = """ + { + "statusCode": 404, + "data": null, + "error": { "code": "PERSPECTIVE_404", "message": "존재하지 않는 관점입니다" } + } + """ + let repo = withDependencies { + $0.networkClient = StubNetworkClient(stubData: Data(json.utf8)) + } operation: { + PerspectiveRepositoryImpl() + } + + await expectNetworkResponseError( + statusCode: 404, + code: "PERSPECTIVE_404", + message: "존재하지 않는 관점입니다" + ) { + try await repo.reportPerspective(perspectiveId: 42) + } + } + // MARK: - fetchLabeledComments @Test From b6eabc0007ebd5a0546addc14cfb510dc8247163 Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 15:47:33 +0900 Subject: [PATCH 09/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=A6=AC?= =?UTF-8?q?=ED=8F=AC=ED=8A=B8=EC=99=80=20=EB=82=B4=EA=B0=80=20=EB=82=A8?= =?UTF-8?q?=EA=B8=B4=20=EC=9D=98=EA=B2=AC=EC=9D=84=20=EB=82=B4=20=ED=99=9C?= =?UTF-8?q?=EB=8F=99=20API=EB=A1=9C=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 리포트 내 투표 결과 활동 행을 Android와 같이 댓글·대댓글·받은 추천 3열로 변경(받은 추천은 내 활동 API, 모르면 —) - 내 활동 요청은 요청 ID로 늦은 응답을 거르고 재조회·뒤로·닫기·라우트 제거 시 취소 - 내가 남긴 의견: 공개된 내 의견은 남긴 댓글, 내 활동 댓글은 남긴 대댓글(원 의견 제목·내 이름의 답글)로 표시 - 의견과 대댓글을 따로 보관해 응답 순서와 무관하게 유지하고, 목록 끝에서 커서로 다음 대댓글을 이어 붙임(첫 페이지는 교체) - 내 활동·멤버 조회 실패는 의견을 유지한 채 "정보를 불러오지 못했어요" 토스트 - 리포트·내 의견 리듀서 테스트 추가 및 서버 의견 행 기대값 갱신 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Navigation/Class/ClassCoordinator.swift | 1 + .../Reducer/ClassMyOpinionsFeature.swift | 201 +++++++++++++++--- .../View/ClassMyOpinionsView.swift | 17 +- .../Reducer/ClassReportFeature.swift | 40 ++++ .../ClassReport/View/ClassReportView.swift | 6 +- .../Tests/Sources/ClassMyOpinionsTests.swift | 193 ++++++++++++++++- .../Tests/Sources/ClassReportTests.swift | 90 +++++++- 7 files changed, 510 insertions(+), 38 deletions(-) diff --git a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift index 33f0edbc..3244ae79 100644 --- a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift +++ b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift @@ -501,6 +501,7 @@ private extension ClassCoordinator { [ .cancel(id: ClassReportFeature.CancelID.load(id)), .cancel(id: ClassReportFeature.CancelID.generate(id)), + .cancel(id: ClassReportFeature.CancelID.myActivity(id)), ] } + cancelledDashboards.flatMap { id in diff --git a/Projects/Feature/Class/Sources/ClassMyOpinions/Reducer/ClassMyOpinionsFeature.swift b/Projects/Feature/Class/Sources/ClassMyOpinions/Reducer/ClassMyOpinionsFeature.swift index a7819431..480d67e7 100644 --- a/Projects/Feature/Class/Sources/ClassMyOpinions/Reducer/ClassMyOpinionsFeature.swift +++ b/Projects/Feature/Class/Sources/ClassMyOpinions/Reducer/ClassMyOpinionsFeature.swift @@ -15,10 +15,20 @@ public struct ClassMyOpinionsFeature { @ObservableState public struct State: Equatable { + /// 레거시 리포트 경로(서버 결과가 없을 때) 코디네이터가 채우는 스냅샷. public var opinions: [ClassReportOpinion] = [] + /// 서버 경로: 클래스에서 내가 쓴 공개 의견(Android `남긴 댓글`). + public var perspectiveOpinions: [ClassReportOpinion] = [] + /// 서버 경로: 내 활동 API의 내가 남긴 댓글(Android `남긴 대댓글`). + public var replyOpinions: [ClassReportOpinion] = [] public var classID: Int? public var isLoading = false public var errorMessage: String? + public var myDisplayName: String? + public var nextCursor: String? + public var hasNext = false + public var isLoadingMore = false + public var toastMessage: String? public var comments: [ClassMyOpinionRow] { rows.filter { $0.opinion.kind == .comment } @@ -32,10 +42,18 @@ public struct ClassMyOpinionsFeature { rows.filter { $0.opinion.kind == .unspecified } } - public var isEmpty: Bool { opinions.isEmpty } + public var isEmpty: Bool { + opinions.isEmpty && perspectiveOpinions.isEmpty && replyOpinions.isEmpty + } + + public var canLoadMoreReplies: Bool { + hasNext && nextCursor != nil && !isLoadingMore + } private var rows: [ClassMyOpinionRow] { - opinions.enumerated().map { ClassMyOpinionRow(index: $0.offset, opinion: $0.element) } + (opinions + perspectiveOpinions + replyOpinions) + .enumerated() + .map { ClassMyOpinionRow(index: $0.offset, opinion: $0.element) } } public init() {} @@ -53,16 +71,22 @@ public struct ClassMyOpinionsFeature { case onAppear case retryTapped case backTapped + case repliesEndReached + case toastShown } @CasePathable public enum AsyncAction: Equatable { case fetch + case fetchReplies + case fetchReplyPage(cursor: String?) } @CasePathable public enum InnerAction: Equatable { case perspectivesResponse(Result<[ClassPerspectiveItem], ClassBattleError>) + case myDisplayNameResponse(Result) + case replyPageResponse(cursor: String?, Result) } @CasePathable @@ -72,39 +96,127 @@ public struct ClassMyOpinionsFeature { public enum CancelID: Hashable, Sendable { case fetch + case replies + case moreReplies } + static let loadFailedMessage = "정보를 불러오지 못했어요" + @Dependency(\.classBattleUseCase) private var classUseCase + @Dependency(\.classMemberUseCase) private var memberUseCase + @Dependency(\.classResultUseCase) private var resultUseCase public var body: some Reducer { Reduce { state, action in switch action { - case .view(.onAppear): - guard state.classID != nil, !state.isLoading, state.opinions.isEmpty, state.errorMessage == nil else { return .none } - return .send(.async(.fetch)) - case .view(.retryTapped): - guard state.classID != nil, !state.isLoading else { return .none } - return .send(.async(.fetch)) - case .view(.backTapped): - return .merge(.cancel(id: CancelID.fetch), .send(.delegate(.dismiss))) - case .async(.fetch): - guard let classID = state.classID else { return .none } - state.isLoading = true - state.errorMessage = nil - return .run { [useCase = classUseCase] send in - let result = await Result { try await useCase.fetchMyPerspectives(classID: classID) } - .mapError { ($0 as? ClassBattleError) ?? .network($0.localizedDescription) } - await send(.inner(.perspectivesResponse(result))) + case let .view(viewAction): + return handleViewAction(state: &state, action: viewAction) + case let .async(asyncAction): + return handleAsyncAction(state: &state, action: asyncAction) + case let .inner(innerAction): + return handleInnerAction(state: &state, action: innerAction) + case .delegate: + return .none + } + } + } + + private func handleViewAction( + state: inout State, + action: View + ) -> Effect { + switch action { + case .onAppear: + guard state.classID != nil, + !state.isLoading, + state.perspectiveOpinions.isEmpty, + state.replyOpinions.isEmpty, + state.errorMessage == nil else { return .none } + return .merge(.send(.async(.fetch)), .send(.async(.fetchReplies))) + case .retryTapped: + guard state.classID != nil, !state.isLoading else { return .none } + state.perspectiveOpinions = [] + state.replyOpinions = [] + state.nextCursor = nil + state.hasNext = false + state.isLoadingMore = false + return .concatenate( + .cancel(id: CancelID.replies), + .cancel(id: CancelID.moreReplies), + .merge(.send(.async(.fetch)), .send(.async(.fetchReplies))) + ) + case .backTapped: + return .merge( + .cancel(id: CancelID.fetch), + .cancel(id: CancelID.replies), + .cancel(id: CancelID.moreReplies), + .send(.delegate(.dismiss)) + ) + case .repliesEndReached: + guard state.canLoadMoreReplies else { return .none } + return .send(.async(.fetchReplyPage(cursor: state.nextCursor))) + case .toastShown: + state.toastMessage = nil + return .none + } + } + + private func handleAsyncAction( + state: inout State, + action: AsyncAction + ) -> Effect { + switch action { + case .fetch: + guard let classID = state.classID else { return .none } + state.isLoading = true + state.errorMessage = nil + return .run { [useCase = classUseCase] send in + let result = await Result { try await useCase.fetchMyPerspectives(classID: classID) } + .mapError { ($0 as? ClassBattleError) ?? .network($0.localizedDescription) } + await send(.inner(.perspectivesResponse(result))) + } + .cancellable(id: CancelID.fetch, cancelInFlight: true) + case .fetchReplies: + guard let classID = state.classID else { return .none } + return .run { [memberUseCase] send in + let result = await Result { + let list = try await memberUseCase.fetchMembers(classID: classID) + return list.members.first { $0.id == list.currentMemberID }?.name ?? "" + } + .mapError(ClassError.from) + await send(.inner(.myDisplayNameResponse(result))) + } + .cancellable(id: CancelID.replies, cancelInFlight: true) + case let .fetchReplyPage(cursor): + guard let classID = state.classID else { return .none } + if cursor != nil { state.isLoadingMore = true } + return .run { [resultUseCase] send in + let result = await Result { + try await resultUseCase.fetchMyActivity(roomID: classID, cursor: cursor) } - .cancellable(id: CancelID.fetch, cancelInFlight: true) - case let .inner(.perspectivesResponse(result)): - state.isLoading = false - switch result { - case let .success(items): - state.opinions = items.map { + .mapError(ClassError.from) + await send(.inner(.replyPageResponse(cursor: cursor, result))) + } + .cancellable(id: cursor == nil ? CancelID.replies : CancelID.moreReplies, cancelInFlight: true) + } + } + + private func handleInnerAction( + state: inout State, + action: InnerAction + ) -> Effect { + switch action { + case let .perspectivesResponse(result): + state.isLoading = false + switch result { + case let .success(items): + // Android `PerspectiveStatus.from`처럼 알 수 없는 상태는 공개로 보고 검수 중·거절만 뺀다. + state.perspectiveOpinions = items + .filter { !["PENDING", "REJECTED"].contains($0.status?.uppercased() ?? "") } + .map { ClassReportOpinion( id: String($0.id), - kind: .unspecified, + kind: .comment, body: $0.content, stance: $0.option.stance ?? $0.option.title, likeCount: $0.likeCount, @@ -112,13 +224,42 @@ public struct ClassMyOpinionsFeature { authorName: $0.author.nickname ) } - case let .failure(error): - state.errorMessage = error.message - } - return .none - case .delegate: + case let .failure(error): + state.errorMessage = error.message + } + return .none + case let .myDisplayNameResponse(result): + switch result { + case let .success(name): + state.myDisplayName = name + return .send(.async(.fetchReplyPage(cursor: nil))) + case .failure: + state.toastMessage = Self.loadFailedMessage return .none } + case let .replyPageResponse(cursor, result): + state.isLoadingMore = false + switch result { + case let .success(activity): + let name = state.myDisplayName.flatMap { $0.isEmpty ? nil : $0 } ?? "나" + let page = activity.comments.map { comment in + ClassReportOpinion( + id: "reply-\(comment.id)", + kind: .reply, + body: comment.content, + likeCount: comment.likeCount, + authorName: name, + parent: ClassReportReplyContext(authorName: nil, stance: nil, body: comment.perspectiveTitle) + ) + } + // 첫 페이지는 교체하고 다음 페이지만 이어 붙여 다시 나타날 때 중복되지 않게 한다. + state.replyOpinions = cursor == nil ? page : state.replyOpinions + page + state.nextCursor = activity.nextCursor + state.hasNext = activity.hasNext + case .failure: + state.toastMessage = Self.loadFailedMessage + } + return .none } } } diff --git a/Projects/Feature/Class/Sources/ClassMyOpinions/View/ClassMyOpinionsView.swift b/Projects/Feature/Class/Sources/ClassMyOpinions/View/ClassMyOpinionsView.swift index c8ba407f..ee1163ea 100644 --- a/Projects/Feature/Class/Sources/ClassMyOpinions/View/ClassMyOpinionsView.swift +++ b/Projects/Feature/Class/Sources/ClassMyOpinions/View/ClassMyOpinionsView.swift @@ -28,6 +28,11 @@ public struct ClassMyOpinionsView: View { .screenBackground() .toolbar(.hidden, for: .navigationBar) .onAppear { send(.onAppear) } + .onChange(of: store.toastMessage) { _, message in + guard let message else { return } + ToastManager.shared.showError(message) + send(.toastShown) + } } } @@ -77,13 +82,16 @@ private extension ClassMyOpinionsView { } else if store.isEmpty { emptyState } else { - VStack(alignment: .leading, spacing: 20) { + LazyVStack(alignment: .leading, spacing: 20) { if !store.comments.isEmpty { opinionSection("남긴 댓글", rows: store.comments, headingGap: 16) } if !store.replies.isEmpty { opinionSection("남긴 대댓글", rows: store.replies, headingGap: 12) } + if store.canLoadMoreReplies { + loadMoreRepliesIndicator + } if !store.unspecified.isEmpty { opinionSection("남긴 의견", rows: store.unspecified, headingGap: 12) } @@ -96,6 +104,13 @@ private extension ClassMyOpinionsView { .scrollIndicators(.hidden) } + /// 목록 끝에 닿으면 다음 대댓글 페이지를 불러온다(Android `CommentsEndReach`). + var loadMoreRepliesIndicator: some View { + ProgressView() + .frame(maxWidth: .infinity) + .onAppear { send(.repliesEndReached) } + } + var emptyState: some View { Text("아직 남긴 의견이 없어요.") .pretendardFont(.regular13) diff --git a/Projects/Feature/Class/Sources/ClassReport/Reducer/ClassReportFeature.swift b/Projects/Feature/Class/Sources/ClassReport/Reducer/ClassReportFeature.swift index 9288d770..a6ed1dbf 100644 --- a/Projects/Feature/Class/Sources/ClassReport/Reducer/ClassReportFeature.swift +++ b/Projects/Feature/Class/Sources/ClassReport/Reducer/ClassReportFeature.swift @@ -33,6 +33,8 @@ public struct ClassReportFeature { public var insight: ClassAIInsight? public var insightError: ClassAIFeedbackError? public var insightRequestID: UUID? + public var receivedLikeCount: Int? + public var myActivityRequestID: UUID? public var isGeneratingFeedback = false public var unknownRetryCount = 0 @@ -241,6 +243,11 @@ public struct ClassReportFeature { feedback.map { String($0.commentCount) } ?? "—" } + /// Android와 같이 받은 추천 수를 모르면 0 대신 "—"로 표시한다. + public var personalReceivedLikeCountText: String { + receivedLikeCount.map(String.init) ?? "—" + } + public var myOpinionsButtonTitle: String { if classResult != nil, feedback == nil { return "내가 남긴 의견 보기" } return "내가 남긴 의견 \(myOpinionCount)개 보기" @@ -285,6 +292,7 @@ public struct ClassReportFeature { case fetchVoteHistory case generateFeedback case fetchInsight + case fetchMyActivity } @CasePathable @@ -300,6 +308,7 @@ public struct ClassReportFeature { wasUnknown: Bool ) case insightResponse(instanceID: UUID, requestID: UUID, roomID: Int, Result) + case myActivityResponse(instanceID: UUID, requestID: UUID, roomID: Int, Result) } @CasePathable @@ -313,6 +322,7 @@ public struct ClassReportFeature { case voteHistory(UUID) case generate(UUID) case insight(UUID) + case myActivity(UUID) } @Dependency(\.classResultUseCase) private var classUseCase @@ -360,12 +370,14 @@ extension ClassReportFeature { state.voteHistoryRequestID = nil state.feedbackRequestID = nil state.insightRequestID = nil + state.myActivityRequestID = nil state.isGeneratingFeedback = false return .merge( .cancel(id: CancelID.load(state.instanceID)), .cancel(id: CancelID.voteHistory(state.instanceID)), .cancel(id: CancelID.generate(state.instanceID)), .cancel(id: CancelID.insight(state.instanceID)), + .cancel(id: CancelID.myActivity(state.instanceID)), .send(.delegate(.dismiss)) ) case let .tabSelected(tab): @@ -398,6 +410,8 @@ extension ClassReportFeature { state.feedbackError = nil state.isGeneratingFeedback = false state.unknownRetryCount = 0 + state.receivedLikeCount = nil + state.myActivityRequestID = nil let requestID = uuid() state.requestID = requestID state.sourceViewState = .loading @@ -422,8 +436,25 @@ extension ClassReportFeature { .cancel(id: CancelID.voteHistory(state.instanceID)), .cancel(id: CancelID.generate(state.instanceID)), .cancel(id: CancelID.insight(state.instanceID)), + .cancel(id: CancelID.myActivity(state.instanceID)), fetch ) + case .fetchMyActivity: + let requestID = uuid() + state.myActivityRequestID = requestID + return .run { [classUseCase, instanceID = state.instanceID, roomID = state.room.id] send in + let result = await Result { + try await classUseCase.fetchMyActivity(roomID: roomID, cursor: nil) + } + .mapError(ClassError.from) + await send(.inner(.myActivityResponse( + instanceID: instanceID, + requestID: requestID, + roomID: roomID, + result + ))) + } + .cancellable(id: CancelID.myActivity(state.instanceID), cancelInFlight: true) case .fetchVoteHistory: let requestID = uuid() state.voteHistoryRequestID = requestID @@ -513,6 +544,7 @@ extension ClassReportFeature { } if classResult.myReportState != nil { effects.append(.send(.async(.generateFeedback))) + effects.append(.send(.async(.fetchMyActivity))) } if state.room.role != .member, (classResult.insightState == "SUCCEEDED" || classResult.insightState == "LIMITED") { @@ -526,6 +558,14 @@ extension ClassReportFeature { return .none } + case let .myActivityResponse(instanceID, requestID, roomID, result): + guard state.instanceID == instanceID, + state.myActivityRequestID == requestID, + state.room.id == roomID else { return .none } + state.myActivityRequestID = nil + state.receivedLikeCount = try? result.get().receivedLikeCount + return .none + case let .voteHistoryResponse(instanceID, requestID, roomID, result): guard state.instanceID == instanceID, state.voteHistoryRequestID == requestID, diff --git a/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift b/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift index 1fa23394..995de24f 100644 --- a/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift +++ b/Projects/Feature/Class/Sources/ClassReport/View/ClassReportView.swift @@ -130,9 +130,11 @@ private extension ClassReportView { .padding(.vertical, 12) .background(.beige300, in: RoundedRectangle(cornerRadius: 2)) HStack(spacing: 0) { - activityCount(store.personalPerspectiveCountText, label: "작성한 의견") + activityCount(store.personalPerspectiveCountText, label: "댓글") Rectangle().fill(.beige600).frame(width: 1, height: 38) - activityCount(store.personalCommentCountText, label: "작성한 댓글") + activityCount(store.personalCommentCountText, label: "대댓글") + Rectangle().fill(.beige600).frame(width: 1, height: 38) + activityCount(store.personalReceivedLikeCountText, label: "받은 추천") } } else if store.voteHistoryRequestID != nil { SkeletonView(.round(cornerRadius: 2)).frame(height: 48) diff --git a/Projects/Feature/Class/Tests/Sources/ClassMyOpinionsTests.swift b/Projects/Feature/Class/Tests/Sources/ClassMyOpinionsTests.swift index 2befa8cf..eb37279a 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassMyOpinionsTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassMyOpinionsTests.swift @@ -83,9 +83,9 @@ struct ClassMyOpinionsTests { let store = TestStore(initialState: state) { ClassMyOpinionsFeature() } await store.send(.inner(.perspectivesResponse(.success([item])))) { $0.isLoading = false - $0.opinions = [ClassReportOpinion( + $0.perspectiveOpinions = [ClassReportOpinion( id: "77", - kind: .unspecified, + kind: .comment, body: "내 생각", stance: "찬성", likeCount: 4, @@ -93,6 +93,193 @@ struct ClassMyOpinionsTests { authorName: "나" )] } - #expect(store.state.unspecified.count == 1) + #expect(store.state.comments.count == 1) + } + + @Test + func 서버_내_의견은_검수_중과_거절만_빼고_알_수_없는_상태는_공개로_본다() async { + var state = ClassMyOpinionsFeature.State() + state.classID = 31 + state.isLoading = true + let store = TestStore(initialState: state) { ClassMyOpinionsFeature() } + store.exhaustivity = .off + + await store.send(.inner(.perspectivesResponse(.success([ + perspective(id: 1, status: "PUBLISHED"), + perspective(id: 2, status: "PENDING"), + perspective(id: 3, status: "rejected"), + perspective(id: 4, status: nil), + perspective(id: 5, status: "HIDDEN_BY_ADMIN"), + ])))) + + #expect(store.state.perspectiveOpinions.map(\.id) == ["1", "4", "5"]) + } + + @Test + func 내_활동_댓글은_원_의견_제목과_내_이름을_가진_대댓글로_옮긴다() async { + var state = ClassMyOpinionsFeature.State() + state.classID = 31 + let store = TestStore(initialState: state) { ClassMyOpinionsFeature() } + store.exhaustivity = .off + + await store.send(.inner(.myDisplayNameResponse(.success("민지")))) + await store.receive(\.async.fetchReplyPage) + await store.send(.inner(.replyPageResponse(cursor: nil, .success(activity(ids: [71], next: "c-2"))))) + + #expect(store.state.replyOpinions == [ClassReportOpinion( + id: "reply-71", + kind: .reply, + body: "댓글 71", + likeCount: 2, + authorName: "민지", + parent: ClassReportReplyContext(body: "원 의견 71") + )]) + #expect(store.state.replies.first?.replyAuthorLabel == "민지의 답글") + #expect(store.state.replies.first?.parentStanceLabel == nil) + #expect(store.state.nextCursor == "c-2") + #expect(store.state.canLoadMoreReplies) + } + + @Test + func 표시_이름이_비면_나로_표시한다() async { + var state = ClassMyOpinionsFeature.State() + state.classID = 31 + state.myDisplayName = "" + let store = TestStore(initialState: state) { ClassMyOpinionsFeature() } + store.exhaustivity = .off + + await store.send(.inner(.replyPageResponse(cursor: nil, .success(activity(ids: [71], next: nil))))) + + #expect(store.state.replies.first?.replyAuthorLabel == "나의 답글") + } + + @Test + func 의견과_대댓글_응답은_도착_순서와_무관하게_서로를_덮어쓰지_않는다() async { + var state = ClassMyOpinionsFeature.State() + state.classID = 31 + state.isLoading = true + state.myDisplayName = "민지" + let store = TestStore(initialState: state) { ClassMyOpinionsFeature() } + store.exhaustivity = .off + + await store.send(.inner(.replyPageResponse(cursor: nil, .success(activity(ids: [71], next: nil))))) + await store.send(.inner(.perspectivesResponse(.success([perspective(id: 1, status: nil)])))) + + #expect(store.state.comments.map(\.opinion.id) == ["1"]) + #expect(store.state.replies.map(\.opinion.id) == ["reply-71"]) + #expect(!store.state.isEmpty) + } + + @Test + func 목록_끝에서_커서로_다음_대댓글을_이어_붙이고_첫_페이지는_교체한다() async { + var state = ClassMyOpinionsFeature.State() + state.classID = 31 + state.myDisplayName = "민지" + let store = TestStore(initialState: state) { + ClassMyOpinionsFeature() + } withDependencies: { + $0.classResultUseCase = ActivityStub(pages: ["c-2": activity(ids: [72], next: nil)]) + } + store.exhaustivity = .off + + await store.send(.inner(.replyPageResponse(cursor: nil, .success(activity(ids: [71], next: "c-2"))))) + await store.send(.view(.repliesEndReached)) + await store.receive(\.async.fetchReplyPage) + #expect(store.state.isLoadingMore) + await store.receive(\.inner.replyPageResponse) + + #expect(store.state.replyOpinions.map(\.id) == ["reply-71", "reply-72"]) + #expect(!store.state.canLoadMoreReplies) + #expect(!store.state.isLoadingMore) + + await store.send(.inner(.replyPageResponse(cursor: nil, .success(activity(ids: [71], next: nil))))) + #expect(store.state.replyOpinions.map(\.id) == ["reply-71"]) + } + + @Test + func 내_활동_조회에_실패하면_의견은_유지하고_토스트를_띄운다() async { + var state = ClassMyOpinionsFeature.State() + state.classID = 31 + state.perspectiveOpinions = [ClassReportOpinion(id: "1", kind: .comment, body: "내 의견")] + let store = TestStore(initialState: state) { ClassMyOpinionsFeature() } + store.exhaustivity = .off + + await store.send(.inner(.replyPageResponse(cursor: nil, .failure(.network("실패"))))) + #expect(store.state.toastMessage == "정보를 불러오지 못했어요") + #expect(store.state.perspectiveOpinions.count == 1) + + await store.send(.view(.toastShown)) + #expect(store.state.toastMessage == nil) + + await store.send(.inner(.myDisplayNameResponse(.failure(.network("실패"))))) + #expect(store.state.toastMessage == "정보를 불러오지 못했어요") + } + + private func perspective( + id: Int, + status: String? + ) -> ClassPerspectiveItem { + ClassPerspectiveItem( + id: id, + author: .init(userTag: "member", nickname: "나", characterType: nil, characterImageURL: nil), + option: .init(id: 1, label: "A", title: "찬성", stance: "찬성"), + content: "의견 \(id)", + likeCount: 0, + commentCount: 0, + isLiked: false, + isMyPerspective: true, + status: status, + createdAt: .now + ) + } + + private func activity( + ids: [Int], + next: String? + ) -> ClassMyActivity { + ClassMyActivity( + classID: 31, + receivedLikeCount: 0, + commentCount: ids.count, + comments: ids.map { + ClassMyActivityComment( + id: $0, + perspectiveID: $0 + 100, + perspectiveTitle: "원 의견 \($0)", + stance: "", + content: "댓글 \($0)", + likeCount: 2, + createdAt: nil + ) + }, + nextCursor: next, + hasNext: next != nil + ) + } + + private struct ActivityStub: ClassResultInterface { + let pages: [String: ClassMyActivity] + + func fetchResult(roomID _: Int) async throws -> ClassResult { + throw ClassError.network("사용하지 않음") + } + + func fetchPersonalResult(roomID _: Int) async throws -> ClassAIPersonalFeedback { + throw ClassAIFeedbackError.temporarilyUnavailable + } + + func fetchInsight(roomID _: Int) async throws -> ClassAIInsight { + throw ClassAIFeedbackError.temporarilyUnavailable + } + + func retryInsight(roomID _: Int) async throws {} + + func fetchMyActivity( + roomID _: Int, + cursor: String? + ) async throws -> ClassMyActivity { + guard let cursor, let page = pages[cursor] else { throw ClassError.network("페이지 없음") } + return page + } } } diff --git a/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift b/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift index 970c4f68..e060ead8 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassReportTests.swift @@ -38,13 +38,16 @@ struct ClassReportTests { private struct ResultStub: ClassResultInterface { let result: Result let feedback: ClassAIPersonalFeedback? + let myActivity: ClassMyActivity? init( result: Result, - feedback: ClassAIPersonalFeedback? = nil + feedback: ClassAIPersonalFeedback? = nil, + myActivity: ClassMyActivity? = nil ) { self.result = result self.feedback = feedback + self.myActivity = myActivity } func fetchResult(roomID _: Int) async throws -> ClassResult { @@ -68,7 +71,8 @@ struct ClassReportTests { roomID _: Int, cursor _: String? ) async throws -> ClassMyActivity { - throw ClassAIFeedbackError.temporarilyUnavailable + guard let myActivity else { throw ClassError.network("내 활동을 불러오지 못했습니다.") } + return myActivity } } @@ -400,4 +404,86 @@ struct ClassReportTests { await store.receive(\.inner.resultResponse) #expect(store.state.sourceError == .resultExpired) } + + @Test + func 참여자_리포트의_받은_추천은_내_활동_응답으로_표시한다() async { + let room = ClassRoom.mockClosedSociety + let activity = ClassMyActivity( + classID: room.id, + receivedLikeCount: 12, + commentCount: 3, + comments: [], + nextCursor: nil, + hasNext: false + ) + let store = TestStore(initialState: ClassReportFeature.State(room: room, instanceID: UUID())) { + ClassReportFeature() + } withDependencies: { + $0.uuid = .incrementing + $0.classResultUseCase = ResultStub( + result: .success(result(room: room, reportState: "SUCCEEDED")), + myActivity: activity + ) + } + store.exhaustivity = .off + + #expect(store.state.personalReceivedLikeCountText == "—") + await store.send(.view(.onAppear)) + await store.receive(\.async.fetchMyActivity) + await store.receive(\.inner.myActivityResponse) + + #expect(store.state.receivedLikeCount == 12) + #expect(store.state.personalReceivedLikeCountText == "12") + #expect(store.state.myActivityRequestID == nil) + + await store.send(.async(.fetch)) + #expect(store.state.receivedLikeCount == nil) + } + + @Test + func 내_활동_조회에_실패하면_받은_추천을_대시로_둔다() async { + let room = ClassRoom.mockClosedSociety + let store = TestStore(initialState: ClassReportFeature.State(room: room, instanceID: UUID())) { + ClassReportFeature() + } withDependencies: { + $0.uuid = .incrementing + $0.classResultUseCase = ResultStub(result: .success(result(room: room, reportState: "SUCCEEDED"))) + } + store.exhaustivity = .off + + await store.send(.view(.onAppear)) + await store.receive(\.inner.myActivityResponse) + + #expect(store.state.receivedLikeCount == nil) + #expect(store.state.personalReceivedLikeCountText == "—") + } + + @Test + func 개인_리포트_권한이_없으면_내_활동을_조회하지_않는다() async { + let room = ClassRoom.mockClosedSociety + let store = TestStore(initialState: ClassReportFeature.State(room: room, instanceID: UUID())) { + ClassReportFeature() + } withDependencies: { + $0.uuid = .incrementing + $0.classResultUseCase = ResultStub( + result: .success(result(room: room, reportState: nil)), + myActivity: ClassMyActivity( + classID: room.id, + receivedLikeCount: 12, + commentCount: 0, + comments: [], + nextCursor: nil, + hasNext: false + ) + ) + } + store.exhaustivity = .off + + await store.send(.view(.onAppear)) + await store.receive(\.inner.resultResponse) + await store.finish() + + // 조회했다면 대역이 돌려주는 12가 들어간다. + #expect(store.state.receivedLikeCount == nil) + } } From a3c171659d9cd81350a2fa7235b1efb17379abb7 Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 15:56:08 +0900 Subject: [PATCH 10/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=9D=98?= =?UTF-8?q?=EA=B2=AC=20=EB=8D=94=EB=B3=B4=EA=B8=B0=20=EB=A9=94=EB=89=B4?= =?UTF-8?q?=EB=A1=9C=20=EC=8B=A0=EA=B3=A0=C2=B7=EC=88=98=EC=A0=95=C2=B7?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Android PerspectiveItemCard와 같이 남의 의견은 신고, 내 공개 의견은 삭제·수정, 거절된 의견은 수정만, 검수 중은 메뉴 없음 - 공개되지 않은 내 의견(검수중·거절됨)을 목록 위 상태 카드로 표시 - 수정은 입력창에 내용을 채워 PATCH, 삭제·신고는 확인 알림 후 실행하고 목록과 내 의견을 다시 조회 - 삭제 후 같은 화면에서 새 의견을 다시 쓸 수 있게 하고, 신고 결과(접수·중복·실패)는 토스트로 안내 - 의견 변경 요청은 상세 화면 이동에서는 유지하고 라우트 제거 시에만 취소 - 메뉴 종류·수정·취소·삭제·신고·상태 카드·화면 이탈 후 응답 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Navigation/Class/ClassCoordinator.swift | 1 + .../Model/ClassPerspectiveMenuKind.swift | 38 +++ .../Model/ClassPerspectiveRow.swift | 15 +- .../Reducer/ClassBattleFlowFeature.swift | 230 +++++++++++++++++- .../View/ClassBattleFlowView.swift | 118 ++++++++- .../Tests/Sources/ClassBattleFlowTests.swift | 199 +++++++++++++++ 6 files changed, 590 insertions(+), 11 deletions(-) create mode 100644 Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveMenuKind.swift diff --git a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift index 3244ae79..da17fad9 100644 --- a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift +++ b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift @@ -462,6 +462,7 @@ private extension ClassCoordinator { .cancel(id: ClassBattleFlowFeature.CancelID.voteStats(id)), .cancel(id: ClassBattleFlowFeature.CancelID.perspectiveLike(id)), .cancel(id: ClassBattleFlowFeature.CancelID.myPerspective(id)), + .cancel(id: ClassBattleFlowFeature.CancelID.perspectiveMutation(id)), ] } guard !removedBattleFlows.isEmpty else { return .merge(effects) } diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveMenuKind.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveMenuKind.swift new file mode 100644 index 00000000..fb0c6928 --- /dev/null +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveMenuKind.swift @@ -0,0 +1,38 @@ +// +// ClassPerspectiveMenuKind.swift +// Class +// +// Created by Picke on 11/3/26. +// + +import ClassDomainInterface + +/// 클래스 의견 카드 더보기(•••) 메뉴 종류. Android `PerspectiveItemCard.perspectiveMenuItems`와 같다. +public enum ClassPerspectiveMenuKind: Equatable, Sendable { + case hidden + case report + case editOnly + case editAndDelete + + /// 목록 행: 서버가 남의 의견(false)이라고 알려 준 경우만 신고, 내 의견(true)은 상태 규칙, 모르면(nil) 메뉴 없음. + public init(listItem: ClassPerspectiveItem) { + switch listItem.isMyPerspective { + case .some(false): self = .report + case .some(true): self = .init(myStatus: listItem.status) + case .none: self = .hidden + } + } + + /// 내 의견: 검수 중은 메뉴 없음, 거절은 수정만, 그 외는 삭제·수정. + public init(myStatus status: String?) { + switch status?.uppercased() { + case "PENDING": self = .hidden + case "REJECTED": self = .editOnly + default: self = .editAndDelete + } + } + + public var hasMenu: Bool { + self != .hidden + } +} diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveRow.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveRow.swift index 8c212dad..1d6ece51 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveRow.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Model/ClassPerspectiveRow.swift @@ -6,18 +6,31 @@ public struct ClassPerspectiveRow: Equatable, Identifiable { public let perspective: ClassPerspectiveItem public let likeCountLabel: String public let commentCountLabel: String + public let menuKind: ClassPerspectiveMenuKind public var createdAtLabel: String { perspective.createdAt.relativeKoreanString } + /// 공개되지 않은 내 의견의 상태 배지 문구(Android `comment_status_pending/rejected`). + public var statusBadgeTitle: String? { + if isPendingReview { return "검수중" } + if isRejected { return "거절됨" } + return nil + } + + public var isPendingReview: Bool { perspective.status?.uppercased() == "PENDING" } + public var isRejected: Bool { perspective.status?.uppercased() == "REJECTED" } + public init( id: Int, perspective: ClassPerspectiveItem, likeCountLabel: String, - commentCountLabel: String + commentCountLabel: String, + menuKind: ClassPerspectiveMenuKind? = nil ) { self.id = id self.perspective = perspective self.likeCountLabel = likeCountLabel self.commentCountLabel = commentCountLabel + self.menuKind = menuKind ?? ClassPerspectiveMenuKind(listItem: perspective) } } diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift index 7b4dd032..6748587a 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift @@ -7,6 +7,7 @@ import Foundation import HomeDomainInterface import PickeCoreUtility import PerspectiveDomainInterface +import PickeSharedUI @Reducer public struct ClassBattleFlowFeature { @@ -52,6 +53,15 @@ public struct ClassBattleFlowFeature { public var voteStatsRequestID: UUID? public var perspectiveLikeRequestID: UUID? public var myPerspectiveRequestID: UUID? + /// 내 의견 API의 첫 항목. 공개되지 않았으면 목록 위 상태 카드로 보여 준다. + public var myPerspective: ClassPerspectiveItem? + public var menuTargetPerspectiveID: Int? + public var editingPerspectiveID: Int? + public var deleteTargetPerspectiveID: Int? + public var reportTargetPerspectiveID: Int? + public var mutationRequestID: UUID? + public var toastMessage: String? + @Presents public var customAlert: CustomAlertState? public var isAudioReady = false public var isPlaying = false public var hasListened = false @@ -79,6 +89,11 @@ public struct ClassBattleFlowFeature { public var canParticipate: Bool { session?.canParticipate == true } public var canSubmit: Bool { + // 의견 수정은 이미 게시한 의견이 대상이라 게시 조건과 별도로 판단하고, 마감 여부는 서버에 맡긴다. + if editingPerspectiveID != nil { + return mutationRequestID == nil && submitRequestID == nil + && !draft.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + } guard viewState == .loaded, canParticipate, submitRequestID == nil else { return false } guard let step else { return false } switch step { @@ -164,10 +179,39 @@ public struct ClassBattleFlowFeature { } public var shouldShowComposer: Bool { - step == .some(.completed) && canParticipate + if editingPerspectiveID != nil { return true } + return step == .some(.completed) && canParticipate && hasPublishedPerspective == false && !hasSubmittedInCurrentSession } + /// 공개되지 않은 내 의견(검수중·거절됨)이 목록에 없을 때 목록 맨 위에 보여 줄 행. + public var hiddenMyPerspectiveRow: ClassPerspectiveRow? { + guard let myPerspective, + ["PENDING", "REJECTED"].contains(myPerspective.status?.uppercased() ?? ""), + !perspectives.contains(where: { $0.id == myPerspective.id }) else { return nil } + return ClassPerspectiveRow( + id: -1, + perspective: myPerspective, + likeCountLabel: myPerspective.likeCount.decimalFormatted, + commentCountLabel: myPerspective.commentCount.decimalFormatted, + menuKind: ClassPerspectiveMenuKind(myStatus: myPerspective.status) + ) + } + + func perspectiveItem(id: Int) -> ClassPerspectiveItem? { + perspectives.first { $0.id == id } ?? (myPerspective?.id == id ? myPerspective : nil) + } + + func perspectiveMenuKind(id: Int) -> ClassPerspectiveMenuKind { + if let item = perspectives.first(where: { $0.id == id }) { + return ClassPerspectiveMenuKind(listItem: item) + } + if let myPerspective, myPerspective.id == id { + return ClassPerspectiveMenuKind(myStatus: myPerspective.status) + } + return .hidden + } + public var isAudioStep: Bool { step == .some(.preVote) || step == .some(.ttsListening) || (step == .some(.postVote) && needsMidVoteRecovery) @@ -279,9 +323,15 @@ public struct ClassBattleFlowFeature { public enum Action: ViewAction { case view(View) case inner(InnerAction) + case scope(ScopeAction) case delegate(DelegateAction) } + @CasePathable + public enum ScopeAction: Equatable { + case customAlert(PresentationAction) + } + @CasePathable public enum View: Equatable { case onAppear @@ -307,6 +357,13 @@ public struct ClassBattleFlowFeature { case perspectiveSortTapped(String) case perspectiveTapped(Int) case perspectiveLikeTapped(Int) + case perspectiveMoreTapped(Int) + case perspectiveMenuDismissed + case perspectiveEditTapped(Int) + case perspectiveDeleteTapped(Int) + case perspectiveReportTapped(Int) + case cancelEditTapped + case toastShown } public enum InnerAction: Equatable { @@ -321,6 +378,9 @@ public struct ClassBattleFlowFeature { case voteStatsResponse(UUID, Result) case perspectiveLikeResponse(UUID, Int, Result) case myPerspectiveResponse(UUID, Result<[ClassPerspectiveItem], ClassBattleError>) + case updatePerspectiveResponse(UUID, Result) + case deletePerspectiveResponse(UUID, Int, Result) + case reportPerspectiveResponse(UUID, Result) } @CasePathable @@ -341,6 +401,8 @@ public struct ClassBattleFlowFeature { case perspectiveLike(UUID) case myPerspective(UUID) case midVote(UUID) + /// 의견 수정·삭제·신고. 상세 화면 이동(onDisappear)에서는 취소하지 않고 라우트 제거 시에만 취소한다. + case perspectiveMutation(UUID) } @Dependency(\.uuid) private var uuid @@ -354,9 +416,13 @@ public struct ClassBattleFlowFeature { switch action { case let .view(viewAction): return handleView(state: &state, action: viewAction) case let .inner(innerAction): return handleInner(state: &state, action: innerAction) + case let .scope(scopeAction): return handleScope(state: &state, action: scopeAction) case .delegate: return .none } } + .ifLet(\.$customAlert, action: \.scope.customAlert) { + CustomConfirmAlert() + } } } @@ -412,6 +478,7 @@ extension ClassBattleFlowFeature { state.voteStatsRequestID = nil state.myPerspectiveRequestID = nil state.midVoteRequestID = nil + state.menuTargetPerspectiveID = nil state.isLoadingPerspectives = false state.isPlaying = false return stopAudio(instanceID: state.instanceID) @@ -463,6 +530,9 @@ extension ClassBattleFlowFeature { return .none case .submitTapped: guard state.canSubmit else { return .none } + if let editingID = state.editingPerspectiveID { + return updatePerspective(state: &state, id: editingID) + } guard let step = state.step else { return .none } let requestID = uuid() state.submitRequestID = requestID @@ -612,7 +682,116 @@ extension ClassBattleFlowFeature { await send(.inner(.perspectiveLikeResponse(requestID, id, result))) } .cancellable(id: CancelID.perspectiveLike(state.instanceID), cancelInFlight: true) + case let .perspectiveMoreTapped(id): + guard state.perspectiveMenuKind(id: id).hasMenu else { return .none } + state.menuTargetPerspectiveID = state.menuTargetPerspectiveID == id ? nil : id + return .none + case .perspectiveMenuDismissed: + state.menuTargetPerspectiveID = nil + return .none + case let .perspectiveEditTapped(id): + state.menuTargetPerspectiveID = nil + guard [.editOnly, .editAndDelete].contains(state.perspectiveMenuKind(id: id)), + let item = state.perspectiveItem(id: id) else { return .none } + state.editingPerspectiveID = id + state.draft = item.content + state.errorMessage = nil + return .none + case let .perspectiveDeleteTapped(id): + state.menuTargetPerspectiveID = nil + guard state.perspectiveMenuKind(id: id) == .editAndDelete else { return .none } + state.deleteTargetPerspectiveID = id + state.reportTargetPerspectiveID = nil + state.customAlert = .deletePerspective() + return .none + case let .perspectiveReportTapped(id): + state.menuTargetPerspectiveID = nil + guard state.perspectiveMenuKind(id: id) == .report else { return .none } + state.reportTargetPerspectiveID = id + state.deleteTargetPerspectiveID = nil + state.customAlert = .report() + return .none + case .cancelEditTapped: + state.editingPerspectiveID = nil + state.draft = "" + return .none + case .toastShown: + state.toastMessage = nil + return .none + } + } + + private func handleScope( + state: inout State, + action: ScopeAction + ) -> Effect { + switch action { + case .customAlert(.presented(.confirmTapped)): + state.customAlert = nil + if let id = state.deleteTargetPerspectiveID { + state.deleteTargetPerspectiveID = nil + return deletePerspective(state: &state, id: id) + } + if let id = state.reportTargetPerspectiveID { + state.reportTargetPerspectiveID = nil + return reportPerspective(state: &state, id: id) + } + return .none + case .customAlert(.presented(.cancelTapped)), .customAlert(.dismiss): + state.customAlert = nil + state.deleteTargetPerspectiveID = nil + state.reportTargetPerspectiveID = nil + return .none + } + } + + private func updatePerspective( + state: inout State, + id: Int + ) -> Effect { + let requestID = uuid() + state.mutationRequestID = requestID + let content = state.draft.trimmingCharacters(in: .whitespacesAndNewlines) + return .run { [useCase = perspectiveUseCase] send in + let result = await Result { + try await useCase.updatePerspective(perspectiveId: id, content: content) + return true + }.mapError(PerspectiveError.from) + await send(.inner(.updatePerspectiveResponse(requestID, result))) + } + .cancellable(id: CancelID.perspectiveMutation(state.instanceID), cancelInFlight: true) + } + + private func deletePerspective( + state: inout State, + id: Int + ) -> Effect { + let requestID = uuid() + state.mutationRequestID = requestID + return .run { [useCase = perspectiveUseCase] send in + let result = await Result { + try await useCase.deletePerspective(perspectiveId: id) + return true + }.mapError(PerspectiveError.from) + await send(.inner(.deletePerspectiveResponse(requestID, id, result))) + } + .cancellable(id: CancelID.perspectiveMutation(state.instanceID), cancelInFlight: true) + } + + private func reportPerspective( + state: inout State, + id: Int + ) -> Effect { + let requestID = uuid() + state.mutationRequestID = requestID + return .run { [useCase = perspectiveUseCase] send in + let result = await Result { + try await useCase.reportPerspective(perspectiveId: id) + return true + }.mapError(PerspectiveError.from) + await send(.inner(.reportPerspectiveResponse(requestID, result))) } + .cancellable(id: CancelID.perspectiveMutation(state.instanceID), cancelInFlight: true) } private func loadProgress(state: inout State) -> Effect { @@ -883,11 +1062,60 @@ extension ClassBattleFlowFeature { state.myPerspectiveRequestID = nil switch result { case let .success(items): + state.myPerspective = items.first state.hasPublishedPerspective = !items.isEmpty || state.hasSubmittedInCurrentSession case let .failure(error): state.perspectiveErrorMessage = error.message } return .none + case let .updatePerspectiveResponse(requestID, result): + guard state.mutationRequestID == requestID else { return .none } + state.mutationRequestID = nil + switch result { + case .success: + state.editingPerspectiveID = nil + state.draft = "" + return .merge( + loadPerspectives(state: &state, cursor: nil, preserveExisting: true), + loadMyPerspectives(state: &state) + ) + case .failure: + state.toastMessage = "의견을 수정하지 못했어요" + return .none + } + case let .deletePerspectiveResponse(requestID, id, result): + guard state.mutationRequestID == requestID else { return .none } + state.mutationRequestID = nil + switch result { + case .success: + // Android와 같이 삭제한 내 의견을 바로 빼고 새 의견을 다시 쓸 수 있게 한다. + state.perspectives.removeAll { $0.id == id } + if state.myPerspective?.id == id { state.myPerspective = nil } + if state.editingPerspectiveID == id { + state.editingPerspectiveID = nil + state.draft = "" + } + state.hasPublishedPerspective = false + state.hasSubmittedInCurrentSession = false + case .failure: + state.toastMessage = "의견을 삭제하지 못했어요" + } + return .merge( + loadPerspectives(state: &state, cursor: nil, preserveExisting: true), + loadMyPerspectives(state: &state) + ) + case let .reportPerspectiveResponse(requestID, result): + guard state.mutationRequestID == requestID else { return .none } + state.mutationRequestID = nil + switch result { + case .success: + state.toastMessage = "신고가 정상 접수되었습니다." + case .failure(.alreadyReported): + state.toastMessage = "이미 신고한 사용자입니다." + case .failure: + state.toastMessage = "신고하지 못했어요" + } + return .none } } diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift index de744a59..6afe907a 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift @@ -38,6 +38,11 @@ public struct ClassBattleFlowView: View { .toolbar(.hidden, for: .navigationBar) .onAppear { send(.onAppear) } .onDisappear { send(.onDisappear) } + .onChange(of: store.toastMessage) { _, message in + guard let message else { return } + ToastManager.shared.showInfo(message) + send(.toastShown) + } } } @@ -629,6 +634,9 @@ private extension ClassBattleFlowView { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 0) { perspectiveSortRow() + if let row = store.hiddenMyPerspectiveRow { + perspectiveCard(row) + } ForEach(store.perspectiveRows) { row in perspectiveCard(row) } @@ -667,6 +675,7 @@ private extension ClassBattleFlowView { } } .background(.white) + .customAlert($store.scope(state: \.customAlert, action: \.scope.customAlert)) } @ViewBuilder @@ -853,10 +862,59 @@ private extension ClassBattleFlowView { .padding(.horizontal, 16) .padding(.vertical, 12) .frame(maxWidth: .infinity, alignment: .leading) - .background(.white) + .background(row.isPendingReview ? Color.secondary50 : row.isRejected ? .primary50 : .white) .overlay(alignment: .bottom) { Rectangle().fill(.beige600).frame(height: 1) } + .overlay(alignment: .bottomTrailing) { + if store.menuTargetPerspectiveID == perspective.id { + perspectiveMenu(row) + .padding(.bottom, 40) + .padding(.trailing, 16) + } + } + .zIndex(store.menuTargetPerspectiveID == perspective.id ? 1 : 0) + } + + /// 더보기 메뉴 — Android 순서(내 의견: 삭제·수정, 거절: 수정, 남 의견: 신고). + @ViewBuilder + func perspectiveMenu(_ row: ClassPerspectiveRow) -> some View { + let id = row.perspective.id + VStack(alignment: .trailing, spacing: 8) { + switch row.menuKind { + case .report: + perspectiveMenuButton("신고", systemImage: "bell") { send(.perspectiveReportTapped(id)) } + case .editOnly: + perspectiveMenuButton("수정", systemImage: "pencil") { send(.perspectiveEditTapped(id)) } + case .editAndDelete: + perspectiveMenuButton("삭제", systemImage: "trash") { send(.perspectiveDeleteTapped(id)) } + perspectiveMenuButton("수정", systemImage: "pencil") { send(.perspectiveEditTapped(id)) } + case .hidden: + EmptyView() + } + } + } + + func perspectiveMenuButton( + _ title: String, + systemImage: String, + action: @escaping () -> Void + ) -> some View { + Button(action: action) { + HStack(spacing: 4) { + Image(systemName: systemImage) + .resizable() + .scaledToFit() + .frame(width: 13, height: 13) + Text(title) + .pretendardFont(.medium13) + } + .foregroundStyle(.beige50) + .padding(.horizontal, 14) + .padding(.vertical, 7) + .background(.primary500, in: Capsule()) + } + .buttonStyle(.plain) } @ViewBuilder @@ -877,14 +935,25 @@ private extension ClassBattleFlowView { .foregroundStyle(.gray300) } Spacer() - Text(perspective.option.title) - .pretendardFont(.semiBold12) - .foregroundStyle(.badgeFilledText) - .lineLimit(1) - .frame(height: 16) - .padding(.horizontal, 4) - .padding(.vertical, 2) - .background(.badgeFilledBackground, in: RoundedRectangle(cornerRadius: 2)) + if let statusTitle = row.statusBadgeTitle { + Text(statusTitle) + .pretendardFont(.semiBold12) + .foregroundStyle(.white) + .lineLimit(1) + .frame(height: 16) + .padding(.horizontal, 4) + .padding(.vertical, 2) + .background(row.isRejected ? Color.primary400 : .secondary500, in: RoundedRectangle(cornerRadius: 2)) + } else { + Text(perspective.option.title) + .pretendardFont(.semiBold12) + .foregroundStyle(.badgeFilledText) + .lineLimit(1) + .frame(height: 16) + .padding(.horizontal, 4) + .padding(.vertical, 2) + .background(.badgeFilledBackground, in: RoundedRectangle(cornerRadius: 2)) + } } } @@ -916,6 +985,18 @@ private extension ClassBattleFlowView { .foregroundStyle(.gray300) } Spacer() + if row.menuKind.hasMenu { + Button { send(.perspectiveMoreTapped(perspective.id)) } label: { + Image(systemName: "ellipsis") + .resizable() + .scaledToFit() + .frame(width: 15, height: 3) + .frame(width: 24, height: 24) + .contentShape(Rectangle().inset(by: -10)) + } + .foregroundStyle(.gray300) + .accessibilityLabel("더보기") + } } .frame(height: 24) .buttonStyle(.plain) @@ -924,6 +1005,9 @@ private extension ClassBattleFlowView { @ViewBuilder func composerBar() -> some View { VStack(alignment: .leading, spacing: 0) { + if store.editingPerspectiveID != nil { + editingBanner() + } if let error = store.errorMessage { Text(error) .pretendardFont(.regular13) @@ -966,6 +1050,22 @@ private extension ClassBattleFlowView { } } + @ViewBuilder + func editingBanner() -> some View { + HStack { + Text("의견 수정 중") + .pretendardFont(.semiBold12) + .foregroundStyle(.primary500) + Spacer() + Button("취소") { send(.cancelEditTapped) } + .pretendardFont(.medium13) + .foregroundStyle(.gray400) + .buttonStyle(.plain) + } + .padding(.horizontal, 16) + .padding(.top, 12) + } + var plainNavigationBar: some View { PickeNavigationBar( onBack: { send(.backTapped) }, diff --git a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift index 9876e2c0..c89a9a5a 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift @@ -1254,6 +1254,205 @@ struct ClassBattleFlowTests { await store.finish() } + @Test + func 의견_더보기_메뉴는_Android와_같이_작성자와_상태로_정한다() { + #expect(ClassPerspectiveMenuKind(listItem: perspectiveItem(id: 1, isMine: false)) == .report) + #expect(ClassPerspectiveMenuKind(listItem: perspectiveItem(id: 1, isMine: true)) == .editAndDelete) + #expect(ClassPerspectiveMenuKind(listItem: perspectiveItem(id: 1, isMine: true, status: "REJECTED")) == .editOnly) + #expect(ClassPerspectiveMenuKind(listItem: perspectiveItem(id: 1, isMine: true, status: "pending")) == .hidden) + #expect(ClassPerspectiveMenuKind(listItem: perspectiveItem(id: 1, isMine: nil)) == .hidden) + #expect(!ClassPerspectiveMenuKind.hidden.hasMenu) + #expect(ClassPerspectiveMenuKind(myStatus: nil) == .editAndDelete) + } + + @Test + func 내_의견_수정은_입력창에_내용을_채우고_보내면_수정_후_다시_불러온다() async { + var state = completedState(perspectives: [perspectiveItem(id: 5, isMine: true)]) + state.hasPublishedPerspective = true + #expect(!state.shouldShowComposer) + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: ClassBattleFlowSpy()) + $0.perspectiveUseCase = MockPerspectiveRepository() + $0.classBattleUseCase = ClassBattleFlowUseCaseStub( + spy: ClassBattleFlowSpy(), + perspectivesPage: .init(items: [perspectiveItem(id: 5, isMine: true)], nextCursor: nil, hasNext: false) + ) + } + store.exhaustivity = .off + + await store.send(.view(.perspectiveMoreTapped(5))) + #expect(store.state.menuTargetPerspectiveID == 5) + await store.send(.view(.perspectiveEditTapped(5))) + #expect(store.state.menuTargetPerspectiveID == nil) + #expect(store.state.editingPerspectiveID == 5) + #expect(store.state.draft == "의견 5") + #expect(store.state.shouldShowComposer) + #expect(store.state.canSubmit) + + await store.send(.view(.draftChanged("고친 의견"))) + await store.send(.view(.submitTapped)) + #expect(store.state.mutationRequestID != nil) + #expect(store.state.submitRequestID == nil) + await store.receive(\.inner) // updatePerspectiveResponse + + #expect(store.state.editingPerspectiveID == nil) + #expect(store.state.draft.isEmpty) + #expect(store.state.mutationRequestID == nil) + #expect(store.state.perspectiveRequestID != nil || store.state.perspectives.map(\.id) == [5]) + } + + @Test + func 의견_수정을_취소하면_수정_상태와_입력이_비워진다() async { + var state = completedState(perspectives: [perspectiveItem(id: 5, isMine: true)]) + state.hasPublishedPerspective = true + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } + store.exhaustivity = .off + + await store.send(.view(.perspectiveEditTapped(5))) + await store.send(.view(.cancelEditTapped)) + + #expect(store.state.editingPerspectiveID == nil) + #expect(store.state.draft.isEmpty) + #expect(!store.state.shouldShowComposer) + } + + @Test + func 내_의견을_삭제하면_목록에서_빼고_다시_쓸_수_있다() async { + var state = completedState(perspectives: [ + perspectiveItem(id: 5, isMine: true), + perspectiveItem(id: 9, isMine: false), + ]) + state.hasPublishedPerspective = true + state.hasSubmittedInCurrentSession = true + state.myPerspective = perspectiveItem(id: 5, isMine: true) + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: ClassBattleFlowSpy()) + $0.perspectiveUseCase = MockPerspectiveRepository() + $0.classBattleUseCase = ClassBattleFlowUseCaseStub(spy: ClassBattleFlowSpy()) + } + store.exhaustivity = .off + + await store.send(.view(.perspectiveDeleteTapped(9))) + #expect(store.state.customAlert == nil) + await store.send(.view(.perspectiveDeleteTapped(5))) + #expect(store.state.customAlert == .deletePerspective()) + await store.send(.scope(.customAlert(.presented(.confirmTapped)))) + await store.receive(\.inner) // deletePerspectiveResponse + + #expect(store.state.perspectives.map(\.id) == [9]) + #expect(store.state.myPerspective == nil) + #expect(store.state.hasPublishedPerspective == false) + #expect(!store.state.hasSubmittedInCurrentSession) + #expect(store.state.shouldShowComposer) + } + + @Test + func 남의_의견_신고는_확인_후_결과를_토스트로_알린다() async { + let state = completedState(perspectives: [perspectiveItem(id: 9, isMine: false)]) + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing + $0.perspectiveUseCase = MockPerspectiveRepository() + } + store.exhaustivity = .off + + await store.send(.view(.perspectiveReportTapped(9))) + #expect(store.state.customAlert == .report()) + await store.send(.scope(.customAlert(.presented(.confirmTapped)))) + await store.receive(\.inner) // reportPerspectiveResponse + #expect(store.state.toastMessage == "신고가 정상 접수되었습니다.") + await store.send(.view(.toastShown)) + #expect(store.state.toastMessage == nil) + + await store.send(.view(.perspectiveReportTapped(9))) + await store.send(.scope(.customAlert(.presented(.cancelTapped)))) + #expect(store.state.reportTargetPerspectiveID == nil) + #expect(store.state.mutationRequestID == nil) + + var duplicate = state + duplicate.mutationRequestID = requestID + let duplicateStore = TestStore(initialState: duplicate) { ClassBattleFlowFeature() } + duplicateStore.exhaustivity = .off + await duplicateStore.send(.inner(.reportPerspectiveResponse(requestID, .failure(.alreadyReported)))) + #expect(duplicateStore.state.toastMessage == "이미 신고한 사용자입니다.") + + var failed = state + failed.mutationRequestID = requestID + let failedStore = TestStore(initialState: failed) { ClassBattleFlowFeature() } + failedStore.exhaustivity = .off + await failedStore.send(.inner(.reportPerspectiveResponse(requestID, .failure(.unknown("x"))))) + #expect(failedStore.state.toastMessage == "신고하지 못했어요") + } + + @Test + func 검수_중인_내_의견은_목록_위_상태_카드로_보이고_메뉴가_없다() async { + var state = completedState(perspectives: [perspectiveItem(id: 9, isMine: false)]) + state.myPerspective = perspectiveItem(id: 5, isMine: true, status: "PENDING") + #expect(state.hiddenMyPerspectiveRow?.perspective.id == 5) + #expect(state.hiddenMyPerspectiveRow?.statusBadgeTitle == "검수중") + #expect(state.hiddenMyPerspectiveRow?.menuKind == .hidden) + + state.myPerspective = perspectiveItem(id: 5, isMine: nil, status: "REJECTED") + #expect(state.hiddenMyPerspectiveRow?.statusBadgeTitle == "거절됨") + #expect(state.hiddenMyPerspectiveRow?.menuKind == .editOnly) + + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } + store.exhaustivity = .off + state.myPerspective = perspectiveItem(id: 5, isMine: true, status: "PENDING") + let pendingStore = TestStore(initialState: state) { ClassBattleFlowFeature() } + pendingStore.exhaustivity = .off + await pendingStore.send(.view(.perspectiveMoreTapped(5))) + #expect(pendingStore.state.menuTargetPerspectiveID == nil) + await store.send(.view(.perspectiveEditTapped(5))) + #expect(store.state.editingPerspectiveID == 5) + } + + @Test + func 상세_이동으로_화면이_가려져도_의견_변경_응답을_처리한다() async { + var state = completedState(perspectives: [perspectiveItem(id: 9, isMine: false)]) + state.mutationRequestID = requestID + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: ClassBattleFlowSpy()) + } + store.exhaustivity = .off + + await store.send(.view(.onDisappear)) + #expect(store.state.mutationRequestID == requestID) + await store.send(.inner(.reportPerspectiveResponse(requestID, .success(true)))) + #expect(store.state.mutationRequestID == nil) + #expect(store.state.toastMessage == "신고가 정상 접수되었습니다.") + } + + private func completedState(perspectives: [ClassPerspectiveItem]) -> ClassBattleFlowFeature.State { + var state = battleState(room: .mocks[0]) + state.session = session(step: .completed, commentRequired: false) + state.viewState = .loaded + state.voteStats = .init(options: [], totalCount: 0, updatedAt: .now) + state.hasPublishedPerspective = false + state.perspectives = perspectives + return state + } + + private func perspectiveItem( + id: Int, + isMine: Bool?, + status: String? = nil + ) -> ClassPerspectiveItem { + ClassPerspectiveItem( + id: id, + author: .init(userTag: "u\(id)", nickname: "작성자 \(id)", characterType: nil, characterImageURL: nil), + option: .init(id: 1, label: "A", title: "찬성", stance: "찬성"), + content: "의견 \(id)", + likeCount: 0, + commentCount: 0, + isLiked: false, + isMyPerspective: isMine, + status: status, + createdAt: .now + ) + } + private func session( step: ClassBattleStep, commentRequired: Bool, From 96de2262ec01ff21c69f1a06b529f2a731401adf Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 16:56:47 +0900 Subject: [PATCH 11/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=9D=98?= =?UTF-8?q?=EA=B2=AC=20=EB=A9=94=EB=89=B4=20=EC=A0=90=EA=B2=80=20=EC=A7=80?= =?UTF-8?q?=EC=A0=81=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 완료 단계 진입마다 내 의견을 다시 받아 제출 직후에도 검수중·거절됨 카드와 거절 의견 수정이 보이게 함 - 수정·삭제·신고 요청이 진행 중이면 다른 메뉴 동작을 막아 같은 취소 ID로 앞 요청이 취소되지 않게 함 - 다음 대댓글 페이지 실패 시 더 불러오기 중단(무한 로딩·반복 토스트 방지) - 상태 카드가 있으면 빈 상태 문구를 숨기고 검수 중·거절 의견은 상세로 이동하지 않음 - 쓰이지 않는 메뉴 닫기 액션 제거, 수정 중 같은 의견을 다시 눌러도 입력 유지 - 메뉴 버튼은 공용 PickeActionPill과 기존 신고 아이콘 재사용 - 화면 이탈 뒤 실제 신고 effect 응답·변경 중 차단·재조회·페이지 실패 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Reducer/ClassBattleFlowFeature.swift | 26 ++- .../View/ClassBattleFlowView.swift | 37 +--- .../Reducer/ClassMyOpinionsFeature.swift | 2 + .../Tests/Sources/ClassBattleFlowTests.swift | 188 +++++++++++++++++- .../Tests/Sources/ClassMyOpinionsTests.swift | 17 ++ 5 files changed, 227 insertions(+), 43 deletions(-) diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift index 6748587a..6f6ed446 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift @@ -358,7 +358,6 @@ public struct ClassBattleFlowFeature { case perspectiveTapped(Int) case perspectiveLikeTapped(Int) case perspectiveMoreTapped(Int) - case perspectiveMenuDismissed case perspectiveEditTapped(Int) case perspectiveDeleteTapped(Int) case perspectiveReportTapped(Int) @@ -683,30 +682,31 @@ extension ClassBattleFlowFeature { } .cancellable(id: CancelID.perspectiveLike(state.instanceID), cancelInFlight: true) case let .perspectiveMoreTapped(id): - guard state.perspectiveMenuKind(id: id).hasMenu else { return .none } + // 진행 중인 수정·삭제·신고가 같은 CancelID로 취소되지 않게 한 번에 하나만 허용한다. + guard state.mutationRequestID == nil, state.perspectiveMenuKind(id: id).hasMenu else { return .none } state.menuTargetPerspectiveID = state.menuTargetPerspectiveID == id ? nil : id return .none - case .perspectiveMenuDismissed: - state.menuTargetPerspectiveID = nil - return .none case let .perspectiveEditTapped(id): state.menuTargetPerspectiveID = nil - guard [.editOnly, .editAndDelete].contains(state.perspectiveMenuKind(id: id)), + guard state.mutationRequestID == nil, + [.editOnly, .editAndDelete].contains(state.perspectiveMenuKind(id: id)), let item = state.perspectiveItem(id: id) else { return .none } + if state.editingPerspectiveID != id { + state.draft = item.content + } state.editingPerspectiveID = id - state.draft = item.content state.errorMessage = nil return .none case let .perspectiveDeleteTapped(id): state.menuTargetPerspectiveID = nil - guard state.perspectiveMenuKind(id: id) == .editAndDelete else { return .none } + guard state.mutationRequestID == nil, state.perspectiveMenuKind(id: id) == .editAndDelete else { return .none } state.deleteTargetPerspectiveID = id state.reportTargetPerspectiveID = nil state.customAlert = .deletePerspective() return .none case let .perspectiveReportTapped(id): state.menuTargetPerspectiveID = nil - guard state.perspectiveMenuKind(id: id) == .report else { return .none } + guard state.mutationRequestID == nil, state.perspectiveMenuKind(id: id) == .report else { return .none } state.reportTargetPerspectiveID = id state.deleteTargetPerspectiveID = nil state.customAlert = .report() @@ -728,6 +728,11 @@ extension ClassBattleFlowFeature { switch action { case .customAlert(.presented(.confirmTapped)): state.customAlert = nil + guard state.mutationRequestID == nil else { + state.deleteTargetPerspectiveID = nil + state.reportTargetPerspectiveID = nil + return .none + } if let id = state.deleteTargetPerspectiveID { state.deleteTargetPerspectiveID = nil return deletePerspective(state: &state, id: id) @@ -863,7 +868,8 @@ extension ClassBattleFlowFeature { stopPlayback(instanceID: state.instanceID), loadPerspectives(state: &state, cursor: nil), loadVoteStats(state: &state), - state.hasPublishedPerspective == nil ? loadMyPerspectives(state: &state) : .none + // 공개되지 않은 내 의견 카드·거절 의견 수정을 위해 완료 단계마다 내 의견을 다시 받는다(Android 제출 후 fetchMyPerspective). + loadMyPerspectives(state: &state) ) } return stopPlayback(instanceID: state.instanceID) diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift index 6afe907a..90123f09 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/View/ClassBattleFlowView.swift @@ -642,7 +642,8 @@ private extension ClassBattleFlowView { } if store.isLoadingPerspectives { SkeletonView(.rect).frame(height: 90) - } else if store.perspectives.isEmpty && store.perspectiveErrorMessage == nil { + } else if store.perspectives.isEmpty && store.perspectiveErrorMessage == nil + && store.hiddenMyPerspectiveRow == nil { Text("아직 등록된 의견이 없어요.") .pretendardFont(.bodyMedium) .foregroundStyle(.gray500) @@ -851,11 +852,13 @@ private extension ClassBattleFlowView { .frame(maxWidth: .infinity, alignment: .leading) } .buttonStyle(.plain) + .disabled(row.statusBadgeTitle != nil) Button("자세히 보기") { send(.perspectiveTapped(perspective.id)) } .pretendardFont(.labelSmall, lineHeightMultiple: 1.4) .foregroundStyle(.gray200) .padding(.horizontal, 2) .buttonStyle(.plain) + .disabled(row.statusBadgeTitle != nil) } perspectiveActions(row) } @@ -883,40 +886,18 @@ private extension ClassBattleFlowView { VStack(alignment: .trailing, spacing: 8) { switch row.menuKind { case .report: - perspectiveMenuButton("신고", systemImage: "bell") { send(.perspectiveReportTapped(id)) } + PickeActionPill(title: "신고", systemImage: "light.beacon.max.fill") { send(.perspectiveReportTapped(id)) } case .editOnly: - perspectiveMenuButton("수정", systemImage: "pencil") { send(.perspectiveEditTapped(id)) } + PickeActionPill(title: "수정", systemImage: "pencil") { send(.perspectiveEditTapped(id)) } case .editAndDelete: - perspectiveMenuButton("삭제", systemImage: "trash") { send(.perspectiveDeleteTapped(id)) } - perspectiveMenuButton("수정", systemImage: "pencil") { send(.perspectiveEditTapped(id)) } + PickeActionPill(title: "삭제", systemImage: "trash") { send(.perspectiveDeleteTapped(id)) } + PickeActionPill(title: "수정", systemImage: "pencil") { send(.perspectiveEditTapped(id)) } case .hidden: EmptyView() } } } - func perspectiveMenuButton( - _ title: String, - systemImage: String, - action: @escaping () -> Void - ) -> some View { - Button(action: action) { - HStack(spacing: 4) { - Image(systemName: systemImage) - .resizable() - .scaledToFit() - .frame(width: 13, height: 13) - Text(title) - .pretendardFont(.medium13) - } - .foregroundStyle(.beige50) - .padding(.horizontal, 14) - .padding(.vertical, 7) - .background(.primary500, in: Capsule()) - } - .buttonStyle(.plain) - } - @ViewBuilder func perspectiveHeader(_ row: ClassPerspectiveRow) -> some View { let perspective = row.perspective @@ -984,6 +965,8 @@ private extension ClassBattleFlowView { } .foregroundStyle(.gray300) } + // Android처럼 검수 중·거절된 의견은 상세로 이동하지 않는다. + .disabled(row.statusBadgeTitle != nil) Spacer() if row.menuKind.hasMenu { Button { send(.perspectiveMoreTapped(perspective.id)) } label: { diff --git a/Projects/Feature/Class/Sources/ClassMyOpinions/Reducer/ClassMyOpinionsFeature.swift b/Projects/Feature/Class/Sources/ClassMyOpinions/Reducer/ClassMyOpinionsFeature.swift index 480d67e7..63e7f5ea 100644 --- a/Projects/Feature/Class/Sources/ClassMyOpinions/Reducer/ClassMyOpinionsFeature.swift +++ b/Projects/Feature/Class/Sources/ClassMyOpinions/Reducer/ClassMyOpinionsFeature.swift @@ -258,6 +258,8 @@ public struct ClassMyOpinionsFeature { state.hasNext = activity.hasNext case .failure: state.toastMessage = Self.loadFailedMessage + // Android `RequestFailed`처럼 다음 페이지 실패 시 더 불러오기를 멈춰 무한 로딩·반복 토스트를 막는다. + if cursor != nil { state.hasNext = false } } return .none } diff --git a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift index c89a9a5a..b94b22b8 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift @@ -1409,21 +1409,91 @@ struct ClassBattleFlowTests { } @Test - func 상세_이동으로_화면이_가려져도_의견_변경_응답을_처리한다() async { - var state = completedState(perspectives: [perspectiveItem(id: 9, isMine: false)]) - state.mutationRequestID = requestID + func 상세_이동으로_화면이_가려져도_진행_중인_신고는_취소되지_않는다() async { + let gate = ReportGate() + let state = completedState(perspectives: [perspectiveItem(id: 9, isMine: false)]) let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: ClassBattleFlowSpy()) + $0.perspectiveUseCase = SuspendedReportPerspective(gate: gate) } store.exhaustivity = .off + await store.send(.view(.perspectiveReportTapped(9))) + await store.send(.scope(.customAlert(.presented(.confirmTapped)))) + await gate.waitForStart() await store.send(.view(.onDisappear)) - #expect(store.state.mutationRequestID == requestID) - await store.send(.inner(.reportPerspectiveResponse(requestID, .success(true)))) + #expect(store.state.mutationRequestID != nil) + await gate.release() + await store.receive(\.inner) // reportPerspectiveResponse #expect(store.state.mutationRequestID == nil) #expect(store.state.toastMessage == "신고가 정상 접수되었습니다.") } + @Test + func 의견_변경_요청이_진행_중이면_다른_메뉴_동작을_막는다() async { + var state = completedState(perspectives: [ + perspectiveItem(id: 5, isMine: true), + perspectiveItem(id: 9, isMine: false), + ]) + state.mutationRequestID = requestID + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } + store.exhaustivity = .off + + await store.send(.view(.perspectiveMoreTapped(9))) + #expect(store.state.menuTargetPerspectiveID == nil) + await store.send(.view(.perspectiveReportTapped(9))) + await store.send(.view(.perspectiveDeleteTapped(5))) + #expect(store.state.customAlert == nil) + await store.send(.view(.perspectiveEditTapped(5))) + #expect(store.state.editingPerspectiveID == nil) + #expect(store.state.mutationRequestID == requestID) + } + + @Test + func 수정_중_같은_의견의_수정을_다시_눌러도_고치던_내용을_유지한다() async { + var state = completedState(perspectives: [perspectiveItem(id: 5, isMine: true)]) + state.hasPublishedPerspective = true + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } + store.exhaustivity = .off + + await store.send(.view(.perspectiveEditTapped(5))) + await store.send(.view(.draftChanged("고치는 중"))) + await store.send(.view(.perspectiveEditTapped(5))) + #expect(store.state.draft == "고치는 중") + } + + @Test + func 의견을_제출한_뒤_완료_단계로_오면_내_의견을_다시_받아_검수_중_카드를_보여_준다() async { + var state = battleState(room: .mocks[0]) + state.loadRequestID = requestID + state.hasSubmittedInCurrentSession = true + let pending = perspectiveItem(id: 5, isMine: true, status: "PENDING") + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing + $0.audioPlayer = ClassBattleFlowAudioPlayer(spy: ClassBattleFlowSpy()) + $0.classBattleUseCase = ClassBattleFlowUseCaseStub( + spy: ClassBattleFlowSpy(), + perspectivesPage: .init(items: [], nextCursor: nil, hasNext: false), + myPerspectives: [pending] + ) + } + store.exhaustivity = .off + + await store.send(.inner(.progressResponse( + requestID, + .success(.init(session: session(step: .completed, commentRequired: false), voteHistory: nil)) + ))) + #expect(store.state.hasPublishedPerspective == true) + // uuid는 의견 목록(0)·투표 통계(1)·내 의견(2) 순서로 발급된다. + let myPerspectiveRequestID = UUID(uuidString: "00000000-0000-0000-0000-000000000002")! + await store.receive(\.inner, .myPerspectiveResponse(myPerspectiveRequestID, .success([pending]))) + + #expect(store.state.myPerspective?.id == 5) + #expect(store.state.hiddenMyPerspectiveRow?.statusBadgeTitle == "검수중") + #expect(store.state.hasPublishedPerspective == true) + } + private func completedState(perspectives: [ClassPerspectiveItem]) -> ClassBattleFlowFeature.State { var state = battleState(room: .mocks[0]) state.session = session(step: .completed, commentRequired: false) @@ -1517,6 +1587,7 @@ private struct ClassBattleFlowUseCaseStub: ClassBattleInterface { var history: ClassVoteHistory? var perspectivesPage: ClassPerspectivePage? var failsLoad = false + var myPerspectives: [ClassPerspectiveItem]? func fetchBattle(classID: Int) async throws -> ClassBattleSession { await spy.recordClass(classID) @@ -1557,7 +1628,112 @@ private struct ClassBattleFlowUseCaseStub: ClassBattleInterface { guard let perspectivesPage else { throw ClassBattleError.invalidResponse } return perspectivesPage } - func fetchMyPerspectives(classID _: Int) async throws -> [ClassPerspectiveItem] { throw ClassBattleError.invalidResponse } + func fetchMyPerspectives(classID _: Int) async throws -> [ClassPerspectiveItem] { + guard let myPerspectives else { throw ClassBattleError.invalidResponse } + return myPerspectives + } +} + +/// 신고 요청을 멈춰 두었다가 풀어 주는 관문. +private actor ReportGate { + private var started = false + private var startWaiters: [CheckedContinuation] = [] + private var releaseContinuation: CheckedContinuation? + + func wait() async { + started = true + startWaiters.forEach { $0.resume() } + startWaiters = [] + await withCheckedContinuation { releaseContinuation = $0 } + } + + func waitForStart() async { + if started { return } + await withCheckedContinuation { startWaiters.append($0) } + } + + func release() { + releaseContinuation?.resume() + releaseContinuation = nil + } +} + +/// 신고만 관문에서 멈추고 나머지는 Mock에 맡기는 의견 유스케이스. +private struct SuspendedReportPerspective: PerspectiveInterface { + let gate: ReportGate + private let base = MockPerspectiveRepository() + + init(gate: ReportGate) { + self.gate = gate + } + + func fetchPerspective(perspectiveId: Int) async throws -> BattlePerspective { + try await base.fetchPerspective(perspectiveId: perspectiveId) + } + + func fetchLabeledComments( + perspectiveId: Int, + cursor: String?, + size: Int? + ) async throws -> PerspectiveCommentPage { + try await base.fetchLabeledComments(perspectiveId: perspectiveId, cursor: cursor, size: size) + } + + func createComment( + perspectiveId: Int, + content: String + ) async throws -> PerspectiveCommentMutationResult { + try await base.createComment(perspectiveId: perspectiveId, content: content) + } + + func updateComment( + perspectiveId: Int, + commentId: Int, + content: String + ) async throws -> PerspectiveCommentMutationResult { + try await base.updateComment(perspectiveId: perspectiveId, commentId: commentId, content: content) + } + + func deleteComment( + perspectiveId: Int, + commentId: Int + ) async throws { + try await base.deleteComment(perspectiveId: perspectiveId, commentId: commentId) + } + + func updatePerspective( + perspectiveId: Int, + content: String + ) async throws { + try await base.updatePerspective(perspectiveId: perspectiveId, content: content) + } + + func deletePerspective(perspectiveId: Int) async throws { + try await base.deletePerspective(perspectiveId: perspectiveId) + } + + func likePerspective(perspectiveId: Int) async throws -> CommentLikeResult { + try await base.likePerspective(perspectiveId: perspectiveId) + } + + func unlikePerspective(perspectiveId: Int) async throws -> CommentLikeResult { + try await base.unlikePerspective(perspectiveId: perspectiveId) + } + + func fetchPerspectiveLikes(perspectiveId: Int) async throws -> CommentLikeResult { + try await base.fetchPerspectiveLikes(perspectiveId: perspectiveId) + } + + func reportPerspective(perspectiveId _: Int) async throws { + await gate.wait() + } + + func reportComment( + perspectiveId: Int, + commentId: Int + ) async throws { + try await base.reportComment(perspectiveId: perspectiveId, commentId: commentId) + } } private struct ClassBattleFlowScenarioStub: BattleInterface { diff --git a/Projects/Feature/Class/Tests/Sources/ClassMyOpinionsTests.swift b/Projects/Feature/Class/Tests/Sources/ClassMyOpinionsTests.swift index eb37279a..48066429 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassMyOpinionsTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassMyOpinionsTests.swift @@ -215,6 +215,23 @@ struct ClassMyOpinionsTests { #expect(store.state.toastMessage == "정보를 불러오지 못했어요") } + @Test + func 다음_대댓글_페이지_조회에_실패하면_더_불러오기를_멈춘다() async { + var state = ClassMyOpinionsFeature.State() + state.classID = 31 + state.nextCursor = "c-2" + state.hasNext = true + state.isLoadingMore = true + let store = TestStore(initialState: state) { ClassMyOpinionsFeature() } + store.exhaustivity = .off + + await store.send(.inner(.replyPageResponse(cursor: "c-2", .failure(.network("실패"))))) + + #expect(!store.state.hasNext) + #expect(!store.state.canLoadMoreReplies) + #expect(store.state.toastMessage == "정보를 불러오지 못했어요") + } + private func perspective( id: Int, status: String? From e2a1880cd44618b2fbfbdf70c01ac55fb055f400 Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 22:27:39 +0900 Subject: [PATCH 12/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=9D=98?= =?UTF-8?q?=EA=B2=AC=20=EC=B0=B8=EC=97=AC=20=ED=8C=90=EC=A0=95=EC=9D=84=20?= =?UTF-8?q?=EB=82=B4=20=EC=9D=98=EA=B2=AC=20=EB=B9=88=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=EC=97=90=EB=8F=84=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 서버가 참여 완료로 판정한 뒤에는 /perspectives/me가 비어도 입력창을 다시 열지 않음(중복 의견 방지) - 내 의견 조회 실패는 판정 전에만 재시도 문구를 띄우고, 판정 뒤에는 목록 오류 문구를 덮지 않음 - 내 의견 상세에서 돌아오면 판정을 비우고 /me 결과로 다시 판정(상세에서 삭제 후 재작성 가능), 플래그는 의견을 누를 때마다 대입 - 의견 필수·참여 완료, 판정 전후 조회 실패, 내 의견·남의 의견 상세 복귀, 변경 중 알림 확인 테스트 추가 - 내 의견 응답 테스트의 고정 UUID를 상태의 요청 ID로 대체 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Reducer/ClassBattleFlowFeature.swift | 17 ++- .../Tests/Sources/ClassBattleFlowTests.swift | 138 +++++++++++++++++- 2 files changed, 148 insertions(+), 7 deletions(-) diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift index 6f6ed446..ca97be4a 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift @@ -44,6 +44,7 @@ public struct ClassBattleFlowFeature { public var hasMorePerspectives = false public var isLoadingPerspectives = false public var refreshAfterPerspectiveDetail = false + public var refreshAfterOwnPerspectiveDetail = false public var perspectiveErrorMessage: String? public var errorMessage: String? public var loadRequestID: UUID? @@ -446,8 +447,12 @@ extension ClassBattleFlowFeature { if state.step == .some(.completed), state.perspectiveRequestID == nil { let shouldRefresh = state.refreshAfterPerspectiveDetail + let returnedFromOwnPerspective = shouldRefresh && state.refreshAfterOwnPerspectiveDetail state.refreshAfterPerspectiveDetail = false + state.refreshAfterOwnPerspectiveDetail = false if shouldRefresh { state.hasSubmittedInCurrentSession = false } + // 내 의견 상세에서 삭제 후 돌아왔을 수 있으므로 /me 결과로 다시 판정한다. + if returnedFromOwnPerspective { state.hasPublishedPerspective = nil } return .merge( state.perspectives.isEmpty || shouldRefresh ? loadPerspectives(state: &state, cursor: nil, preserveExisting: shouldRefresh) : .none, @@ -663,6 +668,8 @@ extension ClassBattleFlowFeature { guard state.step == .some(.completed), state.submitRequestID == nil, state.perspectives.contains(where: { $0.id == id }) else { return .none } state.refreshAfterPerspectiveDetail = true + state.refreshAfterOwnPerspectiveDetail = + state.perspectives.first(where: { $0.id == id })?.isMyPerspective == true return .send(.delegate(.openPerspective(id))) case let .perspectiveLikeTapped(id): guard state.step == .some(.completed), state.canParticipate, @@ -1069,9 +1076,15 @@ extension ClassBattleFlowFeature { switch result { case let .success(items): state.myPerspective = items.first - state.hasPublishedPerspective = !items.isEmpty || state.hasSubmittedInCurrentSession + // 서버가 참여 완료로 판정한 뒤에는 /me가 비어도 입력창을 다시 열지 않는다(중복 의견 방지). + state.hasPublishedPerspective = state.hasPublishedPerspective == true + || !items.isEmpty + || state.hasSubmittedInCurrentSession case let .failure(error): - state.perspectiveErrorMessage = error.message + // 판정 전 실패만 재시도 문구를 띄운다. 이미 판정됐으면 목록 오류 문구를 덮지 않는다. + if state.hasPublishedPerspective == nil { + state.perspectiveErrorMessage = error.message + } } return .none case let .updatePerspectiveResponse(requestID, result): diff --git a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift index b94b22b8..3f1c418c 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift @@ -1464,7 +1464,7 @@ struct ClassBattleFlowTests { } @Test - func 의견을_제출한_뒤_완료_단계로_오면_내_의견을_다시_받아_검수_중_카드를_보여_준다() async { + func 의견을_제출한_뒤_완료_단계로_오면_내_의견을_다시_받아_검수_중_카드를_보여_준다() async throws { var state = battleState(room: .mocks[0]) state.loadRequestID = requestID state.hasSubmittedInCurrentSession = true @@ -1485,8 +1485,7 @@ struct ClassBattleFlowTests { .success(.init(session: session(step: .completed, commentRequired: false), voteHistory: nil)) ))) #expect(store.state.hasPublishedPerspective == true) - // uuid는 의견 목록(0)·투표 통계(1)·내 의견(2) 순서로 발급된다. - let myPerspectiveRequestID = UUID(uuidString: "00000000-0000-0000-0000-000000000002")! + let myPerspectiveRequestID = try #require(store.state.myPerspectiveRequestID) await store.receive(\.inner, .myPerspectiveResponse(myPerspectiveRequestID, .success([pending]))) #expect(store.state.myPerspective?.id == 5) @@ -1494,6 +1493,134 @@ struct ClassBattleFlowTests { #expect(store.state.hasPublishedPerspective == true) } + @Test + func 의견_필수_참여_완료면_내_의견이_비어도_입력창을_다시_열지_않는다() async throws { + var state = battleState(room: .mocks[0]) + state.loadRequestID = requestID + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing + $0.classBattleUseCase = ClassBattleFlowUseCaseStub( + spy: ClassBattleFlowSpy(), + perspectivesPage: .init(items: [], nextCursor: nil, hasNext: false), + myPerspectives: [] + ) + } + store.exhaustivity = .off + + await store.send(.inner(.progressResponse( + requestID, + .success(.init( + session: session(step: .completed, commentRequired: true, participationCompleted: true), + voteHistory: nil + )) + ))) + let myPerspectiveRequestID = try #require(store.state.myPerspectiveRequestID) + await store.receive(\.inner, .myPerspectiveResponse(myPerspectiveRequestID, .success([]))) + + #expect(store.state.hasPublishedPerspective == true) + #expect(!store.state.shouldShowComposer) + } + + @Test + func 판정이_끝난_뒤_내_의견_조회가_실패하면_목록_오류_문구를_덮지_않는다() async { + var state = completedState(perspectives: []) + state.hasPublishedPerspective = true + state.perspectiveErrorMessage = "목록 오류" + state.myPerspectiveRequestID = requestID + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } + store.exhaustivity = .off + + await store.send(.inner(.myPerspectiveResponse(requestID, .failure(.invalidResponse)))) + + #expect(store.state.perspectiveErrorMessage == "목록 오류") + #expect(store.state.hasPublishedPerspective == true) + } + + @Test + func 판정_전에_내_의견_조회가_실패하면_재시도_문구를_보여_준다() async { + var state = completedState(perspectives: []) + state.hasPublishedPerspective = nil + state.myPerspectiveRequestID = requestID + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } + store.exhaustivity = .off + + await store.send(.inner(.myPerspectiveResponse(requestID, .failure(.invalidResponse)))) + + #expect(store.state.perspectiveErrorMessage == ClassBattleError.invalidResponse.message) + #expect(store.state.hasPublishedPerspective == nil) + } + + @Test + func 내_의견_상세에서_돌아와_내_의견이_비면_입력창을_다시_연다() async throws { + var state = completedState(perspectives: [perspectiveItem(id: 5, isMine: true)]) + state.hasPublishedPerspective = true + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing + $0.classBattleUseCase = ClassBattleFlowUseCaseStub( + spy: ClassBattleFlowSpy(), + perspectivesPage: .init(items: [], nextCursor: nil, hasNext: false), + myPerspectives: [] + ) + } + store.exhaustivity = .off + + await store.send(.view(.perspectiveTapped(5))) + await store.send(.view(.onAppear)) + #expect(store.state.hasPublishedPerspective == nil) + let myPerspectiveRequestID = try #require(store.state.myPerspectiveRequestID) + await store.receive(\.inner, .myPerspectiveResponse(myPerspectiveRequestID, .success([]))) + + #expect(store.state.hasPublishedPerspective == false) + #expect(store.state.shouldShowComposer) + } + + @Test + func 남의_의견_상세에서_돌아오면_내_의견이_비어도_참여_완료를_유지한다() async throws { + var state = completedState(perspectives: [ + perspectiveItem(id: 5, isMine: true), + perspectiveItem(id: 9, isMine: false), + ]) + state.hasPublishedPerspective = true + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing + $0.classBattleUseCase = ClassBattleFlowUseCaseStub( + spy: ClassBattleFlowSpy(), + perspectivesPage: .init(items: [], nextCursor: nil, hasNext: false), + myPerspectives: [] + ) + } + store.exhaustivity = .off + + // 내 의견을 누른 뒤 복귀 처리 없이 남의 의견을 누르면 앞의 표시가 남지 않아야 한다. + await store.send(.view(.perspectiveTapped(5))) + await store.send(.view(.perspectiveTapped(9))) + await store.send(.view(.onAppear)) + #expect(store.state.hasPublishedPerspective == true) + let myPerspectiveRequestID = try #require(store.state.myPerspectiveRequestID) + await store.receive(\.inner, .myPerspectiveResponse(myPerspectiveRequestID, .success([]))) + + #expect(store.state.hasPublishedPerspective == true) + #expect(!store.state.shouldShowComposer) + } + + @Test + func 변경_요청_중에_알림을_확인하면_요청을_보내지_않고_대상을_비운다() async { + var state = completedState(perspectives: [perspectiveItem(id: 5, isMine: true)]) + state.hasPublishedPerspective = true + state.mutationRequestID = requestID + state.deleteTargetPerspectiveID = 5 + state.customAlert = .deletePerspective() + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } + store.exhaustivity = .off + + await store.send(.scope(.customAlert(.presented(.confirmTapped)))) + + #expect(store.state.customAlert == nil) + #expect(store.state.deleteTargetPerspectiveID == nil) + #expect(store.state.mutationRequestID == requestID) + #expect(store.state.perspectives.map(\.id) == [5]) + } + private func completedState(perspectives: [ClassPerspectiveItem]) -> ClassBattleFlowFeature.State { var state = battleState(room: .mocks[0]) state.session = session(step: .completed, commentRequired: false) @@ -1526,7 +1653,8 @@ struct ClassBattleFlowTests { private func session( step: ClassBattleStep, commentRequired: Bool, - midVoteEnabled: Bool = false + midVoteEnabled: Bool = false, + participationCompleted: Bool = false ) -> ClassBattleSession { ClassBattleSession( classID: 1, @@ -1535,7 +1663,7 @@ struct ClassBattleFlowTests { closed: false, commentRequired: commentRequired, canParticipate: true, - participationCompleted: false, + participationCompleted: participationCompleted, battle: ClassBattleDetail( id: 10, title: "토론", From fe837df1ccea065d8e716ddb7138d3286e8c9067 Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 22:28:42 +0900 Subject: [PATCH 13/21] =?UTF-8?q?=EC=9A=B4=EC=98=81=EC=9E=90=20=EB=8C=80?= =?UTF-8?q?=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EB=82=B4=20=ED=88=AC=ED=91=9C=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=ED=99=9C=EB=8F=99=20=ED=96=89=EC=9D=84=20?= =?UTF-8?q?3=EC=97=B4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 참여한 운영자의 개인 결과가 있으면 내 활동 API로 받은 추천 수를 함께 조회 - 활동 행을 Android 결과 화면과 같은 댓글·대댓글·받은 추천으로 표시(모르면 —) - 다시 불러오기·뒤로 가기·라우트 제거 시 내 활동 요청을 취소하고 값 초기화 - 받은 추천 성공·실패 표시 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Navigation/Class/ClassCoordinator.swift | 1 + .../Reducer/ClassOwnerDashboardFeature.swift | 45 +++++++++- .../View/ClassOwnerDashboardView.swift | 6 +- .../Sources/ClassOwnerDashboardTests.swift | 83 ++++++++++++++++++- 4 files changed, 130 insertions(+), 5 deletions(-) diff --git a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift index da17fad9..792475a2 100644 --- a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift +++ b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift @@ -510,6 +510,7 @@ private extension ClassCoordinator { .cancel(id: ClassOwnerDashboardFeature.CancelID.load(id)), .cancel(id: ClassOwnerDashboardFeature.CancelID.generate(id)), .cancel(id: ClassOwnerDashboardFeature.CancelID.generatePersonal(id)), + .cancel(id: ClassOwnerDashboardFeature.CancelID.myActivity(id)), ] } ) diff --git a/Projects/Feature/Class/Sources/ClassOwnerDashboard/Reducer/ClassOwnerDashboardFeature.swift b/Projects/Feature/Class/Sources/ClassOwnerDashboard/Reducer/ClassOwnerDashboardFeature.swift index 107eb195..546eca44 100644 --- a/Projects/Feature/Class/Sources/ClassOwnerDashboard/Reducer/ClassOwnerDashboardFeature.swift +++ b/Projects/Feature/Class/Sources/ClassOwnerDashboard/Reducer/ClassOwnerDashboardFeature.swift @@ -59,6 +59,8 @@ public struct ClassOwnerDashboardFeature { public var feedbackError: ClassAIFeedbackError? public var insightRequestID: UUID? public var feedbackRequestID: UUID? + public var receivedLikeCount: Int? + public var myActivityRequestID: UUID? public var isGeneratingInsight = false public var isGeneratingFeedback = false public var insightUnknownRetryCount = 0 @@ -183,6 +185,10 @@ public struct ClassOwnerDashboardFeature { return .unavailable } + public var personalReceivedLikeCountText: String { + receivedLikeCount.map(String.init) ?? "—" + } + public var myOpinionCount: Int { if classResult != nil { return generatedFeedback?.perspectiveCount ?? 0 } return myOpinions.count @@ -337,6 +343,7 @@ public struct ClassOwnerDashboardFeature { case fetchDashboard case generateInsight case generatePersonal + case fetchMyActivity } @CasePathable @@ -357,12 +364,14 @@ public struct ClassOwnerDashboardFeature { Result, wasUnknown: Bool ) + case myActivityResponse(instanceID: UUID, requestID: UUID, roomID: Int, Result) } public enum CancelID: Hashable, Sendable { case load(UUID) case generate(UUID) case generatePersonal(UUID) + case myActivity(UUID) } @CasePathable @@ -423,12 +432,14 @@ extension ClassOwnerDashboardFeature { state.requestID = nil state.insightRequestID = nil state.feedbackRequestID = nil + state.myActivityRequestID = nil state.isGeneratingInsight = false state.isGeneratingFeedback = false return .merge( .cancel(id: CancelID.load(state.instanceID)), .cancel(id: CancelID.generate(state.instanceID)), .cancel(id: CancelID.generatePersonal(state.instanceID)), + .cancel(id: CancelID.myActivity(state.instanceID)), .send(.delegate(.dismiss)) ) case .managementTapped: @@ -491,6 +502,8 @@ extension ClassOwnerDashboardFeature { state.isGeneratingFeedback = false state.insightUnknownRetryCount = 0 state.feedbackUnknownRetryCount = 0 + state.receivedLikeCount = nil + state.myActivityRequestID = nil let requestID = uuid() state.requestID = requestID state.sourceViewState = .loading @@ -514,8 +527,25 @@ extension ClassOwnerDashboardFeature { return .concatenate( .cancel(id: CancelID.generate(state.instanceID)), .cancel(id: CancelID.generatePersonal(state.instanceID)), + .cancel(id: CancelID.myActivity(state.instanceID)), fetch ) + case .fetchMyActivity: + let requestID = uuid() + state.myActivityRequestID = requestID + return .run { [classUseCase, instanceID = state.instanceID, roomID = state.room.id] send in + let result = await Result { + try await classUseCase.fetchMyActivity(roomID: roomID, cursor: nil) + } + .mapError(ClassError.from) + await send(.inner(.myActivityResponse( + instanceID: instanceID, + requestID: requestID, + roomID: roomID, + result + ))) + } + .cancellable(id: CancelID.myActivity(state.instanceID), cancelInFlight: true) case .generateInsight: let retryServerGeneration = state.insightError == .serverRetryAvailable let requestID = uuid() @@ -594,12 +624,16 @@ extension ClassOwnerDashboardFeature { case (true, true): return .merge( .send(.async(.generateInsight)), - .send(.async(.generatePersonal)) + .send(.async(.generatePersonal)), + .send(.async(.fetchMyActivity)) ) case (true, false): return .send(.async(.generateInsight)) case (false, true): - return .send(.async(.generatePersonal)) + return .merge( + .send(.async(.generatePersonal)), + .send(.async(.fetchMyActivity)) + ) case (false, false): return .none } @@ -609,6 +643,13 @@ extension ClassOwnerDashboardFeature { state.sourceViewState = .failed } return .none + case let .myActivityResponse(instanceID, requestID, roomID, result): + guard state.instanceID == instanceID, + state.myActivityRequestID == requestID, + state.room.id == roomID else { return .none } + state.myActivityRequestID = nil + state.receivedLikeCount = try? result.get().receivedLikeCount + return .none case let .dashboardResponse(instanceID, requestID, roomID, result): guard state.instanceID == instanceID, state.requestID == requestID, diff --git a/Projects/Feature/Class/Sources/ClassOwnerDashboard/View/ClassOwnerDashboardView.swift b/Projects/Feature/Class/Sources/ClassOwnerDashboard/View/ClassOwnerDashboardView.swift index 7abd2546..9b0830fc 100644 --- a/Projects/Feature/Class/Sources/ClassOwnerDashboard/View/ClassOwnerDashboardView.swift +++ b/Projects/Feature/Class/Sources/ClassOwnerDashboard/View/ClassOwnerDashboardView.swift @@ -553,9 +553,11 @@ private extension ClassOwnerDashboardView { reportChoice("사후 선택", value: feedback.postOption?.title ?? "선택 없음") } HStack(spacing: 0) { - reportActivity("\(feedback.perspectiveCount)", label: "의견") + reportActivity("\(feedback.perspectiveCount)", label: "댓글") Rectangle().fill(.beige600).frame(width: 1, height: 36) - reportActivity("\(feedback.commentCount)", label: "댓글") + reportActivity("\(feedback.commentCount)", label: "대댓글") + Rectangle().fill(.beige600).frame(width: 1, height: 36) + reportActivity(store.personalReceivedLikeCountText, label: "받은 추천") } } .padding(16) diff --git a/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift b/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift index c866a13d..12a3ee45 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift @@ -258,6 +258,7 @@ struct ClassOwnerDashboardTests { let result: Result var feedback: ClassAIPersonalFeedback? = nil var insight: ClassAIInsight? = nil + var myActivity: ClassMyActivity? = nil func fetchResult(roomID _: Int) async throws -> ClassResult { try result.get() @@ -281,7 +282,8 @@ struct ClassOwnerDashboardTests { roomID _: Int, cursor _: String? ) async throws -> ClassMyActivity { - throw ClassAIFeedbackError.temporarilyUnavailable + guard let myActivity else { throw ClassAIFeedbackError.temporarilyUnavailable } + return myActivity } } @@ -383,6 +385,85 @@ struct ClassOwnerDashboardTests { await store.receive(\.delegate.openMyOpinions) } + @Test + func 참여_운영자의_받은_추천은_내_활동_응답으로_표시한다() async { + let room = ClassRoom.mocks[0] + let requestID = UUID() + var state = ClassOwnerDashboardFeature.State(room: room, instanceID: UUID()) + state.requestID = requestID + let result = ClassResult( + classID: room.id, + closed: true, + closeAt: room.deadline, + stats: serverResult(room).stats, + myReportState: "SUCCEEDED", + insightState: nil + ) + let activity = ClassMyActivity( + classID: room.id, + receivedLikeCount: 12, + commentCount: 3, + comments: [], + nextCursor: nil, + hasNext: false + ) + let store = TestStore(initialState: state) { ClassOwnerDashboardFeature() } withDependencies: { + $0.uuid = .incrementing + $0.classResultUseCase = ResultStub( + result: .success(result), + myActivity: activity + ) + } + store.exhaustivity = .off + #expect(store.state.personalReceivedLikeCountText == "—") + + await store.send(.inner(.resultResponse( + instanceID: state.instanceID, + requestID: requestID, + roomID: room.id, + .success(result) + ))) + await store.receive(\.inner.myActivityResponse) + + #expect(store.state.receivedLikeCount == 12) + #expect(store.state.personalReceivedLikeCountText == "12") + #expect(store.state.myActivityRequestID == nil) + } + + @Test + func 내_활동_조회가_실패하면_받은_추천을_대시로_표시한다() async { + let room = ClassRoom.mocks[0] + let requestID = UUID() + var state = ClassOwnerDashboardFeature.State(room: room, instanceID: UUID()) + state.requestID = requestID + let result = ClassResult( + classID: room.id, + closed: true, + closeAt: room.deadline, + stats: serverResult(room).stats, + myReportState: "SUCCEEDED", + insightState: nil + ) + let store = TestStore(initialState: state) { ClassOwnerDashboardFeature() } withDependencies: { + $0.uuid = .incrementing + $0.classResultUseCase = ResultStub(result: .success(result)) + } + store.exhaustivity = .off + #expect(store.state.personalReceivedLikeCountText == "—") + + await store.send(.inner(.resultResponse( + instanceID: state.instanceID, + requestID: requestID, + roomID: room.id, + .success(result) + ))) + await store.receive(\.inner.myActivityResponse) + + #expect(store.state.receivedLikeCount == nil) + #expect(store.state.personalReceivedLikeCountText == "—") + #expect(store.state.myActivityRequestID == nil) + } + @Test func 서버_현황_표시값은_State에서_계산한다() { let room = ClassRoom.mocks[0] From 8d23d20ca519eff31bf03d82f17fef482b7d57c2 Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 22:29:16 +0900 Subject: [PATCH 14/21] =?UTF-8?q?=EB=82=B4=EA=B0=80=20=EB=82=A8=EA=B8=B4?= =?UTF-8?q?=20=EC=9D=98=EA=B2=AC=20=ED=99=94=EB=A9=B4=EC=9D=84=20=EB=8B=AB?= =?UTF-8?q?=EC=9C=BC=EB=A9=B4=20=EC=A7=84=ED=96=89=20=EC=A4=91=EC=9D=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=B7=A8=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 라우트에 내가 남긴 의견 화면이 없으면 코디네이터가 조회·대댓글·다음 페이지 요청을 취소(스와이프 닫기 포함) - 화면은 항상 최상단 하나뿐이라 상태 플래그 없이 라우트 존재 여부로 판단 - 테스트 대역의 내 활동 조회를 멈춰 둘 수 있게 바꾸고 스와이프 제거 시 취소 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Navigation/Class/ClassCoordinator.swift | 17 +++++++- .../Tests/Sources/ClassCoordinatorTests.swift | 41 ++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift index 792475a2..aeefa673 100644 --- a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift +++ b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift @@ -190,7 +190,8 @@ public struct ClassCoordinator { effect, cancelRemovedReportLoads(state: &state), cancelRemovedChatObservers(state: &state), - cancelRemovedClassEffects(state: &state) + cancelRemovedClassEffects(state: &state), + cancelRemovedMyOpinionsLoads(state: state) ) } } @@ -482,6 +483,20 @@ private extension ClassCoordinator { return .merge(removedIDs.map { .cancel(id: ChatRoomFeature.CancelID.audioObserver($0)) }) } + /// 내 의견 화면은 항상 최상단 하나뿐이고 CancelID가 전역이므로, 라우트에 없으면 남은 조회를 모두 취소한다. + func cancelRemovedMyOpinionsLoads(state: State) -> Effect { + let hasMyOpinions = state.routes.contains { route in + guard case .myOpinions = route.screen else { return false } + return true + } + guard !hasMyOpinions else { return .none } + return .merge( + .cancel(id: ClassMyOpinionsFeature.CancelID.fetch), + .cancel(id: ClassMyOpinionsFeature.CancelID.replies), + .cancel(id: ClassMyOpinionsFeature.CancelID.moreReplies) + ) + } + func cancelRemovedReportLoads(state: inout State) -> Effect { let reportIDs = Set(state.routes.compactMap { route -> UUID? in guard case let .report(report) = route.screen else { return nil } diff --git a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift index 16cac6f1..c9f7e97c 100644 --- a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift +++ b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift @@ -1386,6 +1386,30 @@ struct ClassCoordinatorTests { #expect(store.state.routes.count == 2) } + @Test + func 스와이프로_내가_남긴_의견_경로를_제거하면_진행중인_조회를_취소한다() async { + let room = ClassRoom.mockClosedSociety + let useCase = SuspendedClassUseCase() + var opinions = ClassMyOpinionsFeature.State() + opinions.classID = room.id + var state = ClassCoordinator.State() + state.routes.push(.detail(testDetailState(room: room))) + state.routes.push(.myOpinions(opinions)) + let store = TestStore(initialState: state) { ClassCoordinator() } withDependencies: { + $0.classResultUseCase = useCase + $0.uuid = .incrementing + } + store.exhaustivity = .off + + await store.send(.router(.routeAction(id: 2, action: .myOpinions(.async(.fetchReplyPage(cursor: nil)))))) + await useCase.waitForStarts(1) + await store.send(.router(.updateRoutes(Array(store.state.routes.dropLast())))) + await store.finish(timeout: .seconds(1)) + + #expect(await useCase.cancelledRequestIDs == [1]) + #expect(store.state.routes.count == 2) + } + @Test func 운영자_보고서가_루트로_제거되면_진행중인_로드를_취소한다() async { let room = ClassRoom.mocks[0] @@ -3019,6 +3043,7 @@ private struct FixedClassResultUseCase: ClassResultInterface { private actor SuspendedClassUseCase: ClassResultInterface { private var nextRequestID = 0 private var requests: [Int: CheckedContinuation] = [:] + private var activityRequests: [Int: CheckedContinuation] = [:] private var startedCount = 0 private var startWaiters: [(Int, CheckedContinuation)] = [] private(set) var cancelledRequestIDs: [Int] = [] @@ -3048,6 +3073,12 @@ private actor SuspendedClassUseCase: ClassResultInterface { } } + private func cancelActivity(_ id: Int) { + guard let continuation = activityRequests.removeValue(forKey: id) else { return } + cancelledRequestIDs.append(id) + continuation.resume(throwing: CancellationError()) + } + private func cancelReport(_ id: Int) { guard let continuation = requests.removeValue(forKey: id) else { return } cancelledRequestIDs.append(id) @@ -3082,7 +3113,15 @@ private actor SuspendedClassUseCase: ClassResultInterface { roomID _: Int, cursor _: String? ) async throws -> ClassMyActivity { - throw ClassError.network("이 테스트는 내 활동 조회를 사용하지 않습니다.") + let id = nextID() + return try await withTaskCancellationHandler { + try await withCheckedThrowingContinuation { continuation in + activityRequests[id] = continuation + didStart() + } + } onCancel: { + Task { await self.cancelActivity(id) } + } } private static var fixture: ClassResult { From cc957743bfc6de47a08e0571649670a75abc09bc Mon Sep 17 00:00:00 2001 From: Roy Date: Tue, 3 Nov 2026 22:29:19 +0900 Subject: [PATCH 15/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=BD=94?= =?UTF-8?q?=EB=94=94=EB=84=A4=EC=9D=B4=ED=84=B0=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EB=B0=B1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9D=98=20uuid=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TestStore 밖에서 만들던 클래스 상세 상태를 withDependencies(uuid incrementing) 안에서 생성 - 10/25부터 실패하던 AI 생성 취소 테스트 3건이 실기기에서 통과 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../ClassCoordinatorFeedbackTests.swift | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Projects/App/Tests/Sources/ClassCoordinatorFeedbackTests.swift b/Projects/App/Tests/Sources/ClassCoordinatorFeedbackTests.swift index 5c757c21..a2cb85e4 100644 --- a/Projects/App/Tests/Sources/ClassCoordinatorFeedbackTests.swift +++ b/Projects/App/Tests/Sources/ClassCoordinatorFeedbackTests.swift @@ -28,7 +28,11 @@ struct ClassCoordinatorFeedbackTests { removed.report = MockClassReport.figma removed.sourceViewState = .ready var state = ClassCoordinator.State() - state.routes.push(.detail(.init(room: room))) + state.routes.push(.detail(withDependencies { + $0.uuid = .incrementing + } operation: { + ClassDetailFeature.State(room: room) + })) state.routes.push(.report(first)) state.routes.push(.report(removed)) let store = TestStore(initialState: state) { ClassCoordinator() } withDependencies: { @@ -69,7 +73,11 @@ struct ClassCoordinatorFeedbackTests { original.report = MockClassReport.figma original.sourceViewState = .ready var state = ClassCoordinator.State() - state.routes.push(.detail(.init(room: room))) + state.routes.push(.detail(withDependencies { + $0.uuid = .incrementing + } operation: { + ClassDetailFeature.State(room: room) + })) state.routes.push(.report(original)) let store = TestStore(initialState: state) { ClassCoordinator() } withDependencies: { $0.classResultUseCase = generator @@ -116,7 +124,11 @@ struct ClassCoordinatorFeedbackTests { original.dashboard = MockClassOwnerDashboard.figma original.sourceViewState = .ready var state = ClassCoordinator.State() - state.routes.push(.detail(.init(room: room))) + state.routes.push(.detail(withDependencies { + $0.uuid = .incrementing + } operation: { + ClassDetailFeature.State(room: room) + })) state.routes.push(.ownerDashboard(original)) let store = TestStore(initialState: state) { ClassCoordinator() } withDependencies: { $0.classResultUseCase = generator From 13367d7c68feaf6954b08c4ce183b0f5c77a143d Mon Sep 17 00:00:00 2001 From: Roy Date: Wed, 4 Nov 2026 00:30:30 +0900 Subject: [PATCH 16/21] =?UTF-8?q?=EB=A6=AC=ED=8F=AC=ED=8A=B8=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=9D=84=20=EB=8B=AB=EC=9C=BC=EB=A9=B4=20=ED=88=AC?= =?UTF-8?q?=ED=91=9C=20=EA=B8=B0=EB=A1=9D=C2=B7=EC=9D=B8=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EC=9A=94=EC=B2=AD=EB=8F=84=20=EC=B7=A8=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 스와이프 등으로 리포트 라우트가 제거될 때 투표 기록·인사이트 요청까지 취소(뒤로 가기와 같은 5종) - 테스트 대역의 인사이트 조회를 멈춰 둘 수 있게 바꾸고 라우트 제거 시 인사이트 취소 테스트 추가(수정 전 실패 확인) - 운영자 대시보드 라우트 제거 시 내 활동 요청 취소 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Navigation/Class/ClassCoordinator.swift | 2 + .../Tests/Sources/ClassCoordinatorTests.swift | 61 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift index aeefa673..e8e82d28 100644 --- a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift +++ b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift @@ -516,7 +516,9 @@ private extension ClassCoordinator { cancelledReports.flatMap { id in [ .cancel(id: ClassReportFeature.CancelID.load(id)), + .cancel(id: ClassReportFeature.CancelID.voteHistory(id)), .cancel(id: ClassReportFeature.CancelID.generate(id)), + .cancel(id: ClassReportFeature.CancelID.insight(id)), .cancel(id: ClassReportFeature.CancelID.myActivity(id)), ] } diff --git a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift index c9f7e97c..729b2ed4 100644 --- a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift +++ b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift @@ -1410,6 +1410,50 @@ struct ClassCoordinatorTests { #expect(store.state.routes.count == 2) } + @Test + func 스와이프로_멤버_보고서_경로를_제거하면_진행중인_인사이트_조회를_취소한다() async { + let room = ClassRoom.mockClosedSociety + let useCase = SuspendedClassUseCase() + var state = ClassCoordinator.State() + state.routes.push(.detail(testDetailState(room: room))) + state.routes.push(.report(.init(room: room, instanceID: UUID()))) + let store = TestStore(initialState: state) { ClassCoordinator() } withDependencies: { + $0.classResultUseCase = useCase + $0.uuid = .incrementing + } + store.exhaustivity = .off + + await store.send(.router(.routeAction(id: 2, action: .report(.async(.fetchInsight))))) + await useCase.waitForStarts(1) + await store.send(.router(.updateRoutes(Array(store.state.routes.dropLast())))) + await store.finish(timeout: .seconds(1)) + + #expect(await useCase.cancelledRequestIDs == [1]) + #expect(store.state.routes.count == 2) + } + + @Test + func 스와이프로_운영자_보고서_경로를_제거하면_진행중인_내_활동_조회를_취소한다() async { + let room = ClassRoom.mocks[0] + let useCase = SuspendedClassUseCase() + var state = ClassCoordinator.State() + state.routes.push(.detail(testDetailState(room: room))) + state.routes.push(.ownerDashboard(.init(room: room, instanceID: UUID()))) + let store = TestStore(initialState: state) { ClassCoordinator() } withDependencies: { + $0.classResultUseCase = useCase + $0.uuid = .incrementing + } + store.exhaustivity = .off + + await store.send(.router(.routeAction(id: 2, action: .ownerDashboard(.async(.fetchMyActivity))))) + await useCase.waitForStarts(1) + await store.send(.router(.updateRoutes(Array(store.state.routes.dropLast())))) + await store.finish(timeout: .seconds(1)) + + #expect(await useCase.cancelledRequestIDs == [1]) + #expect(store.state.routes.count == 2) + } + @Test func 운영자_보고서가_루트로_제거되면_진행중인_로드를_취소한다() async { let room = ClassRoom.mocks[0] @@ -3044,6 +3088,7 @@ private actor SuspendedClassUseCase: ClassResultInterface { private var nextRequestID = 0 private var requests: [Int: CheckedContinuation] = [:] private var activityRequests: [Int: CheckedContinuation] = [:] + private var insightRequests: [Int: CheckedContinuation] = [:] private var startedCount = 0 private var startWaiters: [(Int, CheckedContinuation)] = [] private(set) var cancelledRequestIDs: [Int] = [] @@ -3073,6 +3118,12 @@ private actor SuspendedClassUseCase: ClassResultInterface { } } + private func cancelInsight(_ id: Int) { + guard let continuation = insightRequests.removeValue(forKey: id) else { return } + cancelledRequestIDs.append(id) + continuation.resume(throwing: CancellationError()) + } + private func cancelActivity(_ id: Int) { guard let continuation = activityRequests.removeValue(forKey: id) else { return } cancelledRequestIDs.append(id) @@ -3102,7 +3153,15 @@ private actor SuspendedClassUseCase: ClassResultInterface { } func fetchInsight(roomID _: Int) async throws -> ClassAIInsight { - throw ClassError.network("이 테스트는 인사이트 조회를 사용하지 않습니다.") + let id = nextID() + return try await withTaskCancellationHandler { + try await withCheckedThrowingContinuation { continuation in + insightRequests[id] = continuation + didStart() + } + } onCancel: { + Task { await self.cancelInsight(id) } + } } func retryInsight(roomID _: Int) async throws { From 16e1590686d3cb8119793d52b8dbb75b035becf3 Mon Sep 17 00:00:00 2001 From: Roy Date: Wed, 4 Nov 2026 00:30:31 +0900 Subject: [PATCH 17/21] =?UTF-8?q?=EC=9A=B4=EC=98=81=EC=9E=90=20=EB=8C=80?= =?UTF-8?q?=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EB=82=B4=EA=B0=80=20=EB=82=A8?= =?UTF-8?q?=EA=B8=B4=20=EC=9D=98=EA=B2=AC=20=EB=AC=B8=EA=B5=AC=EB=A5=BC=20?= =?UTF-8?q?=EB=A6=AC=ED=8F=AC=ED=8A=B8=EC=99=80=20=EB=A7=9E=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 개인 피드백을 받지 못하면(FAILED 등) 개수를 모르므로 '내가 남긴 의견 보기'로 표시(실기기에서 의견 1건인데 0개로 보이던 문제) - 뒤로 가기·다시 불러오기 시 내 활동 요청 취소, 받은 추천 초기화, 이전 응답 무시 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Reducer/ClassOwnerDashboardFeature.swift | 4 +- .../Sources/ClassOwnerDashboardTests.swift | 80 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/Class/Sources/ClassOwnerDashboard/Reducer/ClassOwnerDashboardFeature.swift b/Projects/Feature/Class/Sources/ClassOwnerDashboard/Reducer/ClassOwnerDashboardFeature.swift index 546eca44..5e00330f 100644 --- a/Projects/Feature/Class/Sources/ClassOwnerDashboard/Reducer/ClassOwnerDashboardFeature.swift +++ b/Projects/Feature/Class/Sources/ClassOwnerDashboard/Reducer/ClassOwnerDashboardFeature.swift @@ -199,7 +199,9 @@ public struct ClassOwnerDashboardFeature { } public var myOpinionsTitle: String { - "내가 남긴 의견 \(myOpinionCount)개 보기" + // 리포트 화면과 같이 개인 피드백을 받지 못하면 개수를 모르므로 표시하지 않는다. + if classResult != nil, generatedFeedback == nil { return "내가 남긴 의견 보기" } + return "내가 남긴 의견 \(myOpinionCount)개 보기" } public var canOpenMyOpinions: Bool { diff --git a/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift b/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift index 12a3ee45..6850a659 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassOwnerDashboardTests.swift @@ -259,6 +259,7 @@ struct ClassOwnerDashboardTests { var feedback: ClassAIPersonalFeedback? = nil var insight: ClassAIInsight? = nil var myActivity: ClassMyActivity? = nil + var suspendsMyActivity = false func fetchResult(roomID _: Int) async throws -> ClassResult { try result.get() @@ -282,6 +283,7 @@ struct ClassOwnerDashboardTests { roomID _: Int, cursor _: String? ) async throws -> ClassMyActivity { + if suspendsMyActivity { try await Task.never() } guard let myActivity else { throw ClassAIFeedbackError.temporarilyUnavailable } return myActivity } @@ -464,6 +466,84 @@ struct ClassOwnerDashboardTests { #expect(store.state.myActivityRequestID == nil) } + @Test + func 뒤로_가면_진행중인_내_활동_조회를_취소한다() async { + let room = ClassRoom.mocks[0] + let state = ClassOwnerDashboardFeature.State(room: room, instanceID: UUID()) + let store = TestStore(initialState: state) { ClassOwnerDashboardFeature() } withDependencies: { + $0.uuid = .incrementing + $0.classResultUseCase = ResultStub( + result: .success(serverResult(room)), + suspendsMyActivity: true + ) + } + store.exhaustivity = .off + + await store.send(.async(.fetchMyActivity)) + #expect(store.state.myActivityRequestID != nil) + await store.send(.view(.backTapped)) + await store.finish(timeout: .seconds(1)) + + #expect(store.state.myActivityRequestID == nil) + } + + @Test + func 다시_불러오면_받은_추천을_비우고_이전_내_활동_응답을_무시한다() async throws { + let room = ClassRoom.mocks[0] + var state = ClassOwnerDashboardFeature.State(room: room, instanceID: UUID()) + state.receivedLikeCount = 12 + let activity = ClassMyActivity( + classID: room.id, + receivedLikeCount: 30, + commentCount: 0, + comments: [], + nextCursor: nil, + hasNext: false + ) + let store = TestStore(initialState: state) { ClassOwnerDashboardFeature() } withDependencies: { + $0.uuid = .incrementing + $0.classResultUseCase = ResultStub( + result: .success(serverResult(room)), + suspendsMyActivity: true + ) + } + store.exhaustivity = .off + + await store.send(.async(.fetchMyActivity)) + let staleRequestID = try #require(store.state.myActivityRequestID) + await store.send(.view(.retryTapped)) + await store.receive(\.async.fetchDashboard) + #expect(store.state.receivedLikeCount == nil) + #expect(store.state.myActivityRequestID == nil) + await store.receive(\.inner.resultResponse) + await store.send(.inner(.myActivityResponse( + instanceID: state.instanceID, + requestID: staleRequestID, + roomID: room.id, + .success(activity) + ))) + #expect(store.state.receivedLikeCount == nil) + await store.finish(timeout: .seconds(1)) + } + + @Test + func 개인_피드백이_없으면_내가_남긴_의견_버튼에_개수를_표시하지_않는다() { + let room = ClassRoom.mocks[0] + var state = ClassOwnerDashboardFeature.State(room: room, instanceID: UUID()) + #expect(state.myOpinionsTitle == "내가 남긴 의견 0개 보기") + state.classResult = serverResult(room) + #expect(state.myOpinionsTitle == "내가 남긴 의견 보기") + state.generatedFeedback = ClassAIPersonalFeedback( + summaryTitle: "개인 결과", + summaryDetail: "참여 분석", + points: [], + nextQuestion: "다음 질문", + nextHint: "", + perspectiveCount: 2 + ) + #expect(state.myOpinionsTitle == "내가 남긴 의견 2개 보기") + } + @Test func 서버_현황_표시값은_State에서_계산한다() { let room = ClassRoom.mocks[0] From 316f47859a16a106829c360b9910c20cfc9b0775 Mon Sep 17 00:00:00 2001 From: Roy Date: Wed, 4 Nov 2026 00:30:31 +0900 Subject: [PATCH 18/21] =?UTF-8?q?=EB=82=B4=20=EC=9D=98=EA=B2=AC=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EC=8B=9C=EB=8F=84=20=EC=8B=9C=20=EB=B9=84?= =?UTF-8?q?=EC=96=B4=20=EC=9E=88=EB=8A=94=20=EC=9D=98=EA=B2=AC=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=EB=8F=84=20=EB=8B=A4=EC=8B=9C=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 목록과 내 의견 조회가 모두 실패한 뒤 다시 시도하면 오류 문구만 사라져 빈 목록처럼 보이던 문제 수정 - 목록이 비어 있고 진행 중인 목록 요청이 없을 때만 함께 다시 불러오고, 목록이 있으면 유지 - 목록 재조회·목록 유지 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Reducer/ClassBattleFlowFeature.swift | 7 +++- .../Tests/Sources/ClassBattleFlowTests.swift | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift index ca97be4a..62f543fc 100644 --- a/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift +++ b/Projects/Feature/Class/Sources/ClassBattleFlow/Reducer/ClassBattleFlowFeature.swift @@ -502,7 +502,12 @@ extension ClassBattleFlowFeature { guard state.step == .some(.completed), state.hasPublishedPerspective == nil, state.myPerspectiveRequestID == nil else { return .none } state.perspectiveErrorMessage = nil - return loadMyPerspectives(state: &state) + // 목록도 실패해 비어 있으면 함께 다시 불러온다. 오류 문구만 지우면 빈 목록처럼 보인다. + let reloadsPerspectives = state.perspectives.isEmpty && state.perspectiveRequestID == nil + return .merge( + reloadsPerspectives ? loadPerspectives(state: &state, cursor: nil) : .none, + loadMyPerspectives(state: &state) + ) case .backTapped: let instanceID = state.instanceID return .concatenate( diff --git a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift index 3f1c418c..a9205c62 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassBattleFlowTests.swift @@ -1621,6 +1621,42 @@ struct ClassBattleFlowTests { #expect(store.state.perspectives.map(\.id) == [5]) } + @Test + func 목록과_내_의견이_모두_실패한_뒤_다시_시도하면_목록도_다시_불러온다() async { + var state = completedState(perspectives: []) + state.hasPublishedPerspective = nil + state.perspectiveErrorMessage = "목록 오류" + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing + $0.classBattleUseCase = ClassBattleFlowUseCaseStub(spy: ClassBattleFlowSpy()) + } + store.exhaustivity = .off + + await store.send(.view(.retryMyPerspectiveTapped)) + + #expect(store.state.perspectiveRequestID != nil) + #expect(store.state.isLoadingPerspectives) + #expect(store.state.myPerspectiveRequestID != nil) + #expect(store.state.perspectiveErrorMessage == nil) + } + + @Test + func 목록이_있으면_다시_시도해도_내_의견만_다시_불러오고_목록은_유지한다() async { + var state = completedState(perspectives: [perspectiveItem(id: 9, isMine: false)]) + state.hasPublishedPerspective = nil + let store = TestStore(initialState: state) { ClassBattleFlowFeature() } withDependencies: { + $0.uuid = .incrementing + $0.classBattleUseCase = ClassBattleFlowUseCaseStub(spy: ClassBattleFlowSpy()) + } + store.exhaustivity = .off + + await store.send(.view(.retryMyPerspectiveTapped)) + + #expect(store.state.perspectiveRequestID == nil) + #expect(store.state.perspectives.map(\.id) == [9]) + #expect(store.state.myPerspectiveRequestID != nil) + } + private func completedState(perspectives: [ClassPerspectiveItem]) -> ClassBattleFlowFeature.State { var state = battleState(room: .mocks[0]) state.session = session(step: .completed, commentRequired: false) From 1e8b7c474472df4b729e935448bb141eb356ddb8 Mon Sep 17 00:00:00 2001 From: Roy Date: Sat, 7 Nov 2026 16:41:12 +0900 Subject: [PATCH 19/21] =?UTF-8?q?=EC=9D=B4=EC=9A=A9=EA=B6=8C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EA=B0=92=EC=9D=84=20=EB=AA=A8=EB=A5=B4=EB=A9=B4=20?= =?UTF-8?q?=EB=A7=8C=EB=A3=8C=EB=A1=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Android ClassTicketStatus.from과 같이 알 수 없는 이용권 상태값을 잘못된 응답 대신 만료로 매핑 - 상태값 매핑 테스트 갱신 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../ClassDomain/Sources/Model/Mapper/ClassTicketDTO+.swift | 3 ++- .../ClassDomain/Tests/Sources/ClassTicketAPITests.swift | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassTicketDTO+.swift b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassTicketDTO+.swift index bec26639..253e1ec1 100644 --- a/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassTicketDTO+.swift +++ b/Projects/Domain/ClassDomain/Sources/Model/Mapper/ClassTicketDTO+.swift @@ -4,8 +4,9 @@ import PickeCoreUtility extension ClassTicketDTO { func toDomain() throws -> ClassTicket { + // Android `ClassTicketStatus.from`과 같이 모르는 상태값은 만료로 본다. + let status = ClassTicket.Status(rawValue: self.status) ?? .expired guard - let status = ClassTicket.Status(rawValue: status), let activatedAt = ServerDateParser.parse(activatedAt), let expiresAt = ServerDateParser.parse(expiresAt), let readGraceEndsAt = ServerDateParser.parse(readGraceEndsAt) diff --git a/Projects/Domain/ClassDomain/Tests/Sources/ClassTicketAPITests.swift b/Projects/Domain/ClassDomain/Tests/Sources/ClassTicketAPITests.swift index b466520e..6fd732c3 100644 --- a/Projects/Domain/ClassDomain/Tests/Sources/ClassTicketAPITests.swift +++ b/Projects/Domain/ClassDomain/Tests/Sources/ClassTicketAPITests.swift @@ -72,12 +72,12 @@ struct ClassTicketAPITests { } @Test - func 알_수_없는_이용권_상태는_잘못된_응답으로_처리한다() throws { + func 알_수_없는_이용권_상태는_만료로_처리한다() throws { let json = """ {"ticketId":3,"capacity":15,"status":"REVOKED","activatedAt":"2026-10-01T10:00:00",\ "expiresAt":"2027-04-01T10:00:00","remainingDays":0,"readGraceEndsAt":"2027-05-01T10:00:00"} """ let payload = try JSONDecoder().decode(ClassTicketDTO.self, from: Data(json.utf8)) - #expect(throws: ClassTicketError.invalidResponse) { try payload.toDomain() } + #expect(try payload.toDomain().status == .expired) } } From 3bb9296d16240ec9fa13d4b755be62df4819b0fe Mon Sep 17 00:00:00 2001 From: Roy Date: Sat, 7 Nov 2026 16:41:13 +0900 Subject: [PATCH 20/21] =?UTF-8?q?=EA=B3=B5=EC=9A=A9=20=ED=8C=9D=EC=97=85?= =?UTF-8?q?=EC=97=90=20=EC=95=88=EB=82=B4=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=EA=B3=BC=20=EC=9D=B4=EC=9A=A9=EA=B6=8C=20=EC=95=88=EB=82=B4=20?= =?UTF-8?q?=EB=AC=B8=EA=B5=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - notice(취소 왼쪽·확인 오른쪽, 취소 문구가 비면 확인 1개)와 noticeReversed(확인 왼쪽·취소 오른쪽) 스타일 추가 - 배경 탭이 취소라서 "안내만 닫기"가 항상 취소가 되도록 배치를 나눔 - Android와 같은 클래스 이용권 안내 7종 팩토리와 단일 버튼 행 헬퍼 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Sources/Alert/CustomAlertState.swift | 60 +++++++++++++++++++ ...stomConfirmationPopupView+Components.swift | 10 ++++ .../CustomConfirmationPopupView+Content.swift | 39 +++++++++++- 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/Projects/UI/PickeSharedUI/Sources/Alert/CustomAlertState.swift b/Projects/UI/PickeSharedUI/Sources/Alert/CustomAlertState.swift index a0c6522b..95e9342e 100644 --- a/Projects/UI/PickeSharedUI/Sources/Alert/CustomAlertState.swift +++ b/Projects/UI/PickeSharedUI/Sources/Alert/CustomAlertState.swift @@ -45,6 +45,10 @@ public enum CustomAlertStyle: Equatable { case logout case withdraw case suggestTopic + /// 본문 + 취소(왼쪽 밝은)·확인(오른쪽 primary), 취소 문구가 비면 확인 1개. + case notice + /// 본문 + 확인(왼쪽 밝은)·취소(오른쪽 primary). 배경 탭(취소)이 "안내만 닫기"여야 할 때 쓴다. + case noticeReversed } @CasePathable @@ -162,4 +166,60 @@ public extension CustomAlertState where Action == CustomAlertAction { style: .suggestTopic ) } + + // MARK: - 클래스 이용권 안내 (Android 문구) + + static func classTicketRequired() -> CustomAlertState { + notice( + "클래스를 열려면 이용권이 필요해요\n클래스 기능을 먼저 알아보세요!", + confirmTitle: "뒤로가기" + ) + } + + static func classTicketExpired() -> CustomAlertState { + CustomAlertState( + title: "사용 중이던 이용권이 종료되었습니다\n새로운 이용권을 등록해주세요!", + confirmTitle: "등록하기", + cancelTitle: "뒤로가기", + style: .notice + ) + } + + /// 뒤로가기=확인(화면 닫기), 등록하기=취소(안내만 닫기). 배경 탭도 안내만 닫는다. + static func ticketAlreadyRegistered() -> CustomAlertState { + CustomAlertState( + title: "이미 등록된 이용권입니다\n다른 이용권을 등록해주세요!", + confirmTitle: "뒤로가기", + cancelTitle: "등록하기", + style: .noticeReversed + ) + } + + static func ticketInvalidCode() -> CustomAlertState { + notice("코드 번호를 다시 확인해주세요!") + } + + static func ticketRegistrationExpired() -> CustomAlertState { + notice("해당 이용권은 등록기간이 만료되었습니다") + } + + static func ticketRegisterFailed() -> CustomAlertState { + notice("코드 등록이 완료되지 않았습니다\n네트워크 확인 후 한 번 더 시도해주세요") + } + + static func ticketAlreadyActive() -> CustomAlertState { + notice("이미 사용 중인 이용권이 있어요\n만료 후 등록할 수 있어요") + } + + private static func notice( + _ title: String, + confirmTitle: String = "확인" + ) -> CustomAlertState { + CustomAlertState( + title: title, + confirmTitle: confirmTitle, + cancelTitle: "", + style: .notice + ) + } } diff --git a/Projects/UI/PickeSharedUI/Sources/Alert/CustomPopup/CustomConfirmationPopupView+Components.swift b/Projects/UI/PickeSharedUI/Sources/Alert/CustomPopup/CustomConfirmationPopupView+Components.swift index 74f9357a..bbbdb81d 100644 --- a/Projects/UI/PickeSharedUI/Sources/Alert/CustomPopup/CustomConfirmationPopupView+Components.swift +++ b/Projects/UI/PickeSharedUI/Sources/Alert/CustomPopup/CustomConfirmationPopupView+Components.swift @@ -65,6 +65,16 @@ extension CustomConfirmationPopup { } } + /// primary 버튼 1개 행. + func pickeSingleButtonRow( + title: String, + action: @escaping () -> Void + ) -> some View { + pickePrimaryButton(title: title, action: action) + .padding(.horizontal, 16) + .padding(.bottom, 16) + } + @ViewBuilder private func pickeSecondaryButton( title: String, diff --git a/Projects/UI/PickeSharedUI/Sources/Alert/CustomPopup/CustomConfirmationPopupView+Content.swift b/Projects/UI/PickeSharedUI/Sources/Alert/CustomPopup/CustomConfirmationPopupView+Content.swift index c9647f31..743ac997 100644 --- a/Projects/UI/PickeSharedUI/Sources/Alert/CustomPopup/CustomConfirmationPopupView+Content.swift +++ b/Projects/UI/PickeSharedUI/Sources/Alert/CustomPopup/CustomConfirmationPopupView+Content.swift @@ -18,7 +18,7 @@ extension CustomConfirmationPopup { switch style { case .confirmation: return 20 - case .finalVote, .report, .alreadyWatched, .deleteConfirm, .logout, .withdraw, .suggestTopic: + case .finalVote, .report, .alreadyWatched, .deleteConfirm, .logout, .withdraw, .suggestTopic, .notice, .noticeReversed: return 0 } } @@ -40,6 +40,10 @@ extension CustomConfirmationPopup { logoutWithdrawContent case .suggestTopic: suggestTopicContent + case .notice: + noticeContent + case .noticeReversed: + noticeReversedContent } } @@ -58,6 +62,39 @@ extension CustomConfirmationPopup { } } + /// 안내 — 본문 + 취소(왼쪽 밝은)·확인(오른쪽 primary). 취소 문구가 비면 확인 버튼 1개. + @ViewBuilder + var noticeContent: some View { + pickeAlertCard { + VStack(spacing: 16) { + pickeBodyText(title) + + if cancelTitle.isEmpty { + pickeSingleButtonRow(title: confirmTitle, action: onConfirm) + } else { + pickeTwoButtonRow( + leftTitle: cancelTitle, leftAction: onCancel, + rightTitle: confirmTitle, rightAction: onConfirm + ) + } + } + } + } + + /// 안내(반대 배치) — 확인(왼쪽 밝은)·취소(오른쪽 primary). 배경 탭(취소)이 "안내만 닫기"가 되게 한다. + var noticeReversedContent: some View { + pickeAlertCard { + VStack(spacing: 16) { + pickeBodyText(title) + + pickeTwoButtonRow( + leftTitle: confirmTitle, leftAction: onConfirm, + rightTitle: cancelTitle, rightAction: onCancel + ) + } + } + } + /// 주제 제안 — 타이틀 + 본문 + 뒤로가기(왼쪽 밝은) / 제안하기(오른쪽 primary). var suggestTopicContent: some View { pickeAlertCard { From ba488918dc697bc8dab56f408103f415bad89ac6 Mon Sep 17 00:00:00 2001 From: Roy Date: Sat, 7 Nov 2026 16:41:37 +0900 Subject: [PATCH 21/21] =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=EA=B6=8C=20=EB=8F=99=EC=9E=91=EC=9D=84=20Android?= =?UTF-8?q?=EC=99=80=20=EB=A7=9E=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 새 클래스 만들기 전에 내 이용권을 확인: 사용 중이면 생성, 만료면 등록 안내(등록하기 → 마이 탭 코드 입력), 없으면 이용권 필요 안내, 조회 실패는 토스트 - 확인 중 다른 이동이나 늦은 응답이 화면을 쌓지 않도록 확인 취소와 만들기 루트 가드 추가 - 이용권 화면: 만료·없음이면 코드 입력부터, 진입 조회 실패는 토스트 후 닫기, 내가 쓰는 코드 재입력은 기존 개요로 이동(만료면 만료 안내) - 코드 확인·등록 오류를 입력창 아래 글자 대신 Android 안내창으로 표시(사용 중 이용권 409만 iOS에서 따로 안내) - 확인 단계 뒤로가기는 코드 입력으로, 코드는 제출 시 ASCII 영문·숫자·하이픈 대문자로 정규화 - 확인 단계·FAQ·개요 문구를 Android와 같게, 안내 중 탭바 숨김, 이용권 라우트 제거 시 진입 조회 취소 - 기존에 실패하던 이용권 App 테스트 3건을 현재 등록 흐름으로 수정하고 상태별·코디네이터 테스트 추가 Claude-Session: https://claude.ai/code/session_012p64qYTmqHhkJDFnd2C6KB --- .../Navigation/Class/ClassCoordinator.swift | 41 ++- .../MainTab/MainTabCoordinator.swift | 16 +- .../Profile/ProfileCoordinator.swift | 31 +- .../Tests/Sources/ClassCoordinatorTests.swift | 150 ++++++++- .../Sources/ClassTicketVisualAuditTests.swift | 4 +- .../Sources/MainTabCoordinatorTests.swift | 39 +++ .../ProfileTicketCoordinatorTests.swift | 100 ++++++ .../Model/ClassCreateTicketNotice.swift | 23 ++ .../Reducer/ClassIntroFeature.swift | 101 +++++- .../ClassIntro/View/ClassIntroView.swift | 5 + .../Model/ClassTicketRegisterNotice.swift | 40 +++ .../Reducer/ClassTicketFeature.swift | 148 +++++++-- .../ClassTicket/View/ClassTicketView.swift | 50 +-- .../Class/Tests/Sources/ClassTests.swift | 115 ++++++- .../Tests/Sources/ClassTicketTests.swift | 305 ++++++++++++++++-- 15 files changed, 1077 insertions(+), 91 deletions(-) create mode 100644 Projects/Feature/Class/Sources/ClassIntro/Model/ClassCreateTicketNotice.swift create mode 100644 Projects/Feature/Class/Sources/ClassTicket/Model/ClassTicketRegisterNotice.swift diff --git a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift index e8e82d28..35584024 100644 --- a/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift +++ b/Projects/App/Sources/Navigation/Class/ClassCoordinator.swift @@ -61,8 +61,8 @@ public struct ClassCoordinator { case let .members(members)?: return members.filter == nil && members.editName == nil && members.customAlert == nil && !members.isSaving - case .ticket?: - return true + case let .ticket(ticket)?: + return ticket.customAlert == nil default: return false } @@ -451,7 +451,12 @@ private extension ClassCoordinator { .cancel(id: ClassAIVoteFeature.CancelID.submit(id)), ] } - + removedTickets.map { .cancel(id: ClassTicketFeature.CancelID.check($0)) } + + removedTickets.flatMap { id in + [ + .cancel(id: ClassTicketFeature.CancelID.check(id)), + .cancel(id: ClassTicketFeature.CancelID.existingTicket(id)), + ] + } + removedBattleFlows.flatMap { id in [ .cancel(id: ClassBattleFlowFeature.CancelID.load(id)), @@ -607,6 +612,30 @@ private extension ClassCoordinator { state.routes.push(.ticket(registration)) return .none + case let .routeAction(routeID, action: .ticket(.delegate(.existingTicketFound(ticket)))): + // 내가 등록한 사용 중 이용권을 다시 입력함: 바로 아래가 개요면 그리로 돌아가고, 아니면 이 화면을 개요로 바꾼다. + guard routeID == state.routes.count - 1, + case var .ticket(current)? = state.routes.last?.screen + else { return .none } + if routeID >= 1, + case var .ticket(previous) = state.routes[routeID - 1].screen, + previous.phase == .overview + { + state.routes.goBack() + previous.ticket = ticket + previous.registrationStartDate = ticket.activatedAt + previous.expirationDate = ticket.expiresAt + state.routes[screenAt: routeID - 1] = .ticket(previous) + return .none + } + current.preview = nil + current.ticket = ticket + current.registrationStartDate = ticket.activatedAt + current.expirationDate = ticket.expiresAt + current.phase = .overview + state.routes[screenAt: routeID] = .ticket(current) + return .none + case let .routeAction(routeID, action: .ticket(.delegate(.dismiss))): guard routeID == state.routes.count - 1, case .ticket = state.routes.last?.screen @@ -689,7 +718,11 @@ private extension ClassCoordinator { state.routes.push(.myClasses(.init())) return .none - case .routeAction(_, action: .intro(.delegate(.create))): + case let .routeAction(routeID, action: .intro(.delegate(.create))): + // 이용권 확인 응답이 늦게 와도 소개 화면이 맨 위일 때만 만들기 흐름을 연다. + guard routeID == 0, state.routes.count == 1, + case .intro = state.routes[0].screen + else { return .none } invalidateCompletingJoin(state: &state) state.routes.push(.startMethod(.init())) return .none diff --git a/Projects/App/Sources/Navigation/MainTab/MainTabCoordinator.swift b/Projects/App/Sources/Navigation/MainTab/MainTabCoordinator.swift index c2c59f53..e1630292 100644 --- a/Projects/App/Sources/Navigation/MainTab/MainTabCoordinator.swift +++ b/Projects/App/Sources/Navigation/MainTab/MainTabCoordinator.swift @@ -73,7 +73,7 @@ public struct MainTabCoordinator { case .classroom: return !classState.showsTabBar case .myPage: - if case .ticket? = myPageState.routes.last?.screen { return false } + if case let .ticket(ticket)? = myPageState.routes.last?.screen { return ticket.customAlert != nil } return myPageState.routes.count > 1 case .none: return false @@ -186,6 +186,20 @@ public struct MainTabCoordinator { state.selectedTab = Tab.myPage.rawValue return .none + case .classroom(.router(.routeAction(0, action: .intro(.delegate(.registerTicket))))): + // 만료 안내의 등록하기: Android와 같이 마이 탭의 코드 입력 화면으로 바로 간다. + state.classState.routes.goBackToRoot() + state.myPageState.routes.goBackToRoot() + var registration = ClassTicketFeature.State() + registration.shouldCheckExistingTicket = false + state.myPageState.routes.push(.ticket(registration)) + if state.selectedTab != Tab.myPage.rawValue { + state.previousTab = state.selectedTab + trackTabSelection(Tab.myPage.rawValue) + } + state.selectedTab = Tab.myPage.rawValue + return .none + case .myPage(.router(.routeAction(_, action: .settings(.delegate(.sessionEnded))))): return .send(.delegate(.sessionEnded)) diff --git a/Projects/App/Sources/Navigation/Profile/ProfileCoordinator.swift b/Projects/App/Sources/Navigation/Profile/ProfileCoordinator.swift index 9a89082d..7568bbdc 100644 --- a/Projects/App/Sources/Navigation/Profile/ProfileCoordinator.swift +++ b/Projects/App/Sources/Navigation/Profile/ProfileCoordinator.swift @@ -75,7 +75,12 @@ private extension ProfileCoordinator { }) let removedIDs = state.ticketInstanceIDs.subtracting(currentIDs) state.ticketInstanceIDs = currentIDs - return .merge(removedIDs.map { .cancel(id: ClassTicketFeature.CancelID.check($0)) }) + return .merge(removedIDs.flatMap { id in + [ + .cancel(id: ClassTicketFeature.CancelID.check(id)), + .cancel(id: ClassTicketFeature.CancelID.existingTicket(id)), + ] + }) } func routerAction( @@ -215,6 +220,30 @@ private extension ProfileCoordinator { state.routes.push(.ticket(overview)) return .none + case let .routeAction(routeID, action: .ticket(.delegate(.existingTicketFound(ticket)))): + // 내가 등록한 사용 중 이용권을 다시 입력함: 바로 아래가 개요면 그리로 돌아가고, 아니면 이 화면을 개요로 바꾼다. + guard routeID == state.routes.count - 1, + case var .ticket(current)? = state.routes.last?.screen + else { return .none } + if routeID >= 1, + case var .ticket(previous) = state.routes[routeID - 1].screen, + previous.phase == .overview + { + state.routes.goBack() + previous.ticket = ticket + previous.registrationStartDate = ticket.activatedAt + previous.expirationDate = ticket.expiresAt + state.routes[screenAt: routeID - 1] = .ticket(previous) + return .none + } + current.preview = nil + current.ticket = ticket + current.registrationStartDate = ticket.activatedAt + current.expirationDate = ticket.expiresAt + current.phase = .overview + state.routes[screenAt: routeID] = .ticket(current) + return .none + case let .routeAction(routeID, action: .ticket(.delegate(.dismiss))): guard routeID == state.routes.count - 1, case .ticket = state.routes.last?.screen diff --git a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift index 729b2ed4..b849ffe8 100644 --- a/Projects/App/Tests/Sources/ClassCoordinatorTests.swift +++ b/Projects/App/Tests/Sources/ClassCoordinatorTests.swift @@ -1364,6 +1364,22 @@ struct ClassCoordinatorTests { #expect(store.state.routes.count == 2) } + @Test + func 소개_화면이_맨_위가_아니면_늦게_온_만들기_요청을_무시한다() async { + var state = ClassCoordinator.State() + state.routes.push(.myClasses(withDependencies { + $0.uuid = .incrementing + } operation: { + MyClassFeature.State() + })) + let store = TestStore(initialState: state) { ClassCoordinator() } + store.exhaustivity = .off + + await store.send(.router(.routeAction(id: 0, action: .intro(.delegate(.create))))) + + #expect(store.state.routes.count == 2) + } + @Test func 스와이프로_멤버_보고서_경로를_제거하면_진행중인_로드를_취소한다() async { let room = ClassRoom.mockClosedSociety @@ -1737,6 +1753,16 @@ struct ClassParticipantPushTests { let secondEnd = try #require("2027.04.30".date(as: .yearMonthDayDotted)) let clock = TestClock() let originalID = UUID(400) + let preview = ClassTicketPreview(capacity: 30, registerBy: secondEnd, usageMonths: 6, registeredByMe: false) + let secondTicket = ClassTicket( + id: 5, + capacity: 30, + status: .active, + activatedAt: secondStart, + expiresAt: secondEnd, + remainingDays: 180, + readGraceEndsAt: secondEnd + ) var registration = ClassTicketFeature.State(instanceID: originalID) registration.phase = .registered registration.registrationStartDate = firstStart @@ -1748,6 +1774,7 @@ struct ClassParticipantPushTests { $0.uuid = .incrementing $0.continuousClock = clock $0.date = .constant(secondStart) + $0.classTicketUseCase = ClockTicketUseCase(clock: clock, preview: preview, ticket: secondTicket) } await store.send(.router(.routeAction( @@ -1768,7 +1795,9 @@ struct ClassParticipantPushTests { id: 2, action: .ticket(.delegate(.addRegistrationRequested)) ))) { - $0.routes.push(.ticket(.init(instanceID: UUID(1)))) + var registration = ClassTicketFeature.State(instanceID: UUID(1)) + registration.shouldCheckExistingTicket = false + $0.routes.push(.ticket(registration)) $0.ticketInstanceIDs = [originalID, UUID(0), UUID(1)] } await store.send(.router(.routeAction( @@ -1787,10 +1816,28 @@ struct ClassParticipantPushTests { ticket.isChecking = true $0.routes[screenAt: 3] = .ticket(ticket) } + // 현재 계약: 코드 확인은 미리보기만 보여 주고, 등록하기에서 등록한다. + await clock.advance(by: .milliseconds(350)) + await store.receive(\.router.routeAction) { + guard case var .ticket(ticket) = $0.routes[3].screen else { return } + ticket.isChecking = false + ticket.preview = preview + ticket.phase = .preview + $0.routes[screenAt: 3] = .ticket(ticket) + } + await store.send(.router(.routeAction( + id: 3, + action: .ticket(.view(.confirmTapped)) + ))) { + guard case var .ticket(ticket) = $0.routes[3].screen else { return } + ticket.isChecking = true + $0.routes[screenAt: 3] = .ticket(ticket) + } await clock.advance(by: .milliseconds(350)) await store.receive(\.router.routeAction) { guard case var .ticket(ticket) = $0.routes[3].screen else { return } ticket.isChecking = false + ticket.ticket = secondTicket ticket.phase = .registered ticket.registrationStartDate = secondStart ticket.expirationDate = secondEnd @@ -1802,8 +1849,10 @@ struct ClassParticipantPushTests { else { return } overview.registrationStartDate = secondStart overview.expirationDate = secondEnd + overview.ticket = secondTicket original.registrationStartDate = secondStart original.expirationDate = secondEnd + original.ticket = secondTicket $0.routes[screenAt: 2] = .ticket(overview) $0.routes[screenAt: 1] = .ticket(original) } @@ -1837,6 +1886,76 @@ struct ClassParticipantPushTests { #expect(original.periodText == "2026.11.01 – 2027.04.30") } + @Test + func 개요_위_등록_화면에서_사용_중인_내_이용권을_다시_입력하면_개요로_돌아간다() async { + let ticket = ClassTicket( + id: 9, + capacity: 30, + status: .active, + activatedAt: Date(timeIntervalSince1970: 1_800_000_000), + expiresAt: Date(timeIntervalSince1970: 1_815_000_000), + remainingDays: 170, + readGraceEndsAt: Date(timeIntervalSince1970: 1_817_592_000) + ) + var overview = ClassTicketFeature.State(instanceID: UUID(310)) + overview.phase = .overview + var registration = ClassTicketFeature.State(instanceID: UUID(311)) + registration.shouldCheckExistingTicket = false + var state = ClassCoordinator.State() + state.routes.push(.ticket(overview)) + state.routes.push(.ticket(registration)) + state.ticketInstanceIDs = [UUID(310), UUID(311)] + let store = TestStore(initialState: state) { ClassCoordinator() } + store.exhaustivity = .off + + await store.send(.router(.routeAction(id: 2, action: .ticket(.delegate(.existingTicketFound(ticket)))))) + + #expect(store.state.routes.count == 2) + guard case let .ticket(current)? = store.state.routes.last?.screen else { + Issue.record("기존 이용권 개요로 돌아가지 않았습니다") + return + } + #expect(current.instanceID == UUID(310)) + #expect(current.ticket == ticket) + } + + @Test + func 바로_연_이용권_화면에서_사용_중인_내_이용권을_다시_입력하면_개요로_바꾸고_안내_중에는_탭바를_숨긴다() async { + let ticket = ClassTicket( + id: 9, + capacity: 30, + status: .active, + activatedAt: Date(timeIntervalSince1970: 1_800_000_000), + expiresAt: Date(timeIntervalSince1970: 1_815_000_000), + remainingDays: 170, + readGraceEndsAt: Date(timeIntervalSince1970: 1_817_592_000) + ) + var registration = ClassTicketFeature.State(instanceID: UUID(312)) + registration.code = "PICKE30" + var state = ClassCoordinator.State() + state.routes.push(.ticket(registration)) + state.ticketInstanceIDs = [UUID(312)] + #expect(state.showsTabBar) + var alerted = registration + alerted.customAlert = .ticketInvalidCode() + var alertedState = state + alertedState.routes[screenAt: 1] = .ticket(alerted) + #expect(!alertedState.showsTabBar) + let store = TestStore(initialState: state) { ClassCoordinator() } + store.exhaustivity = .off + + await store.send(.router(.routeAction(id: 1, action: .ticket(.delegate(.existingTicketFound(ticket)))))) + + #expect(store.state.routes.count == 2) + guard case let .ticket(current)? = store.state.routes.last?.screen else { + Issue.record("이용권 화면이 사라졌습니다") + return + } + #expect(current.instanceID == UUID(312)) + #expect(current.phase == .overview) + #expect(current.ticket == ticket) + } + @Test func 이용권_확인_중_스와이프로_경로가_제거되면_응답도_취소된다() async { let clock = TestClock() @@ -1849,6 +1968,7 @@ struct ClassParticipantPushTests { let store = TestStore(initialState: state) { ClassCoordinator() } withDependencies: { $0.continuousClock = clock $0.date = .constant(Date(timeIntervalSince1970: 0)) + $0.classTicketUseCase = ClockTicketUseCase(clock: clock) } await store.send(.router(.routeAction(id: 1, action: .ticket(.view(.checkTapped))))) { @@ -1937,6 +2057,7 @@ struct ClassParticipantPushTests { let store = TestStore(initialState: state) { ClassCoordinator() } withDependencies: { $0.continuousClock = clock $0.date = .constant(start) + $0.classTicketUseCase = ClockTicketUseCase(clock: clock) } await store.send(.router(.routeAction(id: 3, action: .ticket(.view(.checkTapped))))) { @@ -2830,6 +2951,33 @@ struct ChatPlayerLifecycleTests { } } +/// 코드 확인·등록이 테스트 시계 350ms 뒤에 끝나는 이용권 대역. +private struct ClockTicketUseCase: ClassTicketInterface { + let clock: TestClock + var preview = ClassTicketPreview( + capacity: 30, + registerBy: Date(timeIntervalSince1970: 1_800_000_000), + usageMonths: 6, + registeredByMe: false + ) + var ticket: ClassTicket? + + func previewTicket(code _: String) async throws -> ClassTicketPreview { + try await clock.sleep(for: .milliseconds(350)) + return preview + } + + func registerTicket(code _: String) async throws -> ClassTicket { + try await clock.sleep(for: .milliseconds(350)) + guard let ticket else { throw ClassTicketError.invalidCode } + return ticket + } + + func fetchMyTicket() async throws -> ClassTicket { + throw ClassTicketError.notFound + } +} + private struct SlowBattleUseCase: ClassBattleInterface { let tracker: SlowBattleRequestTracker private let fallback = MockClassRepository() diff --git a/Projects/App/Tests/Sources/ClassTicketVisualAuditTests.swift b/Projects/App/Tests/Sources/ClassTicketVisualAuditTests.swift index 5b166307..7b6640fd 100644 --- a/Projects/App/Tests/Sources/ClassTicketVisualAuditTests.swift +++ b/Projects/App/Tests/Sources/ClassTicketVisualAuditTests.swift @@ -15,7 +15,9 @@ final class ClassTicketVisualAuditTests: XCTestCase { throw XCTSkip("Set PICKE_CLASS_VISUAL_AUDIT=1 to capture ticket screens") } - let entry = ClassTicketFeature.State() + var entry = ClassTicketFeature.State() + // 진입 조회가 실패하면 토스트 후 닫히므로 입력 화면 캡처에서는 조회하지 않는다. + entry.shouldCheckExistingTicket = false var registered = ClassTicketFeature.State() registered.phase = .registered diff --git a/Projects/App/Tests/Sources/MainTabCoordinatorTests.swift b/Projects/App/Tests/Sources/MainTabCoordinatorTests.swift index 2560e66b..4755b1da 100644 --- a/Projects/App/Tests/Sources/MainTabCoordinatorTests.swift +++ b/Projects/App/Tests/Sources/MainTabCoordinatorTests.swift @@ -52,6 +52,45 @@ struct MainTabCoordinatorTests { await store.finish() } + @Test + func 클래스_만료_안내의_등록하기는_마이_탭_코드_입력_화면을_연다() async { + var state = MainTabCoordinator.State() + state.classState.routes.push(.myClasses(withDependencies { + $0.uuid = .incrementing + } operation: { + MyClassFeature.State() + })) + let store = TestStore(initialState: state) { + MainTabCoordinator() + } withDependencies: { + $0.analyticsUseCase.track = { _ in } + } + store.exhaustivity = .off + + await store.send(.classroom(.router(.routeAction(id: 0, action: .intro(.delegate(.registerTicket)))))) + + #expect(store.state.selectedTab == MainTabCoordinator.Tab.myPage.rawValue) + #expect(store.state.classState.routes.count == 1) + guard case let .ticket(ticket)? = store.state.myPageState.routes.last?.screen else { + Issue.record("마이 탭에 이용권 화면이 열리지 않았습니다") + return + } + #expect(ticket.shouldCheckExistingTicket == false) + #expect(ticket.phase == .entry) + } + + @Test + func 마이_탭_이용권_화면은_안내가_떠_있을_때만_탭바를_숨긴다() { + var state = MainTabCoordinator.State(selectedTab: MainTabCoordinator.Tab.myPage.rawValue) + var ticket = ClassTicketFeature.State() + state.myPageState.routes.push(.ticket(ticket)) + #expect(!state.shouldHideTabBar) + + ticket.customAlert = .ticketInvalidCode() + state.myPageState.routes[screenAt: 1] = .ticket(ticket) + #expect(state.shouldHideTabBar) + } + @Test func 탭_변경은_한번만_기록하고_재선택과_잘못된_탭은_기록하지_않는다() async { let events = LockIsolated<[AnalyticsButton]>([]) diff --git a/Projects/App/Tests/Sources/ProfileTicketCoordinatorTests.swift b/Projects/App/Tests/Sources/ProfileTicketCoordinatorTests.swift index 3c280982..405854a3 100644 --- a/Projects/App/Tests/Sources/ProfileTicketCoordinatorTests.swift +++ b/Projects/App/Tests/Sources/ProfileTicketCoordinatorTests.swift @@ -1,3 +1,4 @@ +import ClassDomainInterface import ComposableArchitecture import FeatureAssembly import Foundation @@ -115,4 +116,103 @@ struct ProfileTicketCoordinatorTests { #expect(updated.expirationDate == laterEnd) await store.finish() } + + @Test + func 개요_위에서_사용_중인_내_이용권을_다시_입력하면_기존_개요로_돌아간다() async { + let ticket = profileActiveTicket() + var overview = ClassTicketFeature.State(instanceID: UUID(10)) + overview.phase = .overview + var registration = ClassTicketFeature.State(instanceID: UUID(11)) + registration.shouldCheckExistingTicket = false + var state = Picke.ProfileCoordinator.State() + state.routes.push(.ticket(overview)) + state.routes.push(.ticket(registration)) + let store = TestStore(initialState: state) { Picke.ProfileCoordinator() } withDependencies: { + $0.uuid = .incrementing + } + store.exhaustivity = .off + + await store.send(.router(.routeAction(id: 2, action: .ticket(.delegate(.existingTicketFound(ticket)))))) + + #expect(store.state.routes.count == 2) + guard case let .ticket(current)? = store.state.routes.last?.screen else { + Issue.record("기존 이용권 개요로 돌아가지 않았습니다") + return + } + #expect(current.instanceID == UUID(10)) + #expect(current.phase == .overview) + #expect(current.ticket == ticket) + } + + @Test + func 바로_연_등록_화면에서_사용_중인_내_이용권을_다시_입력하면_그_화면을_개요로_바꾼다() async { + let ticket = profileActiveTicket() + var registration = ClassTicketFeature.State(instanceID: UUID(12)) + registration.code = "PICKE30" + var state = Picke.ProfileCoordinator.State() + state.routes.push(.ticket(registration)) + let store = TestStore(initialState: state) { Picke.ProfileCoordinator() } withDependencies: { + $0.uuid = .incrementing + } + store.exhaustivity = .off + + await store.send(.router(.routeAction(id: 1, action: .ticket(.delegate(.existingTicketFound(ticket)))))) + + #expect(store.state.routes.count == 2) + guard case let .ticket(current)? = store.state.routes.last?.screen else { + Issue.record("이용권 화면이 사라졌습니다") + return + } + #expect(current.instanceID == UUID(12)) + #expect(current.phase == .overview) + #expect(current.ticket == ticket) + #expect(current.preview == nil) + } + + @Test + func 이용권_화면을_닫으면_진입_조회도_취소한다() async { + let ticketID = UUID(13) + var state = Picke.ProfileCoordinator.State() + state.routes.push(.ticket(ClassTicketFeature.State(instanceID: ticketID))) + state.ticketInstanceIDs = [ticketID] + let store = TestStore(initialState: state) { Picke.ProfileCoordinator() } withDependencies: { + $0.uuid = .incrementing + $0.classTicketUseCase = NeverEndingTicketUseCase() + } + store.exhaustivity = .off + + await store.send(.router(.routeAction(id: 1, action: .ticket(.view(.onAppear))))) + await store.send(.router(.updateRoutes(Array(store.state.routes.dropLast())))) + await store.finish(timeout: .seconds(1)) + + #expect(store.state.routes.count == 1) + } +} + +private func profileActiveTicket() -> ClassTicket { + ClassTicket( + id: 7, + capacity: 30, + status: .active, + activatedAt: Date(timeIntervalSince1970: 1_800_000_000), + expiresAt: Date(timeIntervalSince1970: 1_815_000_000), + remainingDays: 170, + readGraceEndsAt: Date(timeIntervalSince1970: 1_817_592_000) + ) +} + +/// 내 이용권 조회가 취소될 때까지 끝나지 않는 대역. +private struct NeverEndingTicketUseCase: ClassTicketInterface { + func previewTicket(code _: String) async throws -> ClassTicketPreview { + throw ClassTicketError.invalidCode + } + + func registerTicket(code _: String) async throws -> ClassTicket { + throw ClassTicketError.invalidCode + } + + func fetchMyTicket() async throws -> ClassTicket { + try await Task.sleep(for: .seconds(60)) + throw ClassTicketError.notFound + } } diff --git a/Projects/Feature/Class/Sources/ClassIntro/Model/ClassCreateTicketNotice.swift b/Projects/Feature/Class/Sources/ClassIntro/Model/ClassCreateTicketNotice.swift new file mode 100644 index 00000000..f43a8b1e --- /dev/null +++ b/Projects/Feature/Class/Sources/ClassIntro/Model/ClassCreateTicketNotice.swift @@ -0,0 +1,23 @@ +// +// ClassCreateTicketNotice.swift +// Class +// +// Created by Picke on 11/4/26. +// + +import PickeSharedUI + +/// 클래스 만들기 전 이용권 확인 결과로 띄우는 안내(Android `ClassTicketDialog`). +public enum ClassCreateTicketNotice: Equatable, Sendable { + /// 등록된 이용권이 없음(`TICKET_404`). 닫기만 한다. + case required + /// 사용 중이던 이용권이 만료됨. 등록하기로 이용권 등록 화면에 간다. + case expired + + public var alert: CustomAlertState { + switch self { + case .required: return .classTicketRequired() + case .expired: return .classTicketExpired() + } + } +} diff --git a/Projects/Feature/Class/Sources/ClassIntro/Reducer/ClassIntroFeature.swift b/Projects/Feature/Class/Sources/ClassIntro/Reducer/ClassIntroFeature.swift index a7c656a3..0f4a599a 100644 --- a/Projects/Feature/Class/Sources/ClassIntro/Reducer/ClassIntroFeature.swift +++ b/Projects/Feature/Class/Sources/ClassIntro/Reducer/ClassIntroFeature.swift @@ -5,6 +5,7 @@ // Created by Picke on 10/11/26. // +import ClassDomainInterface import ComposableArchitecture import Foundation import PickeSharedUI @@ -16,6 +17,9 @@ public struct ClassIntroFeature { @ObservableState public struct State: Equatable { @Presents public var customAlert: CustomAlertState? + public var ticketNotice: ClassCreateTicketNotice? + public var isCheckingTicket = false + public var toastMessage: String? public init() {} } @@ -23,6 +27,7 @@ public struct ClassIntroFeature { public enum Action: ViewAction { case view(View) case customAlert(PresentationAction) + case inner(InnerAction) case delegate(DelegateAction) } @@ -33,6 +38,12 @@ public struct ClassIntroFeature { case myClassesTapped case createTapped case ticketTapped + case toastShown + } + + @CasePathable + public enum InnerAction: Equatable { + case ticketResponse(Result) } @CasePathable @@ -42,8 +53,15 @@ public struct ClassIntroFeature { case myClasses case create case ticket + case registerTicket } + public enum CancelID: Hashable, Sendable { + case ticketCheck + } + + @Dependency(\.classTicketUseCase) private var ticketUseCase + public var body: some Reducer { Reduce { state, action in switch action { @@ -53,6 +71,9 @@ public struct ClassIntroFeature { case let .customAlert(alertAction): return handleCustomAlertAction(state: &state, action: alertAction) + case let .inner(innerAction): + return handleInnerAction(state: &state, action: innerAction) + case .delegate: return .none } @@ -71,8 +92,12 @@ extension ClassIntroFeature { action: PresentationAction ) -> Effect { switch action { - case .presented(.confirmTapped), .presented(.cancelTapped), .dismiss: - state.customAlert = nil + case .presented(.confirmTapped): + let notice = state.ticketNotice + clearTicketNotice(state: &state) + return notice == .expired ? .send(.delegate(.registerTicket)) : .none + case .presented(.cancelTapped), .dismiss: + clearTicketNotice(state: &state) return .none case .presented: return .none @@ -85,19 +110,81 @@ extension ClassIntroFeature { ) -> Effect { switch action { case .backTapped: - return .send(.delegate(.backToHome)) + return cancelTicketCheck(state: &state, then: .send(.delegate(.backToHome))) case .joinTapped: - return .send(.delegate(.join)) + return cancelTicketCheck(state: &state, then: .send(.delegate(.join))) case .myClassesTapped: - return .send(.delegate(.myClasses)) + return cancelTicketCheck(state: &state, then: .send(.delegate(.myClasses))) case .createTapped: - return .send(.delegate(.create)) + // Android와 같이 만들기 전에 내 이용권 상태를 확인한다. 확인 중 다시 누르면 무시한다. + guard !state.isCheckingTicket else { return .none } + state.isCheckingTicket = true + return .run { [ticketUseCase] send in + let result: Result = await Result { + try await ticketUseCase.fetchMyTicket() + } + .mapError { ($0 as? ClassTicketError) ?? .network($0.localizedDescription) } + await send(.inner(.ticketResponse(result))) + } + .cancellable(id: CancelID.ticketCheck, cancelInFlight: true) case .ticketTapped: - return .send(.delegate(.ticket)) + return cancelTicketCheck(state: &state, then: .send(.delegate(.ticket))) + + case .toastShown: + state.toastMessage = nil + return .none + } + } + + private func handleInnerAction( + state: inout State, + action: InnerAction + ) -> Effect { + switch action { + case let .ticketResponse(result): + guard state.isCheckingTicket else { return .none } + state.isCheckingTicket = false + switch result { + case let .success(ticket): + switch ticket.status { + case .active: + return .send(.delegate(.create)) + case .expired: + presentTicketNotice(.expired, state: &state) + } + case .failure(.notFound): + presentTicketNotice(.required, state: &state) + case .failure: + state.toastMessage = "정보를 불러오지 못했어요" + } + return .none } } + + /// 확인 중 다른 이동을 하면 늦은 응답이 화면을 쌓지 않도록 확인을 취소한다. + private func cancelTicketCheck( + state: inout State, + then effect: Effect + ) -> Effect { + guard state.isCheckingTicket else { return effect } + state.isCheckingTicket = false + return .merge(.cancel(id: CancelID.ticketCheck), effect) + } + + private func presentTicketNotice( + _ notice: ClassCreateTicketNotice, + state: inout State + ) { + state.ticketNotice = notice + state.customAlert = notice.alert + } + + private func clearTicketNotice(state: inout State) { + state.ticketNotice = nil + state.customAlert = nil + } } diff --git a/Projects/Feature/Class/Sources/ClassIntro/View/ClassIntroView.swift b/Projects/Feature/Class/Sources/ClassIntro/View/ClassIntroView.swift index d056b99a..108f68e1 100644 --- a/Projects/Feature/Class/Sources/ClassIntro/View/ClassIntroView.swift +++ b/Projects/Feature/Class/Sources/ClassIntro/View/ClassIntroView.swift @@ -42,6 +42,11 @@ public struct ClassIntroView: View { .background(backgroundImage()) .toolbar(.hidden, for: .navigationBar) .customAlert($store.scope(state: \.customAlert, action: \.customAlert)) + .onChange(of: store.toastMessage, initial: true) { _, message in + guard let message else { return } + ToastManager.shared.showError(message) + send(.toastShown) + } } } diff --git a/Projects/Feature/Class/Sources/ClassTicket/Model/ClassTicketRegisterNotice.swift b/Projects/Feature/Class/Sources/ClassTicket/Model/ClassTicketRegisterNotice.swift new file mode 100644 index 00000000..3dfb3128 --- /dev/null +++ b/Projects/Feature/Class/Sources/ClassTicket/Model/ClassTicketRegisterNotice.swift @@ -0,0 +1,40 @@ +// +// ClassTicketRegisterNotice.swift +// Class +// +// Created by Picke on 11/4/26. +// + +import ClassDomainInterface +import PickeSharedUI + +/// 이용권 등록 화면 안내(Android `TicketRegisterDialog`). +public enum ClassTicketRegisterNotice: Equatable, Sendable { + /// 다른 계정에 등록된 코드. 뒤로가기로 화면을 닫는다. + case alreadyRegistered + case invalidCode + case registrationExpired + case alreadyActive + case registerFailed + + /// Android `toRegisterDialog`와 같은 매핑. 사용 중 이용권(`TICKET_409_ACTIVE`)만 iOS에서 따로 안내한다. + public init(error: ClassTicketError) { + switch error { + case .claimed: self = .alreadyRegistered + case .invalidCode, .revoked: self = .invalidCode + case .registrationExpired: self = .registrationExpired + case .activeTicket: self = .alreadyActive + case .notFound, .invalidResponse, .network: self = .registerFailed + } + } + + public var alert: CustomAlertState { + switch self { + case .alreadyRegistered: return .ticketAlreadyRegistered() + case .invalidCode: return .ticketInvalidCode() + case .registrationExpired: return .ticketRegistrationExpired() + case .alreadyActive: return .ticketAlreadyActive() + case .registerFailed: return .ticketRegisterFailed() + } + } +} diff --git a/Projects/Feature/Class/Sources/ClassTicket/Reducer/ClassTicketFeature.swift b/Projects/Feature/Class/Sources/ClassTicket/Reducer/ClassTicketFeature.swift index 86650ddf..a3b71e73 100644 --- a/Projects/Feature/Class/Sources/ClassTicket/Reducer/ClassTicketFeature.swift +++ b/Projects/Feature/Class/Sources/ClassTicket/Reducer/ClassTicketFeature.swift @@ -7,6 +7,7 @@ import ClassDomainInterface import ComposableArchitecture import Foundation import PickeCoreUtility +import PickeSharedUI @Reducer public struct ClassTicketFeature { @@ -20,19 +21,31 @@ public struct ClassTicketFeature { public var code = "" public var isChecking = false public var isLoadingExistingTicket = false - public var errorMessage: String? public var expandedFAQ: Int? public var preview: ClassTicketPreview? public var ticket: ClassTicket? public var registrationStartDate: Date? public var expirationDate: Date? + @Presents public var customAlert: CustomAlertState? + public var registerNotice: ClassTicketRegisterNotice? + public var toastMessage: String? + public var closesAfterToast = false public init(instanceID: UUID = UUID()) { self.instanceID = instanceID } + /// Android와 같이 대문자로 바꾸고 영문·숫자·`-`만 남긴다(서버도 공백·하이픈·대소문자를 무시). + public var normalizedCode: String { + // 글자 묶음이 아니라 유니코드 단위로 걸러 ASCII만 남긴다(예: "1️⃣", "A̲"의 결합 문자 제거). + let scalars = code.uppercased().unicodeScalars.filter { scalar in + ("A"..."Z").contains(scalar) || ("0"..."9").contains(scalar) || scalar == "-" + } + return String(scalars) + } + public var canCheckCode: Bool { - !code.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !isChecking && !isLoadingExistingTicket + !normalizedCode.isEmpty && !isChecking && !isLoadingExistingTicket } public var navigationTitle: String { @@ -47,8 +60,9 @@ public struct ClassTicketFeature { capacity.map { "클래스 \($0)인 이용권" } ?? "클래스 이용권" } + /// 개요는 사용 중(ACTIVE) 이용권만 보여 준다(만료면 코드 입력). public var overviewTitle: String { - ticket?.status == .expired ? "만료된 이용권" : "사용 중인 이용권" + "사용 중인 이용권" } public var capacityDetail: String { @@ -57,7 +71,7 @@ public struct ClassTicketFeature { public var ticketPeriodText: String { if phase == .preview, let preview { - return "등록 후 \(preview.usageMonths)개월" + return "등록일부터 \(preview.usageMonths)개월" } return periodText } @@ -84,9 +98,13 @@ public struct ClassTicketFeature { public enum Action: ViewAction, BindableAction { case binding(BindingAction) case view(View) + case customAlert(PresentationAction) case checkResponse(Result) case registrationResponse(Result) - case myTicketResponse(Result) + /// 화면 진입 시 내 이용권 조회 결과. + case existingTicketResponse(Result) + /// 내가 등록한 코드를 다시 입력했을 때 내 이용권 조회 결과. + case registeredTicketResponse(Result) case delegate(DelegateAction) } @@ -98,6 +116,7 @@ public struct ClassTicketFeature { case confirmTapped case addRegistrationTapped case faqTapped(Int) + case toastShown } @CasePathable @@ -106,6 +125,8 @@ public struct ClassTicketFeature { case registrationConfirmed(startDate: Date, endDate: Date) case addRegistrationRequested case registered(Date, Date) + /// 내가 등록한 사용 중 이용권을 다시 입력함. 코디네이터가 기존 개요로 돌아가거나 이 화면을 개요로 바꾼다. + case existingTicketFound(ClassTicket) } public enum CancelID: Hashable, Sendable { @@ -120,8 +141,12 @@ public struct ClassTicketFeature { Reduce { state, action in switch action { case .binding(\.code): - state.errorMessage = nil + let wasLookingUpRegisteredTicket = state.preview != nil && state.isLoadingExistingTicket state.preview = nil + if wasLookingUpRegisteredTicket { + state.isLoadingExistingTicket = false + return .cancel(id: CancelID.existingTicket(state.instanceID)) + } guard state.isChecking else { return .none } state.isChecking = false return .cancel(id: CancelID.check(state.instanceID)) @@ -138,24 +163,23 @@ public struct ClassTicketFeature { else { return .none } state.shouldCheckExistingTicket = false state.isLoadingExistingTicket = true - return .run { [ticketUseCase] send in - let result: Result = await Result { - try await ticketUseCase.fetchMyTicket() - } - .mapError(Self.mapError) - await send(.myTicketResponse(result)) - } - .cancellable(id: CancelID.existingTicket(state.instanceID), cancelInFlight: true) + return fetchMyTicket(state: state) { .existingTicketResponse($0) } case .view(.backTapped): + // Android와 같이 확인 단계에서는 코드 입력으로 돌아간다. 등록 요청 중에는 무시한다. + if state.phase == .preview { + guard !state.isChecking else { return .none } + state.phase = .entry + state.preview = nil + return .none + } return .send(.delegate(.dismiss)) case .view(.confirmTapped): if state.phase == .preview { guard state.canRegister else { return .none } state.isChecking = true - state.errorMessage = nil - let submittedCode = state.code.trimmingCharacters(in: .whitespacesAndNewlines) + let submittedCode = state.normalizedCode return .run { [ticketUseCase] send in let result: Result = await Result { try await ticketUseCase.registerTicket(code: submittedCode) @@ -186,8 +210,7 @@ public struct ClassTicketFeature { case .view(.checkTapped): guard state.phase == .entry, state.canCheckCode else { return .none } state.isChecking = true - state.errorMessage = nil - let submittedCode = state.code.trimmingCharacters(in: .whitespacesAndNewlines) + let submittedCode = state.normalizedCode return .run { [ticketUseCase] send in let result: Result = await Result { try await ticketUseCase.previewTicket(code: submittedCode) @@ -197,6 +220,25 @@ public struct ClassTicketFeature { } .cancellable(id: CancelID.check(state.instanceID), cancelInFlight: true) + case .view(.toastShown): + state.toastMessage = nil + // 토스트를 띄운 뒤에 닫아야 사라지는 화면에서도 안내가 보인다. + guard state.closesAfterToast else { return .none } + state.closesAfterToast = false + return .send(.delegate(.dismiss)) + + case .customAlert(.presented(.confirmTapped)): + let notice = state.registerNotice + clearRegisterNotice(state: &state) + return notice == .alreadyRegistered ? .send(.delegate(.dismiss)) : .none + + case .customAlert(.presented(.cancelTapped)), .customAlert(.dismiss): + clearRegisterNotice(state: &state) + return .none + + case .customAlert: + return .none + case let .checkResponse(result): guard state.phase == .entry, state.isChecking else { return .none } state.isChecking = false @@ -204,19 +246,13 @@ public struct ClassTicketFeature { case let .success(preview): state.preview = preview if preview.registeredByMe { + // Android와 같이 내 이용권 상태를 확인해 사용 중이면 이용권 화면, 아니면 만료 안내. state.isLoadingExistingTicket = true - return .run { [ticketUseCase] send in - let result: Result = await Result { - try await ticketUseCase.fetchMyTicket() - } - .mapError(Self.mapError) - await send(.myTicketResponse(result)) - } - .cancellable(id: CancelID.existingTicket(state.instanceID), cancelInFlight: true) + return fetchMyTicket(state: state) { .registeredTicketResponse($0) } } state.phase = .preview case let .failure(error): - state.errorMessage = error.message + presentRegisterNotice(.init(error: error), state: &state) } return .none @@ -231,23 +267,39 @@ public struct ClassTicketFeature { state.phase = .registered return .send(.delegate(.registered(ticket.activatedAt, ticket.expiresAt))) case let .failure(error): - state.errorMessage = error.message + presentRegisterNotice(.init(error: error), state: &state) } return .none - case let .myTicketResponse(result): + case let .existingTicketResponse(result): guard state.phase == .entry, state.isLoadingExistingTicket else { return .none } state.isLoadingExistingTicket = false switch result { - case let .success(ticket): + case let .success(ticket) where ticket.status == .active: state.ticket = ticket state.registrationStartDate = ticket.activatedAt state.expirationDate = ticket.expiresAt state.phase = .overview + case .success, .failure(.notFound): + // 만료됐거나 없으면 Android와 같이 코드 입력부터 시작한다. + break + case .failure: + state.toastMessage = "정보를 불러오지 못했어요" + state.closesAfterToast = true + } + return .none + + case let .registeredTicketResponse(result): + // 코드를 고치면 preview가 비므로 늦게 온 결과는 무시된다. + guard state.phase == .entry, state.isLoadingExistingTicket, state.preview != nil else { return .none } + state.isLoadingExistingTicket = false + switch result { + case let .success(ticket) where ticket.status == .active: + return .send(.delegate(.existingTicketFound(ticket))) + case .success: + presentRegisterNotice(.registrationExpired, state: &state) case let .failure(error): - if error != .notFound { - state.errorMessage = error.message - } + presentRegisterNotice(.init(error: error), state: &state) } return .none @@ -255,6 +307,36 @@ public struct ClassTicketFeature { return .none } } + .ifLet(\.$customAlert, action: \.customAlert) { + CustomConfirmAlert() + } + } + + private func fetchMyTicket( + state: State, + response: @escaping @Sendable (Result) -> Action + ) -> Effect { + .run { [ticketUseCase] send in + let result: Result = await Result { + try await ticketUseCase.fetchMyTicket() + } + .mapError(Self.mapError) + await send(response(result)) + } + .cancellable(id: CancelID.existingTicket(state.instanceID), cancelInFlight: true) + } + + private func presentRegisterNotice( + _ notice: ClassTicketRegisterNotice, + state: inout State + ) { + state.registerNotice = notice + state.customAlert = notice.alert + } + + private func clearRegisterNotice(state: inout State) { + state.registerNotice = nil + state.customAlert = nil } private static func mapError(_ error: Error) -> ClassTicketError { diff --git a/Projects/Feature/Class/Sources/ClassTicket/View/ClassTicketView.swift b/Projects/Feature/Class/Sources/ClassTicket/View/ClassTicketView.swift index c609f058..1bb03fc2 100644 --- a/Projects/Feature/Class/Sources/ClassTicket/View/ClassTicketView.swift +++ b/Projects/Feature/Class/Sources/ClassTicket/View/ClassTicketView.swift @@ -6,11 +6,13 @@ import ComposableArchitecture import PickeCoreUtility import PickeDesignKit +import PickeSharedUI import SwiftUI @ViewAction(for: ClassTicketFeature.self) public struct ClassTicketView: View { @Bindable public var store: StoreOf + @FocusState private var isCodeFocused: Bool public init(store: StoreOf) { self.store = store @@ -46,6 +48,16 @@ public struct ClassTicketView: View { .screenBackground(.surfaceBeigeDefault) .toolbar(.hidden, for: .navigationBar) .onAppear { send(.onAppear) } + .customAlert($store.scope(state: \.customAlert, action: \.customAlert)) + .onChange(of: store.customAlert != nil) { _, isPresented in + // 안내가 키보드에 가리지 않게 입력 포커스를 내린다. + if isPresented { isCodeFocused = false } + } + .onChange(of: store.toastMessage, initial: true) { _, message in + guard let message else { return } + ToastManager.shared.showError(message) + send(.toastShown) + } } } @@ -95,14 +107,8 @@ private extension ClassTicketView { .autocorrectionDisabled() .keyboardType(.asciiCapable) .pickeTextField(height: 44) + .focused($isCodeFocused) .accessibilityIdentifier("class-ticket-code") - - if let errorMessage = store.errorMessage { - Text(errorMessage) - .pretendardFont(.bodySmall) - .foregroundStyle(.errorDefault) - .accessibilityIdentifier("class-ticket-error") - } } } @@ -112,12 +118,12 @@ private extension ClassTicketView { faqRow( index: 0, question: "이용권 코드는 어디서 확인하나요?", - answer: "리워드로 받은 이메일에서 이용권 코드를 확인할 수 있어요." + answer: "펀딩 리워드 발송 안내에서 확인해 주세요. 코드를 받지 못했다면 해당 펀딩의 문의 경로를 이용해 주세요." ) faqRow( index: 1, question: "클래스 참여 코드와 다른가요?", - answer: "이용권 코드는 클래스를 개설할 때 사용해요. 참여 코드는 다른 사람의 클래스에 들어갈 때 사용해요." + answer: "이용권 코드는 모임을 여는 데 사용하는 리워드예요. 모임에 참가하려면 '클래스 탭' - '클래스 참여하기'에서 참여 코드를 입력해 주세요." ) } } @@ -161,28 +167,31 @@ private extension ClassTicketView { @ViewBuilder func previewContent() -> some View { VStack(alignment: .leading, spacing: 0) { - Text("이용권 내용을 확인해 주세요") + Text("이 이용권을 등록할까요?") .pretendardFont(.semiBold24) .foregroundStyle(.gray800) .padding(.bottom, 8) - Text("확인 후 등록하면 이용 기간이 바로 시작돼요") + Text("이용 조건과 주의사항을 확인해 주세요") .pretendardFont(.bodyMedium) .foregroundStyle(.gray300) .padding(.bottom, 36) ticketCard() - if let errorMessage = store.errorMessage { - Text(errorMessage) - .pretendardFont(.bodySmall) - .foregroundStyle(.errorDefault) - .padding(.top, 12) - .accessibilityIdentifier("class-ticket-error") - } + Text("등록하는 순간 이용 기간이 시작돼요\n클래스를 만들기 전에도 기간은 흘러가요") + .pretendardFont(.bodySmall) + .foregroundStyle(.gray300) + .padding(.top, 12) - bottomSection() + Text("등록 후 마이페이지에서 이용권을 확인할 수 있어요") + .pretendardFont(.bodySmall) + .foregroundStyle(.gray300) + .frame(maxWidth: .infinity) .padding(.top, 40) + + bottomSection() + .padding(.top, 10) } .padding(.horizontal, 16) .padding(.top, 20) @@ -241,6 +250,9 @@ private extension ClassTicketView { @ViewBuilder func overviewBottomSection() -> some View { VStack(spacing: 10) { + Text("등록 후 마이페이지에서 이용권을 확인할 수 있어요") + .pretendardFont(.bodySmall) + .foregroundStyle(.gray300) Button("이용권 추가등록하기") { send(.addRegistrationTapped) } diff --git a/Projects/Feature/Class/Tests/Sources/ClassTests.swift b/Projects/Feature/Class/Tests/Sources/ClassTests.swift index a5ffd5eb..da6bc93d 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassTests.swift @@ -8,6 +8,7 @@ @testable import Class import ClassDomainInterface import ComposableArchitecture +import Foundation import Testing @MainActor @@ -43,15 +44,113 @@ struct ClassTests { } @Test - func 새_클래스_만들기_탭하면_생성_델리게이트를_보낸다() async { + func 새_클래스_만들기는_사용_중인_이용권이면_생성_델리게이트를_보낸다() async { let store = TestStore(initialState: ClassIntroFeature.State()) { ClassIntroFeature() + } withDependencies: { + $0.classTicketUseCase = TicketStub(myTicket: .success(introTicket(.active))) } - await store.send(.view(.createTapped)) + await store.send(.view(.createTapped)) { $0.isCheckingTicket = true } + await store.receive(\.inner.ticketResponse) { $0.isCheckingTicket = false } await store.receive(\.delegate.create) } + @Test + func 새_클래스_만들기는_이용권이_만료되면_등록_안내를_띄우고_등록하기로_이동한다() async { + let store = TestStore(initialState: ClassIntroFeature.State()) { + ClassIntroFeature() + } withDependencies: { + $0.classTicketUseCase = TicketStub(myTicket: .success(introTicket(.expired))) + } + + await store.send(.view(.createTapped)) { $0.isCheckingTicket = true } + await store.receive(\.inner.ticketResponse) { + $0.isCheckingTicket = false + $0.ticketNotice = .expired + $0.customAlert = .classTicketExpired() + } + await store.send(.customAlert(.presented(.confirmTapped))) { + $0.ticketNotice = nil + $0.customAlert = nil + } + await store.receive(\.delegate.registerTicket) + } + + @Test + func 새_클래스_만들기는_만료_안내에서_뒤로가기를_누르면_머문다() async { + let store = TestStore(initialState: ClassIntroFeature.State()) { + ClassIntroFeature() + } withDependencies: { + $0.classTicketUseCase = TicketStub(myTicket: .success(introTicket(.expired))) + } + + await store.send(.view(.createTapped)) { $0.isCheckingTicket = true } + await store.receive(\.inner.ticketResponse) { + $0.isCheckingTicket = false + $0.ticketNotice = .expired + $0.customAlert = .classTicketExpired() + } + await store.send(.customAlert(.presented(.cancelTapped))) { + $0.ticketNotice = nil + $0.customAlert = nil + } + } + + @Test + func 새_클래스_만들기는_이용권이_없으면_안내만_띄운다() async { + let store = TestStore(initialState: ClassIntroFeature.State()) { + ClassIntroFeature() + } withDependencies: { + $0.classTicketUseCase = TicketStub(myTicket: .failure(.notFound)) + } + + await store.send(.view(.createTapped)) { $0.isCheckingTicket = true } + await store.receive(\.inner.ticketResponse) { + $0.isCheckingTicket = false + $0.ticketNotice = .required + $0.customAlert = .classTicketRequired() + } + await store.send(.customAlert(.presented(.confirmTapped))) { + $0.ticketNotice = nil + $0.customAlert = nil + } + } + + @Test + func 새_클래스_만들기에서_이용권_조회가_실패하면_토스트를_띄운다() async { + let store = TestStore(initialState: ClassIntroFeature.State()) { + ClassIntroFeature() + } withDependencies: { + $0.classTicketUseCase = TicketStub(myTicket: .failure(.network("연결 실패"))) + } + + await store.send(.view(.createTapped)) { $0.isCheckingTicket = true } + await store.receive(\.inner.ticketResponse) { + $0.isCheckingTicket = false + $0.toastMessage = "정보를 불러오지 못했어요" + } + await store.send(.view(.toastShown)) { $0.toastMessage = nil } + } + + @Test + func 이용권_확인_중_다시_누르면_무시하고_뒤로_가면_확인을_취소한다() async { + let store = TestStore(initialState: ClassIntroFeature.State()) { + ClassIntroFeature() + } withDependencies: { + $0.classTicketUseCase = TicketStub(myTicketFetch: { + try await Task.sleep(for: .seconds(60)) + throw ClassTicketError.notFound + }) + } + + await store.send(.view(.createTapped)) { $0.isCheckingTicket = true } + await store.send(.view(.createTapped)) + await store.send(.view(.backTapped)) { $0.isCheckingTicket = false } + await store.receive(\.delegate.backToHome) + await store.finish(timeout: .seconds(1)) + } + @Test func 이용권_탭하면_등록_화면으로_이동한다() async { let store = TestStore(initialState: ClassIntroFeature.State()) { @@ -118,3 +217,15 @@ struct ClassTests { )) } } + +private func introTicket(_ status: ClassTicket.Status) -> ClassTicket { + ClassTicket( + id: 3, + capacity: 15, + status: status, + activatedAt: Date(timeIntervalSince1970: 1_780_000_000), + expiresAt: Date(timeIntervalSince1970: 1_795_000_000), + remainingDays: 0, + readGraceEndsAt: Date(timeIntervalSince1970: 1_797_592_000) + ) +} diff --git a/Projects/Feature/Class/Tests/Sources/ClassTicketTests.swift b/Projects/Feature/Class/Tests/Sources/ClassTicketTests.swift index 77cbc8d1..98fa35ba 100644 --- a/Projects/Feature/Class/Tests/Sources/ClassTicketTests.swift +++ b/Projects/Feature/Class/Tests/Sources/ClassTicketTests.swift @@ -28,7 +28,7 @@ struct ClassTicketTests { $0.shouldCheckExistingTicket = false $0.isLoadingExistingTicket = true } - await store.receive(\.myTicketResponse) { + await store.receive(\.existingTicketResponse) { $0.isLoadingExistingTicket = false $0.ticket = ticket $0.registrationStartDate = start @@ -63,7 +63,7 @@ struct ClassTicketTests { } #expect(!store.state.canCheckCode) await gate.complete(ticket) - await store.receive(\.myTicketResponse) { + await store.receive(\.existingTicketResponse) { $0.isLoadingExistingTicket = false $0.ticket = ticket $0.registrationStartDate = ticket.activatedAt @@ -82,14 +82,14 @@ struct ClassTicketTests { $0.shouldCheckExistingTicket = false $0.isLoadingExistingTicket = true } - await store.receive(\.myTicketResponse) { $0.isLoadingExistingTicket = false } + await store.receive(\.existingTicketResponse) { $0.isLoadingExistingTicket = false } #expect(store.state.phase == .entry) - #expect(store.state.errorMessage == nil) + #expect(store.state.customAlert == nil) await store.send(.view(.onAppear)) } @Test - func 만료된_이용권은_사용_중으로_표시하지_않는다() async { + func 만료된_이용권은_개요_대신_코드_입력부터_시작한다() async { let ticket = ClassTicket( id: 3, capacity: 15, @@ -107,14 +107,31 @@ struct ClassTicketTests { $0.shouldCheckExistingTicket = false $0.isLoadingExistingTicket = true } - await store.receive(\.myTicketResponse) { + await store.receive(\.existingTicketResponse) { $0.isLoadingExistingTicket = false } + #expect(store.state.phase == .entry) + #expect(store.state.ticket == nil) + } + + @Test + func 진입_조회가_실패하면_토스트를_띄운_뒤_화면을_닫는다() async { + let store = TestStore(initialState: ClassTicketFeature.State()) { ClassTicketFeature() } withDependencies: { + $0.classTicketUseCase = TicketStub(myTicket: .failure(.network("연결 실패"))) + } + + await store.send(.view(.onAppear)) { + $0.shouldCheckExistingTicket = false + $0.isLoadingExistingTicket = true + } + await store.receive(\.existingTicketResponse) { $0.isLoadingExistingTicket = false - $0.ticket = ticket - $0.registrationStartDate = ticket.activatedAt - $0.expirationDate = ticket.expiresAt - $0.phase = .overview + $0.toastMessage = "정보를 불러오지 못했어요" + $0.closesAfterToast = true + } + await store.send(.view(.toastShown)) { + $0.toastMessage = nil + $0.closesAfterToast = false } - #expect(store.state.overviewTitle == "만료된 이용권") + await store.receive(\.delegate.dismiss) } @Test @@ -207,15 +224,77 @@ struct ClassTicketTests { $0.isLoadingExistingTicket = true $0.preview = preview } - await store.receive(\.myTicketResponse) { + await store.receive(\.registeredTicketResponse) { $0.isLoadingExistingTicket = false } + await store.receive(\.delegate, .existingTicketFound(ticket)) + } + + @Test + func 내가_등록한_만료_이용권_코드는_만료_안내를_띄운다() async { + let preview = ClassTicketPreview( + capacity: 30, + registerBy: Date(timeIntervalSince1970: 1_800_000_000), + usageMonths: 6, + registeredByMe: true + ) + let ticket = ClassTicket( + id: 3, + capacity: 30, + status: .expired, + activatedAt: Date(timeIntervalSince1970: 1_780_000_000), + expiresAt: Date(timeIntervalSince1970: 1_795_000_000), + remainingDays: 0, + readGraceEndsAt: Date(timeIntervalSince1970: 1_797_592_000) + ) + var state = ClassTicketFeature.State() + state.code = "PICKE-AB12-CD34" + let store = TestStore(initialState: state) { ClassTicketFeature() } withDependencies: { + $0.classTicketUseCase = TicketStub(preview: .success(preview), myTicket: .success(ticket)) + } + + await store.send(.view(.checkTapped)) { $0.isChecking = true } + await store.receive(\.checkResponse) { + $0.isChecking = false + $0.isLoadingExistingTicket = true + $0.preview = preview + } + await store.receive(\.registeredTicketResponse) { $0.isLoadingExistingTicket = false - $0.ticket = ticket - $0.phase = .overview - $0.registrationStartDate = start - $0.expirationDate = end + $0.registerNotice = .registrationExpired + $0.customAlert = .ticketRegistrationExpired() } } + @Test + func 내_코드_재확인_중_코드를_고치면_조회를_취소한다() async { + let preview = ClassTicketPreview( + capacity: 30, + registerBy: Date(timeIntervalSince1970: 1_800_000_000), + usageMonths: 6, + registeredByMe: true + ) + var state = ClassTicketFeature.State() + state.code = "PICKE-AB12-CD34" + let store = TestStore(initialState: state) { ClassTicketFeature() } withDependencies: { + $0.classTicketUseCase = TicketStub(preview: .success(preview), myTicketFetch: { + try await Task.sleep(for: .seconds(60)) + throw ClassTicketError.notFound + }) + } + + await store.send(.view(.checkTapped)) { $0.isChecking = true } + await store.receive(\.checkResponse) { + $0.isChecking = false + $0.isLoadingExistingTicket = true + $0.preview = preview + } + await store.send(.binding(.set(\.code, "PICKE-ZZ99"))) { + $0.code = "PICKE-ZZ99" + $0.preview = nil + $0.isLoadingExistingTicket = false + } + await store.finish(timeout: .seconds(1)) + } + @Test func 서버_거절_사유를_보여주고_등록을_막는다() async { var state = ClassTicketFeature.State() @@ -227,24 +306,206 @@ struct ClassTicketTests { await store.send(.view(.checkTapped)) { $0.isChecking = true } await store.receive(\.checkResponse) { $0.isChecking = false - $0.errorMessage = ClassTicketError.registrationExpired.message + $0.registerNotice = .registrationExpired + $0.customAlert = .ticketRegistrationExpired() } #expect(store.state.phase == .entry) } + + @Test + func 다른_계정에_등록된_코드_안내는_배경_탭이면_머물고_뒤로가기면_닫는다() async { + var state = ClassTicketFeature.State() + state.code = "CLAIMED" + let store = TestStore(initialState: state) { ClassTicketFeature() } withDependencies: { + $0.classTicketUseCase = TicketStub(preview: .failure(.claimed)) + } + + await store.send(.view(.checkTapped)) { $0.isChecking = true } + await store.receive(\.checkResponse) { + $0.isChecking = false + $0.registerNotice = .alreadyRegistered + $0.customAlert = .ticketAlreadyRegistered() + } + await store.send(.customAlert(.presented(.cancelTapped))) { + $0.registerNotice = nil + $0.customAlert = nil + } + + await store.send(.view(.checkTapped)) { $0.isChecking = true } + await store.receive(\.checkResponse) { + $0.isChecking = false + $0.registerNotice = .alreadyRegistered + $0.customAlert = .ticketAlreadyRegistered() + } + await store.send(.customAlert(.presented(.confirmTapped))) { + $0.registerNotice = nil + $0.customAlert = nil + } + await store.receive(\.delegate.dismiss) + } + + @Test + func 등록_오류는_Android와_같은_안내로_바꾸고_사용_중_이용권만_따로_안내한다() { + #expect(ClassTicketRegisterNotice(error: .claimed) == .alreadyRegistered) + #expect(ClassTicketRegisterNotice(error: .invalidCode) == .invalidCode) + #expect(ClassTicketRegisterNotice(error: .revoked) == .invalidCode) + #expect(ClassTicketRegisterNotice(error: .registrationExpired) == .registrationExpired) + #expect(ClassTicketRegisterNotice(error: .activeTicket) == .alreadyActive) + #expect(ClassTicketRegisterNotice(error: .notFound) == .registerFailed) + #expect(ClassTicketRegisterNotice(error: .invalidResponse) == .registerFailed) + #expect(ClassTicketRegisterNotice(error: .network("x")) == .registerFailed) + } + + @Test + func 확인_단계에서_뒤로가면_코드_입력으로_돌아가고_등록_중에는_무시한다() async { + var state = ClassTicketFeature.State() + state.code = "PICKE-AB12-CD34" + state.phase = .preview + state.preview = ClassTicketPreview( + capacity: 15, + registerBy: Date(timeIntervalSince1970: 1_800_000_000), + usageMonths: 6, + registeredByMe: false + ) + state.isChecking = true + let store = TestStore(initialState: state) { ClassTicketFeature() } + + await store.send(.view(.backTapped)) + await store.send(.binding(.set(\.isChecking, false))) { $0.isChecking = false } + await store.send(.view(.backTapped)) { + $0.phase = .entry + $0.preview = nil + } + } + + @Test + func 코드는_영문_숫자_하이픈만_대문자로_남긴다() { + var state = ClassTicketFeature.State() + state.code = " picke-ab12 cd34!한" + #expect(state.normalizedCode == "PICKE-AB12CD34") + #expect(state.canCheckCode) + state.code = " !한 " + #expect(!state.canCheckCode) + #expect(state.ticketPeriodText == "") + state.code = "1\u{FE0F}\u{20E3}A\u{0332}" + #expect(state.normalizedCode == "1A") + } + + @Test + func 코드_확인과_등록은_정규화한_코드를_보낸다() async { + let codes = LockIsolated<[String]>([]) + let preview = ClassTicketPreview( + capacity: 15, + registerBy: Date(timeIntervalSince1970: 1_800_000_000), + usageMonths: 6, + registeredByMe: false + ) + let ticket = ClassTicket( + id: 3, + capacity: 15, + status: .active, + activatedAt: Date(timeIntervalSince1970: 1_780_000_000), + expiresAt: Date(timeIntervalSince1970: 1_795_000_000), + remainingDays: 162, + readGraceEndsAt: Date(timeIntervalSince1970: 1_797_592_000) + ) + var state = ClassTicketFeature.State() + state.code = " picke-ab12 cd34" + let store = TestStore(initialState: state) { ClassTicketFeature() } withDependencies: { + $0.classTicketUseCase = TicketStub( + preview: .success(preview), + register: .success(ticket), + submittedCodes: codes + ) + } + store.exhaustivity = .off + + await store.send(.view(.checkTapped)) + await store.receive(\.checkResponse) + await store.send(.view(.confirmTapped)) + await store.receive(\.registrationResponse) + + #expect(codes.value == ["PICKE-AB12CD34", "PICKE-AB12CD34"]) + } + + @Test + func 내_코드_재확인_조회가_실패하면_등록_실패_안내를_띄운다() async { + let preview = ClassTicketPreview( + capacity: 30, + registerBy: Date(timeIntervalSince1970: 1_800_000_000), + usageMonths: 6, + registeredByMe: true + ) + var state = ClassTicketFeature.State() + state.code = "PICKE-AB12-CD34" + let store = TestStore(initialState: state) { ClassTicketFeature() } withDependencies: { + $0.classTicketUseCase = TicketStub( + preview: .success(preview), + myTicket: .failure(.network("연결 실패")) + ) + } + + await store.send(.view(.checkTapped)) { $0.isChecking = true } + await store.receive(\.checkResponse) { + $0.isChecking = false + $0.isLoadingExistingTicket = true + $0.preview = preview + } + await store.receive(\.registeredTicketResponse) { + $0.isLoadingExistingTicket = false + $0.registerNotice = .registerFailed + $0.customAlert = .ticketRegisterFailed() + } + } + + @Test + func 등록이_사용_중_이용권으로_거절되면_사용_중_안내를_띄운다() async { + let preview = ClassTicketPreview( + capacity: 15, + registerBy: Date(timeIntervalSince1970: 1_800_000_000), + usageMonths: 6, + registeredByMe: false + ) + var state = ClassTicketFeature.State() + state.code = "PICKE-AB12-CD34" + let store = TestStore(initialState: state) { ClassTicketFeature() } withDependencies: { + $0.classTicketUseCase = TicketStub( + preview: .success(preview), + register: .failure(.activeTicket) + ) + } + + await store.send(.view(.checkTapped)) { $0.isChecking = true } + await store.receive(\.checkResponse) { + $0.isChecking = false + $0.preview = preview + $0.phase = .preview + } + await store.send(.view(.confirmTapped)) { $0.isChecking = true } + await store.receive(\.registrationResponse) { + $0.isChecking = false + $0.registerNotice = .alreadyActive + $0.customAlert = .ticketAlreadyActive() + } + #expect(store.state.phase == .preview) + } } -private struct TicketStub: ClassTicketInterface { +struct TicketStub: ClassTicketInterface { var preview: Result = .failure(.invalidCode) var register: Result = .failure(.invalidCode) var myTicket: Result = .failure(.notFound) var myTicketFetch: (@Sendable () async throws -> ClassTicket)? + var submittedCodes: LockIsolated<[String]>? - func previewTicket(code _: String) async throws -> ClassTicketPreview { - try preview.get() + func previewTicket(code: String) async throws -> ClassTicketPreview { + submittedCodes?.withValue { $0.append(code) } + return try preview.get() } - func registerTicket(code _: String) async throws -> ClassTicket { - try register.get() + func registerTicket(code: String) async throws -> ClassTicket { + submittedCodes?.withValue { $0.append(code) } + return try register.get() } func fetchMyTicket() async throws -> ClassTicket {