diff --git a/Projects/CodestackApp/Project.swift b/Projects/CodestackApp/Project.swift index adda1305..60747a43 100644 --- a/Projects/CodestackApp/Project.swift +++ b/Projects/CodestackApp/Project.swift @@ -27,7 +27,7 @@ let resources: ResourceFileElements "Resources/PLanguage/swift/*.txt", "Resources/PLanguage/typescript/*.txt", "Resources/Base.lproj/LaunchScreen.storyboard", - "Sources/Data/Repository/MO/db_model_v1.xcdatamodeld" + "Sources/Data/MO/db_model_v1.xcdatamodeld" ] diff --git a/Projects/CodestackApp/Sources/Common/View/RichTextView.swift b/Projects/CodestackApp/Sources/Common/View/RichTextView.swift index 86423aca..a22923f5 100644 --- a/Projects/CodestackApp/Sources/Common/View/RichTextView.swift +++ b/Projects/CodestackApp/Sources/Common/View/RichTextView.swift @@ -39,7 +39,7 @@ class RichTextViewController: UIViewController{ } - static func create(with html: [Problem]) -> RichTextViewController{ + static func create(with html: [ProblemVO]) -> RichTextViewController { let vc = RichTextViewController() let problems = html diff --git a/Projects/CodestackApp/Sources/DI/DataAssembly.swift b/Projects/CodestackApp/Sources/DI/DataAssembly.swift index 9d932451..e16e8dc4 100644 --- a/Projects/CodestackApp/Sources/DI/DataAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/DataAssembly.swift @@ -12,10 +12,13 @@ import Data public struct DataAssembly: Assembly { public func assemble(container: Container) { - container.register(WebRepository.self) { resolver in - let tokenService = resolver.resolve(TokenAcquisitionService.self)! - return DefaultRepository(dependency: tokenService) + let graphAPI = resolver.resolve(GraphQLAPI.self)! + let restAPI = resolver.resolve(RestAPI.self)! + let token = resolver.resolve(TokenAcquisitionService.self)! + return DefaultRepository(dependency: .init(tokenAcquizition: token, + graphAPI: graphAPI, + restAPI: restAPI)) }.inObjectScope(.container) container.register(DBRepository.self) { resolver in diff --git a/Projects/CodestackApp/Sources/DI/NetworkAssembly.swift b/Projects/CodestackApp/Sources/DI/NetworkAssembly.swift index c7f5988d..9ac6bad9 100644 --- a/Projects/CodestackApp/Sources/DI/NetworkAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/NetworkAssembly.swift @@ -29,6 +29,13 @@ public struct NetworkAssembly: Assembly { extractToken: API.extractAccessToken) }.inObjectScope(.container) + + container.register(GraphQLAPI.self) { resolver in + let graphAPIDependency = DefaultGraphQLAPI.Dependency.init(baseURL: DefaultRepository.baseURL, + configuration: .default) + return DefaultGraphQLAPI(dependency: graphAPIDependency) + } + container.register(AppleLoginManager.self) { resolver in let service = resolver.resolve(Auth.self)! return AppleLoginManager(serviceManager: service) diff --git a/Projects/CodestackApp/Sources/Data/Interface/DBRepository.swift b/Projects/CodestackApp/Sources/Data/Interface/DBRepository.swift index 2a2b359c..a0605b89 100644 --- a/Projects/CodestackApp/Sources/Data/Interface/DBRepository.swift +++ b/Projects/CodestackApp/Sources/Data/Interface/DBRepository.swift @@ -10,11 +10,11 @@ import Foundation import RxSwift protocol DBRepository: AnyObject { - func fetch(_ requestType: SubmissionMO.RequestType) -> Single<[Submission]> - func store(submission: Submission) -> Single + func fetch(_ requestType: SubmissionMO.RequestType) -> Single<[SubmissionVO]> + func store(submission: SubmissionVO) -> Single func remove() -> Single func remove(_ requestType: SubmissionMO.RequestType) -> Completable - func update(submission: Submission, type request: SubmissionMO.RequestType) -> Single + func update(submission: SubmissionVO, type request: SubmissionMO.RequestType) -> Single func fetchProblemState() -> Single<[ProblemSubmissionState]> func fetchSubmissionCalendar() -> Single<[SubmissionCalendar]> diff --git a/Projects/CodestackApp/Sources/Data/Interface/WebRepository.swift b/Projects/CodestackApp/Sources/Data/Interface/WebRepository.swift index 4a40d706..e3c4f175 100644 --- a/Projects/CodestackApp/Sources/Data/Interface/WebRepository.swift +++ b/Projects/CodestackApp/Sources/Data/Interface/WebRepository.swift @@ -11,7 +11,7 @@ import CodestackAPI import Apollo import Data -protocol WebRepository: AnyObject { +public protocol WebRepository: AnyObject { // MARK: GraphQL // func getSolvedProblems(_ graph: GRAPH) -> Maybe<[ProblemIdentityFR]> @@ -37,7 +37,7 @@ protocol WebRepository: AnyObject { } -extension WebRepository { +public extension WebRepository { private static var endpoint: String { return Bundle.main.infoDictionary!["graphql_endpoint"] as! String diff --git a/Projects/CodestackApp/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift b/Projects/CodestackApp/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift index 17072c88..fc58079b 100644 --- a/Projects/CodestackApp/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift +++ b/Projects/CodestackApp/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift @@ -46,9 +46,9 @@ extension SubmissionCalendarMO : Identifiable { let _submissions = submissionSet .toArray(of: SubmissionMO.self) - .compactMap { Submission(managedContext: $0) } + .compactMap { value in value.toDomain() } - return SubmissionCalendarVO(dates: _submissions.compactMap(\.createdAt)) + return SubmissionCalendarVO(dates: _submissions.map(\.createdAt)) } } diff --git a/Projects/CodestackApp/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift b/Projects/CodestackApp/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift index e9df714e..aba70911 100644 --- a/Projects/CodestackApp/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift +++ b/Projects/CodestackApp/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift @@ -52,6 +52,17 @@ extension SubmissionMO : Identifiable { } extension SubmissionVO { + func updateMO(mo: SubmissionMO) -> SubmissionMO { + mo.codeContext?.code = self.sourceCode + mo.codeContext?.problemID = self.problem.id + mo.codeContext?.problemTitle = self.problem.title + mo.language?.languageID = self.language.id + mo.language?.name = self.language.name + mo.language?.extension = self.language.extension + mo.statusCode = self.statusCode + mo.createdAt = self.createdAt.toDateKST() + return mo + } func toMO(in context: NSManagedObjectContext) -> SubmissionMO? { guard let submissionMO = SubmissionMO.insertNew(in: context), diff --git a/Projects/CodestackApp/Sources/Data/Persistence/FavoriteProblemMO-extension.swift b/Projects/CodestackApp/Sources/Data/Persistence/FavoriteProblemMO-extension.swift index dbc2590c..4d3d9e30 100644 --- a/Projects/CodestackApp/Sources/Data/Persistence/FavoriteProblemMO-extension.swift +++ b/Projects/CodestackApp/Sources/Data/Persistence/FavoriteProblemMO-extension.swift @@ -24,7 +24,7 @@ extension FavoritProblemMO { } } - static func isEqualID(probelmID: ID) -> NSFetchRequest { + static func isEqualID(probelmID: ProblemID) -> NSFetchRequest { let request = newFetchRequest() let predicate1 = NSPredicate(format: "problemID == %@", "\(probelmID)") request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [ predicate1 ]) diff --git a/Projects/CodestackApp/Sources/Data/Persistence/Models-extension.swift b/Projects/CodestackApp/Sources/Data/Persistence/Models-extension.swift index 1bcaf51b..21670e09 100644 --- a/Projects/CodestackApp/Sources/Data/Persistence/Models-extension.swift +++ b/Projects/CodestackApp/Sources/Data/Persistence/Models-extension.swift @@ -26,57 +26,6 @@ protocol MapperbleProtocol { static func fetchRequest() -> NSFetchRequest } -extension CodeContext: MapperbleProtocol { - typealias PersistenceType = CodeContextMO - - static func fetchRequest() -> NSFetchRequest { - CodeContextMO.fetchRequest() - } - - @discardableResult - func store(in context: NSManagedObjectContext) -> CodeContextMO? { - let codeContextMO = CodeContextMO.insertNew(in: context) - codeContextMO?.code = self.code - codeContextMO?.problemID = self.problemID - codeContextMO?.problemTitle = self.problemTitle - return codeContextMO - } - - init?(managedContext: CodeContextMO) { - self.init(code: managedContext.code, - problemID: managedContext.problemID, - problemTitle: managedContext.problemTitle) - } -} - -extension Language: MapperbleProtocol { - typealias PersistenceType = LanguageMO - - static func fetchRequest() -> NSFetchRequest { - LanguageMO.fetchRequest() - } - - @discardableResult - func store(in context: NSManagedObjectContext) -> LanguageMO? { - let languageMO = LanguageMO.insertNew(in: context) - languageMO?.languageID = self.id - languageMO?.extension = self._extension - languageMO?.name = self.name - return languageMO - } - - init?(managedContext: LanguageMO) { - guard - let id = managedContext.languageID, - let name = managedContext.name, - let `extension` = managedContext.extension - else { - return nil - } - self.init(id: id, name: name, _extension: `extension`) - } -} - extension FavoriteProblem: MapperbleProtocol { func store(in context: NSManagedObjectContext) -> FavoritProblemMO? { @@ -106,7 +55,7 @@ extension FavoriteProblem: MapperbleProtocol { } struct ProblemSubmissionState { - var submissions: [Submission] + var submissions: [SubmissionVO] } extension SubmissionCalendar: MapperbleProtocol { @@ -119,9 +68,9 @@ extension SubmissionCalendar: MapperbleProtocol { let _submissions = submissionSet .toArray(of: SubmissionMO.self) - .compactMap { Submission(managedContext: $0) } + .compactMap { $0.toDomain() } - self.dates = _submissions.compactMap(\.createdAt) + self.dates = _submissions.map(\.createdAt) } func store(in context: NSManagedObjectContext) -> SubmissionCalendarMO? { @@ -149,7 +98,8 @@ extension ProblemSubmissionState: MapperbleProtocol { let _submissions = submissionSet .toArray(of: SubmissionMO.self) - .compactMap { Submission(managedContext: $0) } + .compactMap { $0.toDomain() } + //.compactMap { Submission(managedContext: $0) } self.submissions = _submissions } @@ -157,69 +107,8 @@ extension ProblemSubmissionState: MapperbleProtocol { func store(in context: NSManagedObjectContext) -> ProblemSubmissionStateMO? { guard let problemSubmissionMO = ProblemSubmissionStateMO.insertNew(in: context) else { return nil } - problemSubmissionMO.submissions = self.submissions.compactMap { $0.store(in: context) } as? NSSet + problemSubmissionMO.submissions = self.submissions.compactMap { $0.toMO(in:) } as? NSSet return problemSubmissionMO } } - -extension Submission: MapperbleProtocol { - typealias PersistenceType = SubmissionMO - - static func fetchRequest() -> NSFetchRequest { - SubmissionMO.fetchRequest() - } - - func store(in context: NSManagedObjectContext) -> SubmissionMO? { - guard - let submissionMO = SubmissionMO.insertNew(in: context), - let language = self.language, - let languageMO = language.store(in: context) - else { - return nil - } - - let codeContext = CodeContext(code: self.sourceCode, - problemID: self.problem?.id, - problemTitle: self.problem?.title) - guard let codeContextMO = codeContext.store(in: context) else { - return nil } - - submissionMO.createdAt = self.createdAt?.toDateKST() - submissionMO.id = self.id - submissionMO.statusCode = self.statusCode - submissionMO.codeContext = codeContextMO - submissionMO.language = languageMO - - languageMO.submission = submissionMO - codeContextMO.submission = submissionMO - - return submissionMO - } - - init?(managedContext: SubmissionMO) { - guard - let id = managedContext.id, - let statusCode = managedContext.statusCode, - let createdAt = managedContext.createdAt, - let codeContextMO = managedContext.codeContext, - let codeContext = CodeContext(managedContext: codeContextMO), - let problemID = codeContext.problemID, - let problemTitle = codeContext.problemTitle, - let languageMO = managedContext.language, - let language = Language(managedContext: languageMO) - else { - return nil - } - - self.init(id: id, - sourceCode: codeContext.code, - problem: Problem(id: problemID, title: problemTitle), - member: nil, - language: language, - cpuTime: nil, - memoryUsage: nil, - statusCode: statusCode, - createdAt: createdAt.toString()) - } -} diff --git a/Projects/CodestackApp/Sources/Data/Persistence/SubmissionMO-extension.swift b/Projects/CodestackApp/Sources/Data/Persistence/SubmissionMO-extension.swift index 4a542895..ab6bbd1d 100644 --- a/Projects/CodestackApp/Sources/Data/Persistence/SubmissionMO-extension.swift +++ b/Projects/CodestackApp/Sources/Data/Persistence/SubmissionMO-extension.swift @@ -15,8 +15,8 @@ extension SubmissionMO { typealias LanguageName = String enum RequestType { case isExist(ProblemID) - case isNotTemp(ProblemID, String) - case isEqualStatusCode(String) + case isNotTemp(ProblemID, StatusCode) + case isEqualStatusCode(StatusCode) case update(SubmissionID, ProblemID) case recent(ProblemID) case `default` @@ -53,24 +53,24 @@ extension SubmissionMO { } } - static func recent(probelm id: ID) -> NSFetchRequest { + static func recent(probelm id: ProblemID) -> NSFetchRequest { let request = newFetchRequest() - let predicate1 = NSPredicate(format: "codeContext.problemID == %@", "\(id)") + let predicate1 = NSPredicate(format: "codeContext.problemID == %@", "\(String(describing: id))") request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1]) request.fetchLimit = 1 request.sortDescriptors = [ NSSortDescriptor(key: "createdAt", ascending: false)] return request } - static func isEqualID(id: ID, _ probelmID: ID) -> NSFetchRequest { + static func isEqualID(id: SubmissionID, _ probelmID: ProblemID) -> NSFetchRequest { let request = newFetchRequest() - let predicate1 = NSPredicate(format: "id == %@", "\(id)") - let predicate2 = NSPredicate(format: "codeContext.problemID == %@", "\(probelmID)") + let predicate1 = NSPredicate(format: "id == %@", "\(String(describing: id))") + let predicate2 = NSPredicate(format: "codeContext.problemID == %@", "\(String(describing: probelmID))") request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1, predicate2]) return request } - static func isEqualStatusCode(_ statusCode: String) -> NSFetchRequest { + static func isEqualStatusCode(_ statusCode: StatusCode) -> NSFetchRequest { let request = newFetchRequest() let predicate1 = NSPredicate(format: "statusCode == %@", "\(statusCode)") request.predicate = predicate1 @@ -78,18 +78,18 @@ extension SubmissionMO { return request } - static func isNotTempSubmission(probelmID: ID, status: String) -> NSFetchRequest { + static func isNotTempSubmission(probelmID: ProblemID, status: StatusCode) -> NSFetchRequest { let request = newFetchRequest() let predicate1 = NSPredicate(format: "statusCode != %@", "\(status)") - let predicate2 = NSPredicate(format: "codeContext.problemID == %@", "\(probelmID)") + let predicate2 = NSPredicate(format: "codeContext.problemID == %@", "\(String(describing: probelmID))") request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1, predicate2]) request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)] return request } - static func isExistSubmission(problemID: ID) -> NSFetchRequest { + static func isExistSubmission(problemID: ProblemID) -> NSFetchRequest { let request = newFetchRequest() - let predicate1 = NSPredicate(format: "codeContext.problemID == %@", "\(problemID)") + let predicate1 = NSPredicate(format: "codeContext.problemID == %@", "\(String(describing: problemID))") request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1]) request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)] // request.fetchLimit = 1 @@ -107,15 +107,15 @@ extension SubmissionMO { return request } - func update(submission: Submission) -> Self { - self.codeContext?.code = submission.sourceCode - self.codeContext?.problemID = submission.problem?.id - self.codeContext?.problemTitle = submission.problem?.title - self.language?.languageID = submission.language?.id - self.language?.name = submission.language?.name - self.language?.extension = submission.language?._extension - self.statusCode = submission.statusCode - self.createdAt = submission.createdAt?.toDateKST() - return self - } +// func update(submission: Submission) -> Self { +// self.codeContext?.code = submission.sourceCode +// self.codeContext?.problemID = submission.problem?.id +// self.codeContext?.problemTitle = submission.problem?.title +// self.language?.languageID = submission.language?.id +// self.language?.name = submission.language?.name +// self.language?.extension = submission.language?._extension +// self.statusCode = submission.statusCode +// self.createdAt = submission.createdAt?.toDateKST() +// return self +// } } diff --git a/Projects/CodestackApp/Sources/Data/Repository/DefaultDBRepository.swift b/Projects/CodestackApp/Sources/Data/Repository/DefaultDBRepository.swift index 7b3b6dc1..e5ba474c 100644 --- a/Projects/CodestackApp/Sources/Data/Repository/DefaultDBRepository.swift +++ b/Projects/CodestackApp/Sources/Data/Repository/DefaultDBRepository.swift @@ -19,15 +19,15 @@ final class DefaultDBRepository: DBRepository { self.persistentStore = persistenStore } - func fetch(_ requestType: SubmissionMO.RequestType = .default) -> Single<[Submission]> { - Single<[Submission]>.create { [weak persistentStore] single in + func fetch(_ requestType: SubmissionMO.RequestType = .default) -> Single<[SubmissionVO]> { + Single<[SubmissionVO]>.create { [weak persistentStore] single in let fetchRequest = requestType.conditionRequest() fetchRequest.returnsObjectsAsFaults = false let observable = persistentStore? .fetch(fetchRequest , map: { value in - Submission(managedContext: value) + value.toDomain() }).subscribe { value in if case let .next(result) = value { switch result { @@ -128,13 +128,13 @@ final class DefaultDBRepository: DBRepository { } } - func update(submission: Submission, type request: SubmissionMO.RequestType) -> Single { + func update(submission: SubmissionVO, type request: SubmissionMO.RequestType) -> Single { Single.create { [weak persistentStore] single in let observable = persistentStore?.update { context in let request = request.conditionRequest() let willUpdateSubmissionMO = try context.fetch(request) if let mo = willUpdateSubmissionMO.first { - let _ = mo.update(submission: submission) + let _ = submission.updateMO(mo: mo) } }.subscribe(onNext: { value in single(value) @@ -145,13 +145,14 @@ final class DefaultDBRepository: DBRepository { } } - func store(submission: Submission) -> Single { + func store(submission: SubmissionVO) -> Single { Single.create { [weak persistentStore] single in let observable = persistentStore?.update { context in - guard let submissionMO = submission.store(in: context) else { return } - if let statusCode = submission.statusCode, - statusCode != "temp" && statusCode != "favorite" { + // TODO: Submission -> SubmissionVO + guard let submissionMO = submission.toMO(in: context) else { return } + + if submission.statusCode != "temp" && submission.statusCode != "favorite" { // TODO: Submission Calendar 에 Submission 관계 설정 // 임시저장과 즐겨찾기가 아닐경우 즉, 제출 히스토리만 관계 설정 let submissionCalendar = try SubmissionCalendarMO.fetchSubmissionCalendarMO(context: context) diff --git a/Projects/CodestackApp/Sources/Data/Repository/DefaultRepository.swift b/Projects/CodestackApp/Sources/Data/Repository/DefaultRepository.swift index 601b6377..7ba7c15e 100644 --- a/Projects/CodestackApp/Sources/Data/Repository/DefaultRepository.swift +++ b/Projects/CodestackApp/Sources/Data/Repository/DefaultRepository.swift @@ -27,17 +27,22 @@ import Global //public typealias MemberSolvedProblem = GetMeQuery.Data.GetMe.SolvedProblem -class DefaultRepository: WebRepository { +public class DefaultRepository: WebRepository { private var tokenAcquizition: TokenAcquisitionService private var graphAPI: GraphQLAPI - private var restAPI: RestAPI = DefaultRestAPI(session: .shared) + private var restAPI: RestAPI + public struct Dependency { + var tokenAcquizition: TokenAcquisitionService + var graphAPI: GraphQLAPI + var restAPI: RestAPI + } - init(dependency: TokenAcquisitionService ) { - self.tokenAcquizition = dependency - self.graphAPI = DefaultGraphQLAPI(dependency: .init(tokenService: dependency, - baseURL: DefaultRepository.baseURL)) + public init(dependency: Dependency ) { + self.tokenAcquizition = dependency.tokenAcquizition + self.graphAPI = dependency.graphAPI + self.restAPI = dependency.restAPI } func getSubmissionDate(api: GRAPH) -> Single { @@ -59,7 +64,7 @@ class DefaultRepository: WebRepository { // .asSingle() } - func request(type: T.Type, graph: GRAPH) -> Maybe { + public func request(type: T.Type, graph: GRAPH) -> Maybe { let query = API.GRAPH_QUERY(path: graph) switch query { @@ -88,12 +93,13 @@ class DefaultRepository: WebRepository { default: break } + return .empty() } typealias MatchMember = FetchSolvedProblemQuery.Data.MatchMember.SolvedProblem.Fragments - func getSolvedProblems(_ query: FetchSolvedProblemQuery) -> Maybe<[ProblemIdentityFR]> { + public func getSolvedProblems(_ query: FetchSolvedProblemQuery) -> Maybe<[ProblemIdentityFR]> { // let query = API.GRAPH_QUERY(path: graph) as! FetchSolvedProblemQuery return graphAPI.fetch(query: query , cachePolicy: .default, queue: .main) .map { item in @@ -101,7 +107,7 @@ class DefaultRepository: WebRepository { } } - func getMe(_ graph: GRAPH) -> Maybe { + public func getMe(_ graph: GRAPH) -> Maybe { let query = API.GRAPH_QUERY(path: graph) as! FetchMeQuery return graphAPI.fetch(query: query, cachePolicy: .fetchIgnoringCacheData, queue: .main) .map { item in @@ -113,7 +119,7 @@ class DefaultRepository: WebRepository { }) } - func updateNickName(_ graph: GRAPH) -> Maybe { + public func updateNickName(_ graph: GRAPH) -> Maybe { let mutation = API.GRAPH_MUTATION(path: graph) as! UpdateNickNameMutation return graphAPI.perform(query: mutation, cachePolichy: .default, queue: .global(qos: .default)) .map { item in @@ -125,7 +131,7 @@ class DefaultRepository: WebRepository { }) } - func perform(_ graph: GRAPH, max retry: Int = 2) -> Maybe { + public func perform(_ graph: GRAPH, max retry: Int = 2) -> Maybe { let mutation = API.GRAPH_MUTATION(path: graph) as! SubmitSubmissionMutation return graphAPI.perform(query: mutation, cachePolichy: .default, queue: .main) .map { item in @@ -137,7 +143,7 @@ class DefaultRepository: WebRepository { }) // retry } - func getProblemsQuery(_ graph: GRAPH) -> Maybe<([ProblemFR],PageInfoFR)> { + public func getProblemsQuery(_ graph: GRAPH) -> Maybe<([ProblemFR],PageInfoFR)> { let query = API.GRAPH_QUERY(path: graph) as! FetchProblemsQuery return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) .map { item in @@ -150,7 +156,7 @@ class DefaultRepository: WebRepository { } } - func getProblemByID(_ graph: GRAPH) -> Maybe { + public func getProblemByID(_ graph: GRAPH) -> Maybe { let query = API.GRAPH_QUERY(path: graph) as! FetchProblemByIdQuery return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) .map { data in @@ -158,7 +164,7 @@ class DefaultRepository: WebRepository { } } - func getAllTag(_ graph: GRAPH) -> Maybe<([TagFR],PageInfoFR)> { + public func getAllTag(_ graph: GRAPH) -> Maybe<([TagFR],PageInfoFR)> { //tag: 완전 탐색 //tag: 4 //tag: 자료 구조 @@ -173,7 +179,7 @@ class DefaultRepository: WebRepository { } } - func getAllLanguage(_ graph: GRAPH) -> Maybe<[LanguageFR]> { + public func getAllLanguage(_ graph: GRAPH) -> Maybe<[LanguageFR]> { let query = API.GRAPH_QUERY(path: graph) as! FetchAllLanguageQuery return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) .map { data in @@ -183,7 +189,7 @@ class DefaultRepository: WebRepository { } } - func getMeSubmissions(_ graph: GRAPH) -> Maybe<[SubmissionFR]> { + public func getMeSubmissions(_ graph: GRAPH) -> Maybe<[SubmissionFR]> { let query = API.GRAPH_QUERY(path: graph) as! FetchMeSubmissionsQuery return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) .map { data in @@ -193,7 +199,7 @@ class DefaultRepository: WebRepository { } } - func getSubmission(_ graph: GRAPH, cache: CachePolicy? = nil) -> Maybe<([SubmissionFR],PageInfoFR)> { + public func getSubmission(_ graph: GRAPH, cache: CachePolicy? = nil) -> Maybe<([SubmissionFR],PageInfoFR)> { let query = API.GRAPH_QUERY(path: graph) as! FetchSubmissionsQuery return graphAPI.fetch(query: query, cachePolicy: cache ?? .default, queue: .main) .map { data in diff --git a/Projects/CodestackApp/Sources/Data/Repository/UserManager.swift b/Projects/CodestackApp/Sources/Data/Repository/UserManager.swift index 7501b0d8..cc68a0ed 100644 --- a/Projects/CodestackApp/Sources/Data/Repository/UserManager.swift +++ b/Projects/CodestackApp/Sources/Data/Repository/UserManager.swift @@ -27,12 +27,13 @@ final class UserManager { static var shared = UserManager() - var profile: User { + var profile: MemberVO { get { - return User(userName: UserManager.userName, - nickName: UserManager.nickName, - email: UserManager.email , - profileImage: UserManager.profileImage) + return MemberVO(email: UserManager.email ?? "N/A", + nickName: UserManager.nickName ?? "N/A", + username: UserManager.userName ?? "N/A", + solvedProblems: [], + profileImage: UserManager.profileImage ?? "codeStack") } } @@ -78,11 +79,12 @@ extension UserManager { case tokenType } - func saveUser(with user: User) { - UserManager.userName = user.username - UserManager.nickName = user.nickname - UserManager.email = user.email - UserManager.profileImage = user.profileImage + func saveUser(with member: MemberVO) { + UserManager.userName = member.username + UserManager.nickName = member.nickName + UserManager.email = member.email + UserManager.profileImage = member.profileImage + // TODO: Solved Problem 저장 여부 } func saveTokenInfo(with token: TokenInfo) { diff --git a/Projects/CodestackApp/Sources/Data/Util/ConvertUtil.swift b/Projects/CodestackApp/Sources/Data/Util/ConvertUtil.swift deleted file mode 100644 index 99c47ad9..00000000 --- a/Projects/CodestackApp/Sources/Data/Util/ConvertUtil.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// ConvertUtil.swift -// CodeStack -// -// Created by 박형환 on 2023/07/26. -// - -import Foundation -import CodestackAPI - -enum ConvertUtil{ - - static func converting(submission: CreateSubmissionMutation.Data.CreateSubmission) -> Submission{ - let sub = submission - - let id = sub.id - let status = sub.statusCode - - return Submission(id: id, - sourceCode: "", - problem: .init(title: "test"), - member: .init(), - language: .init(id: "", name: "", _extension: ""), - cpuTime: 0, - memoryUsage: 0, - statusCode: status!, - createdAt: "") - } - - static func converting(problems: GetProblemsQuery.Data.GetProblems) -> [Problem]{ - let datas = problems.content.map { - $0.map { data in - - let tag = data.tags.map{ tag in - Tag(id: tag.id, name: tag.name) - } - - let languages = data.languages.map{ lan in - Language(id: lan.id, name: lan.name, _extension: lan.extension) - } - - - let problem = Problem(id: data.id, title: data.title, context: data.context, maxCpuTime: 0, maxMemory: 0, - submission: data.submission, accepted: data.accepted, tags: tag, languages: languages) - return problem - } - } - return datas ?? [] - } - - static func converting(tag: GetAllTagQuery.Data.GetAllTag) -> [Tag]{ - tag.content.map { - $0.map { data in - print("tag: \(data.id)") - print("tag: \(data.name)") - } - } - return [] - } - - static func converting(data: [GetAllLanguageQuery.Data.GetAllLanguage]) -> [Language] { - return data.map { lan in - return Language(id: lan.id, name: lan.name, _extension: lan.extension) - } - } - - static func converting(all submission: GetSubmissionsQuery.Data.GetSubmissions) -> [Submission] { - submission.content.map{ content in - return content.map{value in - } - } - return [] - } - -} diff --git a/Projects/CodestackApp/Sources/Data/Util/Query.swift b/Projects/CodestackApp/Sources/Data/Util/Query.swift deleted file mode 100644 index 0806b81b..00000000 --- a/Projects/CodestackApp/Sources/Data/Util/Query.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// GraphQuery.swift -// CodeStack -// -// Created by 박형환 on 2023/07/25. -// - -import Foundation -import CodestackAPI - - diff --git a/Projects/CodestackApp/Sources/Domain/Usecase/SubmissionUseCase.swift b/Projects/CodestackApp/Sources/Domain/Usecase/SubmissionUseCase.swift index 9768cd99..d7ab6acb 100644 --- a/Projects/CodestackApp/Sources/Domain/Usecase/SubmissionUseCase.swift +++ b/Projects/CodestackApp/Sources/Domain/Usecase/SubmissionUseCase.swift @@ -11,21 +11,21 @@ import RxCocoa import Data import Global -typealias State = Result +public typealias State = Result -protocol SubmissionUseCase: AnyObject { - func submitSubmissionAction(model: SendProblemModel) -> Observable> - func updateSubmissionAction(model: SendProblemModel) -> Observable +public protocol SubmissionUseCase: AnyObject { + func submitSubmissionAction(model: SubmissionVO) -> Observable> + func updateSubmissionAction(model: SubmissionVO) -> Observable - func fetchProblemSubmissionHistory(id: ProblemID, state code: String) -> Observable> + func fetchProblemSubmissionHistory(id: ProblemID, state code: String) -> Observable> - // func fetchSubmissionByLanguage(name: String) -> Observable> func updateFavoritProblem(model: FavoriteProblem, flag: Bool) -> Observable> func fetchFavoriteProblem() -> Observable> - func fetchProblem(id: ProblemID) -> Observable> - func fetchProblemHistoryEqualStatus(status code: String) -> Observable> + func fetchProblem(id: ProblemID) -> Observable> + + func fetchProblemHistoryEqualStatus(status code: String) -> Observable> func fetchSubmissionCalendar() -> Observable> } @@ -36,6 +36,7 @@ enum SendError: Error { case none } + final class DefaultSubmissionUseCase: SubmissionUseCase { private let dbRepository: DBRepository @@ -47,56 +48,62 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { self.webRepository = webRepository } - func fetchProblem(id: ProblemID) -> Observable> { + + func fetchProblem(id: ProblemID) -> Observable> { webRepository - .getProblemByID(query: Query.getProblemByID(id: id)) + .getProblemByID(.PR_BY_ID(id)) .asObservable() - .map { problem in .success(problem)} + .map { problem in .success(problem.toDomain())} .catchAndReturn(.failure(SendError.fetchFailProblem)) } - func submitSubmissionAction(model: SendProblemModel) -> Observable> { - dbRepository.fetch(.recent(model.problemID)) + func submitSubmissionAction(model: SubmissionVO) -> Observable> { + dbRepository.fetch(.recent(model.problem.id)) .asObservable() .subscribe(on: MainScheduler.instance) + // TODO: View에서 판단하는게 맞음 .filter { value in guard let recentSubmission = value.first else { return true } if recentSubmission.isEqualContent(other: model) { throw SendError.isEqualSubmission } return true } .observe(on: ConcurrentDispatchQueueScheduler.init(queue: .global())) - .flatMap { [weak webRepository] value -> Maybe in + .map { _ in + GRSubmit(languageId: model.language.id, + problemId: model.problem.id, + sourceCode: model.sourceCode) + } + .flatMap { [weak webRepository] model -> Maybe in guard let webRepository else { return .empty() } - let mutation = Mutation.problemSubmit(model: model) return webRepository - .perform(mutation: mutation, max: 2) - .map { result in Submission(with: result.createSubmission) } + .perform(.SUBMIT_SUB(submit: model), max: 2) + .map { submitFR in submitFR.toDomain() } } .do(onNext: { [weak self] in self?.storeInRepo(submission: $0) }) - .map { .success($0)} + .map { .success($0) } .catch { .just(.failure($0)) } } - func fetchProblemSubmissionHistory(id: ProblemID, state code: String) -> Observable> { + func fetchProblemSubmissionHistory(id: ProblemID, state code: String) -> Observable> { dbRepository.fetch(.isNotTemp(id, code)) .asObservable() .map { .success($0) } } - func fetchProblemHistoryEqualStatus(status code: String) -> Observable> { + func fetchProblemHistoryEqualStatus(status code: String) -> Observable> { dbRepository.fetch(.isEqualStatusCode(code)) .asObservable() .map { .success($0) } } - func updateSubmissionAction(model: SendProblemModel) -> Observable { + func updateSubmissionAction(model: SubmissionVO) -> Observable { // MARK: 이미 임시저장이 되어있을 경우 무시 // 최근 기록이 같을때 dbRepository .fetchProblemState() .asObservable() - .flatMap { states throws -> Observable<[Submission]> in + .flatMap { states throws -> Observable<[SubmissionVO]> in if let state = states.first { return .just(state.submissions) } else { @@ -110,8 +117,7 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { // 내용이 없을때 임시저장 if submissions.isEmpty || submission == nil { - let _submission = model.makeTempSubmission() - return self.dbRepository.store(submission: _submission) + return self.dbRepository.store(submission: model) .asObservable() .map { _ in true } } @@ -120,9 +126,7 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { if !unWrapSubmission.isEqualContent(other: model) { // 다른 내용일때 임시저장 업데이트 - let _submission = model.makeTempSubmission() - - return self.updateSubmissionState(submission: _submission) + return self.updateSubmissionState(submission: model) .asObservable() .map { _ in true } } else { @@ -177,12 +181,12 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { } // TODO: Update 필요 - private func updateSubmissionState(submission: Submission) -> Single { + private func updateSubmissionState(submission: SubmissionVO) -> Single { return delete(submission: submission) .andThen(dbRepository.store(submission: submission)) } - private func storeInRepo(submission: Submission) { + private func storeInRepo(submission: SubmissionVO) { let _ = dbRepository.store(submission: submission) .subscribe(with: self, onSuccess: { vm, _ in Log.debug("success Store in ProblemState") @@ -192,8 +196,8 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { // 제일 최근 제출 현황을 가져온다 생각하면 // 0 == 없을때는 임시저장 시키면 되고,,,,,, // 1 == 존재했을때는 상태가 temp가 아닐경우에 temp로 변경 하면 될듯? - private func saveInRepository(model: SendProblemModel, submissions: [Submission]) -> Single { - let _submission = model.makeTempSubmission() + private func saveInRepository(model: SubmissionVO, submissions: [SubmissionVO]) -> Single { + let _submission = model if submissions.count == 0 { return store(submission: _submission) @@ -202,32 +206,29 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { if submissions.count == 1 { let fetchedSubmission = submissions.first! - if let statusCode = fetchedSubmission.statusCode { - - if statusCode == "temp" { - return update(submission: _submission) - } - - if _submission.sourceCode == fetchedSubmission.sourceCode, - _submission.language == fetchedSubmission.language { - return .just(()) - } else { - return store(submission: _submission) - } + let statusCode = fetchedSubmission.statusCode + + if statusCode == "temp" { + return update(submission: _submission) } + + return store(submission: _submission) + // if _submission.sourceCode == fetchedSubmission.sourceCode, + // _submission.language == fetchedSubmission.language { + // return .just(()) + // } else { } + } return .just(()) } - private func deleteTempRepo(submission: Submission) { - guard let id = submission.problem?.id else { return } - _ = dbRepository.fetch(.isExist(id)) + private func deleteTempRepo(submission: SubmissionVO) { + _ = dbRepository.fetch(.isExist(submission.problem.id)) .flatMap { [weak self] fetchedSubmissions in guard let self else { return .just(()) } if fetchedSubmissions.count == 1, let fetchedSubmission = fetchedSubmissions.first, - let statusCode = fetchedSubmission.statusCode, - statusCode == "temp" { + fetchedSubmission.statusCode == "temp" { return self.delete(submission: fetchedSubmission) .andThen(self.store(submission: submission)) } @@ -239,57 +240,23 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { } - private func delete(submission: Submission) -> Completable { - if let languageName = submission.language?.name, - let problemID = submission.problem?.id , - let statusCode = submission.statusCode { - return dbRepository - .remove(.delete(languageName, problemID, statusCode)) - } - return .empty() + private func delete(submission: SubmissionVO) -> Completable { + let languageName = submission.language.name + let problemID = submission.problem.id + let statusCode = submission.statusCode + return dbRepository + .remove(.delete(languageName, problemID, statusCode)) } - private func store(submission: Submission) -> Single { + private func store(submission: SubmissionVO) -> Single { dbRepository .store(submission: submission) } - private func update(submission: Submission) -> Single { + private func update(submission: SubmissionVO) -> Single { dbRepository .update(submission: submission, - type: .update(submission.id ?? "-1", - submission.problem?.id ?? "")) + type: .update(submission.id, submission.problem.id )) } } -extension Submission { - - /// 제출 같은 language와 source코드 여부 - /// - Parameter other: 다른 제출 모델 - /// - Returns: 같은 내용인지 flag - func isEqualContent(other: SendProblemModel) -> Bool { - if self.sourceCode == other.sourceCode, - self.language == other.language { - return true - } else { - return false - } - } -} -extension Array where Element == Submission { - - - /// 같은 Problem ID를 찾는 함수, - /// 임시저장을 배열에서 일치하는 제출을 찾아서 업데이트 할때 필요한 extension 함수 - /// - Parameter model: SendProblemModel - /// - Returns: 일치하는 Submission - func find(model: SendProblemModel) -> Submission? { - return self.filter { value in - if let id = value.problem?.id , - id == model.problemID { - return true - } - return false - }.first - } -} diff --git a/Projects/CodestackApp/Sources/Domain/VO/LanguageVO.swift b/Projects/CodestackApp/Sources/Domain/VO/LanguageVO.swift index 1e927fb6..86b59ce4 100644 --- a/Projects/CodestackApp/Sources/Domain/VO/LanguageVO.swift +++ b/Projects/CodestackApp/Sources/Domain/VO/LanguageVO.swift @@ -9,7 +9,7 @@ import Foundation -public struct LanguageVO: Codable { +public struct LanguageVO: Codable, Equatable { public let id: String public let name: String public let `extension`: String @@ -20,3 +20,22 @@ public struct LanguageVO: Codable { self.extension = `extension` } } + +extension LanguageVO { + static var `default`: Self { + LanguageVO(id: "1", + name: "C", + extension: ".c") + } + + static var sample: [Self] { + let lang = ["C", "C++", "Node", "GO", "Python3"] + let `extension` = [".c",".cpp", ".js", ".go", ".py"] + + let languages = zip(lang,`extension`).enumerated().map { (value) in + let (offset, (name, ex)) = value + return LanguageVO(id: "\(offset + 1)", name: name, extension: "\(ex)") + } + return languages + } +} diff --git a/Projects/CodestackApp/Sources/Domain/VO/MemberVO.swift b/Projects/CodestackApp/Sources/Domain/VO/MemberVO.swift index aab5180c..b6d82d84 100644 --- a/Projects/CodestackApp/Sources/Domain/VO/MemberVO.swift +++ b/Projects/CodestackApp/Sources/Domain/VO/MemberVO.swift @@ -9,7 +9,7 @@ import Foundation import Data -public struct MemberNameVO: Codable { +public struct MemberNameVO: Codable, Equatable { public let username: String public init(username: String) { @@ -18,18 +18,28 @@ public struct MemberNameVO: Codable { } public struct MemberVO: Codable { - public let id: ID public let email: String public let nickName: String - public let imageURL: String + public let username: String + public let solvedProblems: [ProblemIdentityVO] + public let profileImage: String + + init(email: String, nickName: String, username: String, solvedProblems: [ProblemIdentityVO], profileImage: String) { + self.email = email + self.nickName = nickName + self.username = username + self.solvedProblems = solvedProblems + self.profileImage = profileImage + } } extension MemberDTO { func toDomain() -> MemberVO { - MemberVO(id: self.id, - email: self.email, + MemberVO(email: self.email, nickName: self.nickName, - imageURL: self.imageURL ?? "codeStack") + username: "", + solvedProblems: [], + profileImage: self.imageURL ?? "codeStack") } } diff --git a/Projects/CodestackApp/Sources/Domain/VO/ProblemIdentityVO.swift b/Projects/CodestackApp/Sources/Domain/VO/ProblemIdentityVO.swift index dd81e42b..af057f92 100644 --- a/Projects/CodestackApp/Sources/Domain/VO/ProblemIdentityVO.swift +++ b/Projects/CodestackApp/Sources/Domain/VO/ProblemIdentityVO.swift @@ -9,7 +9,7 @@ import Foundation -public struct ProblemIdentityVO: Codable { +public struct ProblemIdentityVO: Codable, Equatable { public let id: String public let title: String @@ -19,3 +19,18 @@ public struct ProblemIdentityVO: Codable { } } + +public extension ProblemIdentityVO { + + func toProblemVO() -> ProblemVO { + ProblemVO(id: self.id, + title: self.title, + context: "", + languages: [], + tags: [], + accepted: 0, + submission: 0, + maxCpuTime: "", + maxMemory: 0) + } +} diff --git a/Projects/CodestackApp/Sources/Domain/VO/ProblemVO.swift b/Projects/CodestackApp/Sources/Domain/VO/ProblemVO.swift index 558f0733..88dd825a 100644 --- a/Projects/CodestackApp/Sources/Domain/VO/ProblemVO.swift +++ b/Projects/CodestackApp/Sources/Domain/VO/ProblemVO.swift @@ -44,8 +44,12 @@ public struct ProblemVO: Codable { } } -extension ProblemVO { - func toSubVO() -> SubmissionVO { +extension ProblemVO: Equatable { + + public static var sample: Self { + .init(id: "", title: "'", context: "'", languages: [], tags: [], accepted: 0, submission: 0, maxCpuTime: "", maxMemory: 0) + } +// func toSubVO() -> SubmissionVO { // SubmissionVO(id: <#T##String#>, // sourceCode: <#T##String#>, // problem: <#T##ProblemIdentityVO#>, @@ -55,5 +59,5 @@ extension ProblemVO { // memoryUsage: <#T##Double#>, // statusCode: <#T##String#>, // createdAt: <#T##String#>) - } +// } } diff --git a/Projects/CodestackApp/Sources/Domain/VO/SubmissionVO.swift b/Projects/CodestackApp/Sources/Domain/VO/SubmissionVO.swift index 84f77341..b3be9ac2 100644 --- a/Projects/CodestackApp/Sources/Domain/VO/SubmissionVO.swift +++ b/Projects/CodestackApp/Sources/Domain/VO/SubmissionVO.swift @@ -20,21 +20,26 @@ public struct SubmissionInfoVO: Codable { } } -public struct SubmissionVO: Codable { - var id: String - var sourceCode: String - var problem: ProblemIdentityVO - var member: MemberNameVO - var language: LanguageVO - var cpuTime: Double - var memoryUsage: Double - var statusCode: String - var createdAt: String + + +public struct SubmissionVO: Codable, Equatable { + public var id: String + public var sourceCode: String + public var problem: ProblemIdentityVO + public var problemVO: ProblemVO + public var member: MemberNameVO + public var language: LanguageVO + public var cpuTime: Double + public var memoryUsage: Double + public var statusCode: String + public var createdAt: String - public init(id: String, sourceCode: String, problem: ProblemIdentityVO, member: MemberNameVO, language: LanguageVO, cpuTime: Double, memoryUsage: Double, statusCode: String, createdAt: String) { + public init(id: String, sourceCode: String, problem: ProblemIdentityVO, + problemVO: ProblemVO = ProblemVO.sample ,member: MemberNameVO, language: LanguageVO, cpuTime: Double, memoryUsage: Double, statusCode: String, createdAt: String) { self.id = id self.sourceCode = sourceCode self.problem = problem + self.problemVO = problemVO self.member = member self.language = language self.cpuTime = cpuTime diff --git a/Projects/CodestackApp/Sources/Domain/VO/TagVO.swift b/Projects/CodestackApp/Sources/Domain/VO/TagVO.swift index bd958221..ed258240 100644 --- a/Projects/CodestackApp/Sources/Domain/VO/TagVO.swift +++ b/Projects/CodestackApp/Sources/Domain/VO/TagVO.swift @@ -19,7 +19,7 @@ public struct TagInfoVO: Codable { } } -public struct TagVO: Codable { +public struct TagVO: Codable, Equatable { public let id: String public let name: String diff --git a/Projects/CodestackApp/Sources/Flow/CodestackStep.swift b/Projects/CodestackApp/Sources/Flow/CodestackStep.swift index 5f353293..babce775 100644 --- a/Projects/CodestackApp/Sources/Flow/CodestackStep.swift +++ b/Projects/CodestackApp/Sources/Flow/CodestackStep.swift @@ -64,7 +64,7 @@ enum CodestackStep: Step, CaseIterable{ case codestack case historyflow - case richText([Problem]) + case richText([ProblemVO]) case none diff --git a/Projects/CodestackApp/Sources/Model/CodeContext.swift b/Projects/CodestackApp/Sources/Model/CodeContext.swift deleted file mode 100644 index 4a386bfe..00000000 --- a/Projects/CodestackApp/Sources/Model/CodeContext.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// CodeContext.swift -// CodeStack -// -// Created by 박형환 on 11/22/23. -// - -import Foundation - -struct CodeContext { - var code: String? - var problemID: String? - var problemTitle: String? -} diff --git a/Projects/CodestackApp/Sources/Model/Language.swift b/Projects/CodestackApp/Sources/Model/Language.swift deleted file mode 100644 index c01d065f..00000000 --- a/Projects/CodestackApp/Sources/Model/Language.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// Language.swift -// CodeStack -// -// Created by 박형환 on 2023/06/26. -// - -import Foundation -import CodestackAPI - -struct Language: Codable,Equatable { - let id: String - let name: String - let _extension: String -} - -extension Language { - init(with lang: CreateSubmissionMutation.Data.CreateSubmission.Problem.Language){ - self.init(id: lang.id, name: lang.name, _extension: lang.extension) - } - - static var `default`: Self { - Language(id: "1", - name: "C", - _extension: ".c") - } - - static var sample: [Self] { - let lang = ["C", "C++", "Node", "GO", "Python3"] - let `extension` = [".c",".cpp", ".js", ".go", ".py"] - - let languages = zip(lang,`extension`).enumerated().map { (value) in - let (offset, (name, ex)) = value - return Language(id: "\(offset + 1)", name: name, _extension: "\(ex)") - } - return languages - } -} - -extension Language{ - init(with language: SubmissionMutation.Language){ - self.init(id: language.id, name: language.name, _extension: language.extension) - } -} - - diff --git a/Projects/CodestackApp/Sources/Model/Model+Mapping/FR+Mapping.swift b/Projects/CodestackApp/Sources/Model/Model+Mapping/FR+Mapping.swift new file mode 100644 index 00000000..0e00c539 --- /dev/null +++ b/Projects/CodestackApp/Sources/Model/Model+Mapping/FR+Mapping.swift @@ -0,0 +1,116 @@ +// +// Submission.swift +// CodeStack +// +// Created by 박형환 on 2023/06/26. +// + +import Foundation +import CodestackAPI + + +//[FProblemIdentity] +// +//FMember +// +//FSubmission +// +//[FProblem],FPageInfo +// +//FProblem +// +//[FTag],FPageInfo +// +//[FLanguage] +// +//[FSubmission] +// +//[FSubmission],FPageInfo) + + +extension MemberFR { + func toDomain() -> MemberVO { + MemberVO(email: self.email ?? "", + nickName: self.nickname, + username: self.username, + solvedProblems: self.solvedProblems.map { $0.fragments.problemIdentityFR.toDomain() }, + profileImage: self.profileImage ?? "codeStack") + } +} + +extension SubmissionFR { + func toDomain() -> SubmissionVO { + SubmissionVO(id: self.id, + sourceCode: self.sourceCode, + problem: self.problem.fragments.problemIdentityFR.toDomain(), + member: MemberNameVO(username: self.member.username), + language: self.language.fragments.languageFR.toDomain(), + cpuTime: self.cpuTime ?? -1, + memoryUsage: self.memoryUsage ?? -1, + statusCode: self.statusCode ?? "fail", + createdAt: self.createdAt) + } +} + +extension ProblemIdentityFR { + func toDomain() -> ProblemIdentityVO { + ProblemIdentityVO(id: self.id, + title: self.title) + } + + func toSubVO() -> SubmissionVO { + SubmissionVO(id: "", + sourceCode: "", + problem: self.toDomain(), + member: .init(username: ""), + language: .init(id: "", name: "", extension: ""), + cpuTime: 0, + memoryUsage: 0, + statusCode: "", + createdAt: "") + } + +} +extension LanguageFR { + func toDomain() -> LanguageVO { + LanguageVO(id: self.id, + name: self.name, + extension: self.extension) + } +} + +extension PageInfoFR { + func toDomain() -> PageInfoVO { + PageInfoVO(limit: self.limit, + offset: self.offset, + totalContent: self.totalContent, + totalPage: self.totalPage) + } +} + +extension ProblemFR { + + func toDomain() -> ProblemVO { + ProblemVO(id: self.id, + title: self.title, + context: self.context, + languages: self.languages.map { $0.fragments.languageFR.toDomain() }, + tags: self.tags.map { $0.fragments.tagFR.toDomain() }, + accepted: self.accepted, + submission: self.submission, + maxCpuTime: self.maxCpuTime, + maxMemory: self.maxMemory) + } + + func toIdentifyFR() -> ProblemIdentityFR { + // TODO: 체크해야됌 + try! ProblemIdentityFR(data: ["id" : self.id, "title" : self.title]) + } +} + +extension TagFR { + func toDomain() -> TagVO { + TagVO(id: String(self.id), + name: self.name) + } +} diff --git a/Projects/CodestackApp/Sources/Model/Model+Mapping/ProblemVO+Mapping.swift b/Projects/CodestackApp/Sources/Model/Model+Mapping/ProblemVO+Mapping.swift new file mode 100644 index 00000000..df861fc3 --- /dev/null +++ b/Projects/CodestackApp/Sources/Model/Model+Mapping/ProblemVO+Mapping.swift @@ -0,0 +1,28 @@ +// +// Problem.swift +// CodeStack +// +// Created by 박형환 on 2023/06/26. +// + +import Foundation +import CodestackAPI + + +//MARK: - To View Model +extension ProblemVO { + func toProblemList(_ submission: SubmissionVO? = nil) -> ProblemListItemModel { + return ProblemListItemModel(submissionID: submission?.id ?? UUID().uuidString, + seletedLanguage: submission?.language ?? LanguageVO.default, + sourceCode: submission?.sourceCode ?? "", + problemNumber: self.id, + problemTitle: self.title , + submitCount: Int(self.submission) , + correctAnswer: Int(self.accepted) , + correctRate: self.submission.toRate(second: self.accepted), + contenxt: self.context, + tags: self.tags, + languages: self.languages) + } +} + diff --git a/Projects/CodestackApp/Sources/Model/Problem/FavoriteProblem.swift b/Projects/CodestackApp/Sources/Model/Problem/FavoriteProblem.swift deleted file mode 100644 index 4639645a..00000000 --- a/Projects/CodestackApp/Sources/Model/Problem/FavoriteProblem.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// FavoriteSubmission.swift -// CodeStack -// -// Created by 박형환 on 11/22/23. -// - -import Foundation - -struct FavoriteProblem { - let problemID: String - let problmeTitle: String - let createdAt: Date -} - - diff --git a/Projects/CodestackApp/Sources/Model/Problem/Problem.swift b/Projects/CodestackApp/Sources/Model/Problem/Problem.swift deleted file mode 100644 index 4cc60e60..00000000 --- a/Projects/CodestackApp/Sources/Model/Problem/Problem.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// Problem.swift -// CodeStack -// -// Created by 박형환 on 2023/06/26. -// - -import Foundation -import CodestackAPI - -struct Problem: Codable,Equatable{ - let id: String - let title: String - let context: String - let maxCpuTime: Int - let maxMemory: Int - let submission: Double - let accepted: Double - let tags: [Tag] - var languages: [Language] - - init(id: String = "", title: String, context: String = "", maxCpuTime: Int = 1, maxMemory: Int = 1, submission: Double = 1, accepted: Double = 1, tags: [Tag] = [], languages: [Language] = []) { - self.id = id - self.title = title - self.context = context - self.maxCpuTime = maxCpuTime - self.maxMemory = maxMemory - self.submission = submission - self.accepted = accepted - self.tags = tags - self.languages = languages - } -} - - -//MARK: - Submission Mutation Response Problem initializer -extension Problem { - init(create problem: SubmissionProblem){ - self.id = problem.id - self.title = problem.title - self.context = problem.context - self.maxCpuTime = Int(problem.maxCpuTime) ?? 0 - self.maxMemory = Int(problem.maxMemory) - self.submission = 0 - self.accepted = 0 - self.tags = problem.tags.map{ Tag(with: $0)} - self.languages = problem.languages.map{ Language(with: $0)} - } -} - - -//MARK: - GetProblem Query Response initializer -extension Problem { - init(problem: ProblemContent) { - self.id = problem.id - self.title = problem.title - self.context = problem.context - self.maxCpuTime = 0 - self.maxMemory = 0 - self.submission = problem.submission - self.accepted = problem.accepted - self.tags = problem.tags.map{ Tag(id: $0.id, name: $0.name) } - self.languages = problem.languages.map{ Language(id: $0.id, name: $0.name, _extension: $0.extension)} - } -} - - -//MARK: - To View Model -extension Problem{ - func toProblemList(_ submission: Submission? = nil) -> ProblemListItemModel{ - return ProblemListItemModel(submissionID: submission?.id, - seletedLanguage: submission?.language, - sourceCode: submission?.sourceCode, - problemNumber: self.id, - problemTitle: self.title , - submitCount: Int(self.submission) , - correctAnswer: Int(self.accepted) , - correctRate: self.submission.toRate(second: self.accepted), - contenxt: self.context, - tags: self.tags, - languages: self.languages) - } -} diff --git a/Projects/CodestackApp/Sources/Model/Problem/ProblemCalendar.swift b/Projects/CodestackApp/Sources/Model/Problem/ProblemCalendar.swift deleted file mode 100644 index a8f9c5f8..00000000 --- a/Projects/CodestackApp/Sources/Model/Problem/ProblemCalendar.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// ProblemCalendar.swift -// CodeStack -// -// Created by 박형환 on 2023/06/26. -// - -import Foundation -import Global - -struct ProblemCalendar: Codable { - let date: String - let solved: Int -} - -struct SubmissionCalendar: Codable { - let dates: [String] -} - -extension SubmissionCalendar { - - public static func generateMockCalendar() -> Self { - var date = Date() - let dates = (0...200).map { num in - let randomNumber = (0...200).randomElement()! - - let dateString = date.adding(day: -randomNumber).toString(format: .FULL) - return dateString - } - return SubmissionCalendar(dates: dates) - } -} - - diff --git a/Projects/CodestackApp/Sources/Model/Submission/Submission.swift b/Projects/CodestackApp/Sources/Model/Submission/Submission.swift deleted file mode 100644 index cc1013d8..00000000 --- a/Projects/CodestackApp/Sources/Model/Submission/Submission.swift +++ /dev/null @@ -1,260 +0,0 @@ -// -// Submission.swift -// CodeStack -// -// Created by 박형환 on 2023/06/26. -// - -import Foundation -import CodestackAPI - - -//[FProblemIdentity] -// -//FMember -// -//FSubmission -// -//[FProblem],FPageInfo -// -//FProblem -// -//[FTag],FPageInfo -// -//[FLanguage] -// -//[FSubmission] -// -//[FSubmission],FPageInfo) - - -extension MemberFR { - func toDomain() -> MemberVO { - MemberVO(id: self., - email: <#T##String#>, - nickName: <#T##String#>, - imageURL: <#T##String#>) - } -} - -extension SubmissionFR { - func toDomain() -> SubmissionVO { - SubmissionVO(id: self.id, - sourceCode: self.sourceCode, - problem: self.problem.fragments.problemIdentityFR.toDomain(), - member: MemberNameVO(username: self.member.username), - language: self.language.fragments.languageFR.toDomain(), - cpuTime: self.cpuTime ?? -1, - memoryUsage: self.memoryUsage ?? -1, - statusCode: self.statusCode ?? "fail", - createdAt: self.createdAt) - } -} - -extension ProblemIdentityFR { - func toDomain() -> ProblemIdentityVO { - ProblemIdentityVO(id: self.id, - title: self.title) - } - - func toSubVO() -> SubmissionVO { - SubmissionVO(id: "", - sourceCode: "", - problem: self.toDomain(), - member: .init(username: ""), - language: .init(id: "", name: "", extension: ""), - cpuTime: 0, - memoryUsage: 0, - statusCode: "", - createdAt: "") - } - -} -extension LanguageFR { - func toDomain() -> LanguageVO { - LanguageVO(id: self.id, - name: self.name, - extension: self.extension) - } -} - -extension PageInfoFR { - func toDomain() -> PageInfoVO { - PageInfoVO(limit: self.limit, - offset: self.offset, - totalContent: self.totalContent, - totalPage: self.totalPage) - } -} - -extension ProblemFR { - - func toDomain() -> ProblemVO { - ProblemVO(id: self.id, - title: self.title, - context: self.context, - languages: self.languages.map { $0.fragments.languageFR.toDomain() }, - tags: self.tags.map { $0.fragments.tagFR.toDomain() }, - accepted: self.accepted, - submission: self.submission, - maxCpuTime: self.maxCpuTime, - maxMemory: self.maxMemory) - } - - func toIdentifyFR() -> ProblemIdentityFR { - // TODO: 체크해야됌 - try! ProblemIdentityFR(data: ["id" : self.id, "title" : self.title]) - } -} - -extension TagFR { - func toDomain() -> TagVO { - TagVO(id: String(self.id), - name: self.name) - } -} - - -struct Submission: Codable { - var id: String? - var sourceCode: String? - var problem: Problem? - var member: User? - var language: Language? - var cpuTime: Int? - var memoryUsage: Int? - var statusCode: String? - var isTemp: Bool? - var isFavorite: Bool? - var createdAt: String? -} - -//MARK: - Default initializer -extension Submission { - init( _problem: Problem){ - id = nil - sourceCode = nil - problem = _problem - member = nil - language = nil - cpuTime = nil - memoryUsage = nil - statusCode = nil - isTemp = nil - isFavorite = nil - createdAt = nil - } - - init(_problem: Problem,status code: String){ - self.init(_problem: _problem) - self.statusCode = code - } - - init(id: Int, - sourceCode: String, - problem: Problem, - member: User, - language: Language, - cpuTime: Int, memoryUsage: Int, - statusCode: String, - isTemp: Bool, - isFavorite: Bool, - createdAt: String) { - - self.id = String(id) - self.sourceCode = sourceCode - self.problem = problem - self.member = member - self.language = language - self.cpuTime = cpuTime - self.memoryUsage = memoryUsage - self.statusCode = statusCode - self.isTemp = isTemp - self.isFavorite = isFavorite - self.createdAt = createdAt - } -} - -//MARK: GraphQL mapping initializer -extension Submission { - /// GraphQL Mutation Submission Result -// /// - Parameter submission: CreateSubmissionMutation Result -// init(with submission: SubmissionMutation) { -// self.id = submission.id -// self.statusCode = submission.statusCode -// self.problem = Problem(create: submission.problem) -// self.member = User(with: submission.member) -// self.language = Language(with: submission.language) -// self.cpuTime = nil -// self.sourceCode = submission.sourceCode -// self.memoryUsage = nil -// self.statusCode = submission.statusCode -// self.createdAt = submission.createdAt -// } -// -// init(submission: GetSubmissions.Content){ -// self.id = submission.id -// self.statusCode = submission.statusCode -// self.problem = Problem(id: submission.problem.id, title: submission.problem.title) -// self.language = Language(id: submission.language.id, name: submission.language.name, _extension: "") -// self.statusCode = submission.statusCode -// self.createdAt = submission.createdAt -// } -// - mutating func ifNetworkErorr() -> Self { - if self.problem?.languages == nil { - self.problem?.languages = Language.sample - } - - if self.problem!.languages.isEmpty { - self.problem?.languages = Language.sample - } - - return self - } -} - - -extension Submission { - - struct SubmissionState { - var submissions: [Submission] - } - - struct Temp { - var submission: [Submission] - } - - struct Favorite { - var problemID: ID - } -} - - -//MARK: - make Dummy Data -#if DEBUG -extension Submission { - - static func dummy(type: SegType.Value? = nil) -> [Self]{ - - let dummy = [Submission(_problem: Problem(title: "hello"),status: "AC"), - Submission(_problem: Problem(title: "my"),status: "WA"), - Submission(_problem: Problem(title: "name"),status: "RE"), - Submission(_problem: Problem(title: "is"),status: "PE"), - Submission(_problem: Problem(title: "hyeong"),status: "WA"), - Submission(_problem: Problem(title: "hwan"),status: "AC"), - Submission(_problem: Problem(title: "zzz"),status: "AC"), - Submission(_problem: Problem(title: "hahaha"),status: "TLE"), - Submission(_problem: Problem(title: "no~"),status: "MLE"), - Submission(_problem: Problem(title: "no~"),status: "favorite")] - - if let type { - return dummy.filter{ submission in - return submission.statusCode?.checkIsEqual(with: type) ?? false - } - }else{ - return dummy - } - } -} -#endif diff --git a/Projects/CodestackApp/Sources/Model/Submission/TempSubmission.swift b/Projects/CodestackApp/Sources/Model/Submission/TempSubmission.swift deleted file mode 100644 index 0bad7e5b..00000000 --- a/Projects/CodestackApp/Sources/Model/Submission/TempSubmission.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// TempSubmission.swift -// CodeStack -// -// Created by 박형환 on 11/22/23. -// - -import Foundation - - -struct TempSubmission { - let id: String - var codeContext: CodeContext? - var createdAt: Date? -} - -extension TempSubmission { - static func tempSubmission(submission: Submission) -> TempSubmission { - let codeContext = CodeContext(code: submission.sourceCode, - problemID: submission.problem?.id, - problemTitle: submission.problem?.title) - return TempSubmission(id: submission.id!, - codeContext: codeContext, - createdAt: submission.createdAt?.toDateKST()) - } -} diff --git a/Projects/CodestackApp/Sources/Model/Tag.swift b/Projects/CodestackApp/Sources/Model/Tag.swift deleted file mode 100644 index de4744ce..00000000 --- a/Projects/CodestackApp/Sources/Model/Tag.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// Tag.swift -// CodeStack -// -// Created by 박형환 on 2023/06/26. -// - -import Foundation -import CodestackAPI - -struct Tag: Codable,Equatable{ - let id: Double - let name: String - - init(id: Double,name: String){ - self.id = id - self.name = name - } - - init(with tag: CreateSubmissionMutation.Data.CreateSubmission.Problem.Tag){ - self.id = 0 - self.name = tag.name - } -} diff --git a/Projects/CodestackApp/Sources/Model/User.swift b/Projects/CodestackApp/Sources/Model/User.swift deleted file mode 100644 index 83f00a36..00000000 --- a/Projects/CodestackApp/Sources/Model/User.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// Member.swift -// CodeStack -// -// Created by 박형환 on 2023/06/26. -// - -import Foundation - - -struct User: Codable { - let username: String? - let nickname: String? - let email: String? - let profileImage: String? - var rank: String? - let problemCalendar: [ProblemCalendar?] - - //MARK: 나중에 데이터 타입 확인 필요 - let year: String? - - init(userName: String?, nickName: String?, email: String?, profileImage: String?, problemCalendar: [ProblemCalendar?] = [], year: String? = nil){ - self.username = userName - self.nickname = nickName - self.email = email - self.profileImage = profileImage - self.problemCalendar = problemCalendar - self.year = year - } - - init() { - self.username = nil - self.nickname = nil - self.email = nil - self.profileImage = nil - self.problemCalendar = [] - self.year = nil - } - -} - -//MARK: GraphQL response maaping initializer -// TODO: MAPPING 변경 -extension User{ -// init(with member: SubmissionMutation.Member){ -// self.nickname = member.nickname -// self.problemCalendar = [] -// self.email = nil -// self.profileImage = nil -// self.username = nil -// self.year = nil -// } -// -// init(me member: Member) { -// self.nickname = member.nickname -// self.problemCalendar = [] -// self.email = member.email -// self.profileImage = member.profileImage -// self.username = member.username -// self.year = nil -// } -} - - - diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/CodeTextView.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/CodeTextView.swift index c296def8..1c8b5563 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/CodeTextView.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/CodeTextView.swift @@ -72,21 +72,7 @@ int main() { private var isFirst: Bool = true - func bind(language: Language, sourceCode: String) { - - // 첫 실행시 비어있을때 default Text로 셋팅 - if isFirst { - if self.text.isEmpty { - self.text = sourceCode - } - isFirst.toggle() - return - } - - self.text = sourceCode - } - - func languageBinding(language: Language) { + func languageBinding(language: LanguageVO) { let storage = (self.textStorage as! CodeAttributedString) if language.name == "Node" || language.name == "Node.js" { diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/ProblemPopUpView.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/ProblemPopUpView.swift index fc5b26a9..6e974bca 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/ProblemPopUpView.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/ProblemPopUpView.swift @@ -10,6 +10,7 @@ import RxCocoa import RxGesture import RxSwift import WebKit +import Data import Global @@ -204,7 +205,7 @@ final class ProblemPopUpView: UIView { let feedbackGenerator = UIImpactFeedbackGenerator(style: .medium) var disposeBag = DisposeBag() - let languageRelay = BehaviorRelay(value: Language.default) + let languageRelay = BehaviorRelay(value: LanguageVO.default) let pageValue = BehaviorRelay(value: .problem) let submissionLoadingWating = BehaviorRelay(value: false) let problemState = PublishRelay() @@ -332,12 +333,12 @@ final class ProblemPopUpView: UIView { sendButton.rx.tap.asDriver() } - func languageAction() -> Driver { + func languageAction() -> Driver { languageRelay.asDriver() } /// 언어 모델 - private var languages: [Language] = [] + private var languages: [LanguageVO] = [] /// 언어 Title Action && languageRelay에 값 전달 -> ViewModel로 데이터 전달 private lazy var setTitleAction: (UIAction) -> () = { [weak self] action in @@ -350,7 +351,7 @@ final class ProblemPopUpView: UIView { /// 언어 설정 하기 위한 함수 /// - Parameter languages: 언어 배열 - func setLangueMenu(languages: [Language]){ + func setLangueMenu(languages: [LanguageVO]){ self.languages = languages let element = languages.map { lan in @@ -531,7 +532,6 @@ extension ProblemPopUpView{ } // TODO: autolayout warnig resultStatusView 의 레이아웃 수정 해야함 - self.popUpContainerView.layoutIfNeeded() } @@ -551,7 +551,8 @@ extension ProblemPopUpView{ submissionListView.snp.remakeConstraints { make in make.top.equalToSuperview() make.leading.trailing.equalToSuperview() - make.bottom.equalToSuperview() + make.height.equalTo(300) // TODO: 고정 상수값 -> 변경 + make.bottom.equalToSuperview().priority(.low) } } diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionListView.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionListView.swift index e3223d03..7595f46a 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionListView.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionListView.swift @@ -48,7 +48,6 @@ final class SubmissionListView: UIView { private func addAutoLayout() { addSubview(tableView) tableView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: self.topAnchor), tableView.leadingAnchor.constraint(equalTo: self.leadingAnchor), diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionResultView.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionResultView.swift index 44ca22ff..e3705e51 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionResultView.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionResultView.swift @@ -11,11 +11,11 @@ import RxSwift class SubmissionResultView: UIView{ - lazy var status = Binder(self) + lazy var status = Binder(self) { target, submission in let propertyLoop = [submission.id, - submission.problem?.title, - submission.member?.nickname ?? submission.member?.username, + submission.problem.title, + submission.member.username, "\(submission.memoryUsage ?? -1)", "\(submission.cpuTime ?? -1)", "\(submission.statusCode ?? "N/A")"] diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewController/CodeEditorViewController.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewController/CodeEditorViewController.swift index e234d084..fc17233f 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewController/CodeEditorViewController.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewController/CodeEditorViewController.swift @@ -171,6 +171,7 @@ class CodeEditorViewController: UIViewController, Stepper { if let loadSourceCode = output?.loadSourceCode { languageAction + .distinctUntilChanged() .withLatestFrom(loadSourceCode) { language, sourceCode in return (language, sourceCode) } @@ -178,6 +179,7 @@ class CodeEditorViewController: UIViewController, Stepper { let (language, sourceCode) = tuple vm.codeUITextView.languageBinding(language: language) if !vm.sourceCodeState { + vm.codeUITextView.text = "" vm.codeUITextView.text = sourceCode } else { vm.sourceCodeState = false @@ -250,7 +252,7 @@ class CodeEditorViewController: UIViewController, Stepper { cell.backgroundColor = .clear cell.contentView.backgroundColor = .clear cell.onHistoryData.accept(submission) - cell.onStatus.accept(submission.statusCode?.convertSolveStatus() ?? .none) + cell.onStatus.accept(submission.statusCode.convertSolveStatus()) }.disposed(by: disposeBag) } } diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/CodeEditorViewModel.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/CodeEditorViewModel.swift index c6fffaea..31c389f5 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/CodeEditorViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/CodeEditorViewModel.swift @@ -27,7 +27,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { var dismissAction: Driver var sendAction: Driver var problemTitle: Driver - var language: Driver + var language: Driver var sourceCode: Driver var submissionID: Driver var submissionListGesture: Driver @@ -36,8 +36,8 @@ final class CodeEditorViewModel: CodeEditorViewModelType { } struct Output { - var solvedResult: Driver - var submissionListResult: Driver<[Submission]> + var solvedResult: Driver + var submissionListResult: Driver<[SubmissionVO]> var submitWaiting: Signal var submissionListWaiting: Signal var problemRefreshResult: Signal @@ -67,7 +67,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { private var disposeBag = DisposeBag() - private var languageRelay = BehaviorRelay(value: Language.default) + private var languageRelay = BehaviorRelay(value: LanguageVO.default) private var sourceCodeRelay = BehaviorRelay(value: "") private var sourceCodeCache: [String: SourceCode] = [:] @@ -75,17 +75,17 @@ final class CodeEditorViewModel: CodeEditorViewModelType { private var sendProblemModel = BehaviorRelay(value: .default) // MARK: Output - private var solvedResult = PublishRelay() + private var solvedResult = PublishRelay() private var submitWaiting = BehaviorRelay(value: false) - private var submissionListResult = BehaviorRelay<[Submission]>(value: []) + private var submissionListResult = BehaviorRelay<[SubmissionVO]>(value: []) private var submissionListWaiting = BehaviorRelay(value: false) private var favoriteProblem = BehaviorRelay(value: false) enum ProblemState { - case fetched(Problem) + case fetched(ProblemVO) case fail(Error) } @@ -116,7 +116,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { sendAction(action: input.sendAction.asSignal(onErrorJustReturn: "")) return Output( - solvedResult: solvedResult.asDriver(onErrorJustReturn: .init(_problem: .init(title: "fail"))), + solvedResult: solvedResult.asDriver(onErrorJustReturn: .sample), submissionListResult: submissionListResult.asDriver(), submitWaiting: submitWaiting.asSignal(onErrorJustReturn: false), submissionListWaiting: submissionListWaiting.asSignal(onErrorJustReturn: false), @@ -159,7 +159,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { return self.sendProblemModel.take(1).asObservable() } .delay(.milliseconds(800), scheduler: MainScheduler.instance) - .flatMap { [weak self] model -> Observable> in + .flatMap { [weak self] model -> Observable> in guard let useCase = self?.submissionUseCase else { return .empty() } return useCase.fetchProblem(id: model.problemID) } @@ -181,12 +181,12 @@ final class CodeEditorViewModel: CodeEditorViewModelType { .withUnretained(self) .flatMap { vm, sendModel in vm.submissionUseCase - .updateSubmissionAction(model: sendModel) + .updateSubmissionAction(model: sendModel.toTempDomain()) .do(onNext: { updateFlag in if let home = vm.homeViewModel, let _ = vm.historyViewModel, updateFlag { - let submission = sendModel.makeTempSubmission() + let submission = sendModel.toTempDomain() home.sendSubmission.accept(submission) } }) @@ -201,11 +201,11 @@ final class CodeEditorViewModel: CodeEditorViewModelType { private func sourceCodeAction(action: Driver) { action - .flatMap {[weak self] sourceCode -> Driver<(Language, SourceCode)> in + .flatMap {[weak self] sourceCode -> Driver<(LanguageVO, SourceCode)> in guard let self else { return .empty() } return Observable .zip(self.languageRelay.take(1), Observable.just(sourceCode)) - .asDriver(onErrorJustReturn: (Language.default, "")) + .asDriver(onErrorJustReturn: (LanguageVO.default, "")) } .drive(with: self, onNext: { vm, tuple in let (language, sourceCode) = tuple @@ -214,10 +214,10 @@ final class CodeEditorViewModel: CodeEditorViewModelType { }).disposed(by: disposeBag) } - private func languageAction(action: Driver) { + private func languageAction(action: Driver) { action .distinctUntilChanged() - .map { [weak self] language -> (Language, String?) in + .map { [weak self] language -> (LanguageVO, String?) in if let sourceCode = self?.sourceCodeCache[language.name] { return (language, sourceCode) } @@ -238,15 +238,15 @@ final class CodeEditorViewModel: CodeEditorViewModelType { .asObservable() .take(1) .flatMap { _ in probelmID.asObservable().take(1) } - .flatMap { [weak self] (id) -> Observable> in + .flatMap { [weak self] (id) -> Observable> in guard let self else { return .just(.success([])) } return self.submissionUseCase.fetchProblemSubmissionHistory(id: id, state: "temp") } .map { result in if case let .success(submissions) = result { return submissions.sorted(by: { s1,s2 in - if let createdAt1 = s1.createdAt?.toDateKST(), - let createdAt2 = s2.createdAt?.toDateKST() { + if let createdAt1 = s1.createdAt.toDateKST(), + let createdAt2 = s2.createdAt.toDateKST() { return createdAt1 > createdAt2 } return false @@ -272,7 +272,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { .do(onNext: { vm, _ in vm.submitWaiting.accept(true) }) .delay(.seconds(1), scheduler: MainScheduler.instance) .do(onError: { [weak self] _ in self?.submitWaiting.accept(false) }) - .flatMap { vm, model in vm.submissionUseCase.submitSubmissionAction(model: model)} + .flatMap { vm, model in vm.submissionUseCase.submitSubmissionAction(model: model.toTempDomain())} .observe(on: MainScheduler.instance) .subscribe(with: self,onNext: { vm, submission in switch submission { @@ -286,7 +286,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { .disposed(by: disposeBag) } - private func loadSourceCodeBy(language: Language) -> String { + private func loadSourceCodeBy(language: LanguageVO) -> String { var name = language.name if language.name == "Node" || language.name == "Node.js" { name = "nodejs" @@ -303,7 +303,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { return strings } - private func viewInteractorAction(_ submission: Submission) { + private func viewInteractorAction(_ submission: SubmissionVO) { self.submitWaiting.accept(false) self.solvedResult.accept(submission) //TODO: 제출 결과 -> HomeViewModel Binding - 전달완료 diff --git a/Projects/CodestackApp/Sources/Model/SendProblemModel.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SendProblemModel.swift similarity index 56% rename from Projects/CodestackApp/Sources/Model/SendProblemModel.swift rename to Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SendProblemModel.swift index b8c6e28f..abac7117 100644 --- a/Projects/CodestackApp/Sources/Model/SendProblemModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SendProblemModel.swift @@ -13,9 +13,10 @@ struct SendProblemModel { let problemID: ID let problemTitle: String let sourceCode: String - let language: Language + let language: LanguageVO + let userName: String = "" - init(submissionID: ID, problemID: ID, problemTitle: String = "", sourceCode: String, language: Language) { + init(submissionID: ID, problemID: ID, problemTitle: String = "", sourceCode: String, language: LanguageVO) { self.submissionID = submissionID self.problemID = problemID self.problemTitle = problemTitle @@ -23,7 +24,7 @@ struct SendProblemModel { self.language = language } - init(tuple: (Language, String, SourceCode, ProblemID, SubmissionID)){ + init(tuple: (LanguageVO, String, SourceCode, ProblemID, SubmissionID)){ self.language = tuple.0 self.problemTitle = tuple.1 self.sourceCode = tuple.2 @@ -38,7 +39,7 @@ extension SendProblemModel { problemID: "", problemTitle: "", sourceCode: "", - language: Language.default) + language: LanguageVO.default) } func makeFavoirtProblem() -> FavoriteProblem { @@ -47,16 +48,20 @@ extension SendProblemModel { createdAt: Date()) } - func makeTempSubmission() -> Submission { - let submissionID = self.submissionID.isEmpty ? UUID().uuidString : self.submissionID - return Submission(id: submissionID, - sourceCode: self.sourceCode, - problem: .init(id: self.problemID, title: self.problemTitle), - member: .init(), - language: self.language, - cpuTime: 0, - memoryUsage: 0, - statusCode: "temp", - createdAt: Date().toString()) + func toProblemIdentity() -> ProblemIdentityVO { + ProblemIdentityVO(id: self.problemID, title: self.problemTitle) } + + func toTempDomain() -> SubmissionVO { + let submissionID = self.submissionID.isEmpty ? UUID().uuidString : self.submissionID + return SubmissionVO.init(id: submissionID, + sourceCode: self.sourceCode, + problem: self.toProblemIdentity(), + member: .init(username: self.userName), + language: self.language, + cpuTime: 0, + memoryUsage: 0, + statusCode: "temp", + createdAt: Date().toString()) + } } diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SolvePageType.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SolvePageType.swift index 27ad31ad..d9ceb902 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SolvePageType.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SolvePageType.swift @@ -10,8 +10,8 @@ import Foundation enum SolveResultType { case problem - case result(Submission?) - case resultList([Submission]) + case result(SubmissionVO?) + case resultList([SubmissionVO]) var value: String { switch self { diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/GraphCell/View/LanguageTagContainer.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/GraphCell/View/LanguageTagContainer.swift index bc83dcd0..7cd840be 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/GraphCell/View/LanguageTagContainer.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/GraphCell/View/LanguageTagContainer.swift @@ -79,7 +79,7 @@ class LanguageTagContainer: UIView { invalidateIntrinsicContentSize() } - func setLanguage(_ language: [Language]){ + func setLanguage(_ language: [LanguageVO]){ fetures = language.map{$0.name} feturesUpdate() } diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/ProblemCell.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/ProblemCell.swift index c1550d12..26dc9291 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/ProblemCell.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/ProblemCell.swift @@ -85,7 +85,7 @@ class ProblemCell: UITableViewCell{ return view }() - private var languages: [Language]? { + private var languages: [LanguageVO]? { didSet{ if let languages{ self.langugeContainer.setLanguage(languages) @@ -122,9 +122,9 @@ class ProblemCell: UITableViewCell{ cell.problem_number.setTitle("\(model.problemNumber)", for: .normal) cell.problem_title.text = "\(model.problemTitle)" cell.model = model - cell.languages = language + model.tags.map{ tag in Language(id: "none", + cell.languages = language + model.tags.map{ tag in LanguageVO(id: "none", name: tag.name , - _extension: "none")} + extension: "none")} cell.graphSubmit.bind(.init(submitCount: String(describing: model.submitCount), correctAnswer: String(describing: model.correctAnswer), diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/DummyData.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/DummyData.swift index 8af72011..fcc192c9 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/DummyData.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/DummyData.swift @@ -7,7 +7,7 @@ import Foundation -typealias DummyModel = (model: ProblemListItemModel, language: [Language], flag: Bool) +typealias DummyModel = (model: ProblemListItemModel, language: [LanguageVO], flag: Bool) struct DummyData{ @@ -17,7 +17,7 @@ struct DummyData{ submitCount: Int.random(in: 10...134), correctAnswer: Int.random(in: 10...134), correctRate: Double.random(in: 0...100)) - return (model, Language.sample, true) + return (model, LanguageVO.sample, true) } func getAllModels(limit: Int = 15, index: Int = 0) -> [DummyModel]{ diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/CodeProblemViewModel.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/CodeProblemViewModel.swift index 73f3f7e9..ea305ab2 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/CodeProblemViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/CodeProblemViewModel.swift @@ -9,6 +9,7 @@ import Foundation import RxSwift import RxCocoa import RxFlow +import Data protocol ProblemViewModelProtocol{ associatedtype Input = CodeProblemViewModel.Input @@ -159,6 +160,7 @@ class CodeProblemViewModel: ProblemViewModelProtocol,Stepper { .flatMap { vm, _ in vm.requestProblem(offset: vm.currentPage) } .emit(with: self, onNext: { vm, problems in let dummyModel = problems.map { problem in + let list1 = problem.toProblemList(.sample) let list = problem.toProblemList() return (model: list,language: problem.languages, flag: false) } @@ -205,10 +207,14 @@ class CodeProblemViewModel: ProblemViewModelProtocol,Stepper { private func requestProblem(offset: Int, sort: String = "id", - order: String = "asc") -> Signal<[Problem]> { + order: String = "asc") -> Signal<[ProblemVO]> { apollo // .getProblemsQuery(query: Query.getProblems(offset: offset, sort: sort, order: order)) - .getProblemsQuery(.) + //TODO: 확인후 변경 + .getProblemsQuery(.PR_LIST(arg: GRAR(offset: offset))) + .map { frInfo in + frInfo.0.map { fr in fr.toDomain() } + } .asSignal(onErrorJustReturn: []) } } diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/ProblemItemModel.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/ProblemItemModel.swift index 8f929d40..36881b2a 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/ProblemItemModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/ProblemItemModel.swift @@ -42,9 +42,9 @@ var contextExample = """ -struct ProblemListItemModel: Equatable{ +struct ProblemListItemModel: Equatable { var submissionID: String? - var seletedLanguage: Language? + var seletedLanguage: LanguageVO? var sourceCode: String? var problemNumber: String var problemTitle: String @@ -52,16 +52,16 @@ struct ProblemListItemModel: Equatable{ var correctAnswer: Int var correctRate: Double var contenxt: String? - var tags: [Tag] - var language: [Language] + var tags: [TagVO] + var language: [LanguageVO] init(problemNumber: String, problemTitle: String, submitCount: Int, correctAnswer: Int, correctRate: Double, - tags: [Tag] = [], - languages: [Language] = []) { + tags: [TagVO] = [], + languages: [LanguageVO] = []) { self.submissionID = nil self.problemNumber = problemNumber self.problemTitle = problemTitle @@ -71,12 +71,12 @@ struct ProblemListItemModel: Equatable{ self.tags = [] self.language = ["C", "JAVA" , "C++", "Node.js", "Swift", "Kot", "Go", "Python", "javascript"] - .map{ name in Language(id: "1", name: name, _extension: "")} + .map{ name in LanguageVO(id: "1", name: name, extension: "")} self.contenxt = contextExample } init(submissionID: String? = nil, - seletedLanguage: Language?, + seletedLanguage: LanguageVO?, sourceCode: String?, problemNumber: String, problemTitle: String, @@ -84,8 +84,8 @@ struct ProblemListItemModel: Equatable{ correctAnswer: Int, correctRate: Double, contenxt: String, - tags: [Tag] = [], - languages: [Language] = []) + tags: [TagVO] = [], + languages: [LanguageVO] = []) { self.submissionID = submissionID self.seletedLanguage = seletedLanguage diff --git a/Projects/CodestackApp/Sources/Presentation/History/View/HistoryCell.swift b/Projects/CodestackApp/Sources/Presentation/History/View/HistoryCell.swift index eba909d6..9c6f8aef 100644 --- a/Projects/CodestackApp/Sources/Presentation/History/View/HistoryCell.swift +++ b/Projects/CodestackApp/Sources/Presentation/History/View/HistoryCell.swift @@ -53,7 +53,7 @@ class HistoryCell: UITableViewCell { return button }() - var onHistoryData = PublishRelay() + var onHistoryData = PublishRelay() var onStatus = PublishRelay() var cellDisposeBag = DisposeBag() @@ -64,24 +64,24 @@ class HistoryCell: UITableViewCell { layoutConfigure() - Driver.zip(onHistoryData.asDriver(onErrorJustReturn: .init(_problem: Problem(title: "error"))), + Driver.zip(onHistoryData.asDriver(onErrorJustReturn: .sample), onStatus.asDriver(onErrorJustReturn: .none)) .drive(with: self,onNext: { cell, data in let (submission, solveStatus) = data cell.statusLabel.pr_status_label(solveStatus, default: false) cell.titleBoxContainerSetting(status: solveStatus) - cell.descriptionSetting(status: solveStatus, problem: submission.problem?.title ?? "hellow world ") - cell.problemName.text = submission.problem?.title - cell.languageBtn.setTitle(submission.language?.name ?? "N/A", for: .disabled) + cell.descriptionSetting(status: solveStatus, problem: submission.problem.title ) + cell.problemName.text = submission.problem.title + cell.languageBtn.setTitle(submission.language.name , for: .disabled) - if let date = submission.createdAt?.toDateStringUTC(format: .FULL) { + if let date = submission.createdAt.toDateStringUTC(format: .FULL) { let dateString = DateCalculator().caluculateTime(date) cell.timeLabel.text = dateString return } - if let date = submission.createdAt?.toDateStringKST(format: .FULL) { + if let date = submission.createdAt.toDateStringKST(format: .FULL) { let dateString = DateCalculator().caluculateTime(date) cell.timeLabel.text = dateString return diff --git a/Projects/CodestackApp/Sources/Presentation/History/View/HistoryViewController.swift b/Projects/CodestackApp/Sources/Presentation/History/View/HistoryViewController.swift index 9aa12fd2..b5b5390b 100644 --- a/Projects/CodestackApp/Sources/Presentation/History/View/HistoryViewController.swift +++ b/Projects/CodestackApp/Sources/Presentation/History/View/HistoryViewController.swift @@ -120,7 +120,7 @@ class HistoryViewController: UIViewController, UITableViewDelegate { cell.selectionStyle = .none cell.separatorInset = UIEdgeInsets.zero cell.onHistoryData.accept(submission) - cell.onStatus.accept(submission.statusCode?.convertSolveStatus() ?? .none) + cell.onStatus.accept(submission.statusCode.convertSolveStatus() ) }.disposed(by: disposeBag) diff --git a/Projects/CodestackApp/Sources/Presentation/History/ViewModel/HistoryViewModel.swift b/Projects/CodestackApp/Sources/Presentation/History/ViewModel/HistoryViewModel.swift index eb840952..a664051e 100644 --- a/Projects/CodestackApp/Sources/Presentation/History/ViewModel/HistoryViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/History/ViewModel/HistoryViewModel.swift @@ -10,9 +10,10 @@ import RxSwift import RxCocoa import RxRelay import Global +import Data protocol HistoryViewModelType: ViewModelType, AnyObject { - var sendSubmission: PublishRelay { get set } + var sendSubmission: PublishRelay { get set } } class HistoryViewModel: HistoryViewModelType { @@ -26,7 +27,7 @@ class HistoryViewModel: HistoryViewModelType { } struct Output{ - var historyData: Driver<[Submission]> + var historyData: Driver<[SubmissionVO]> var paginationRefreshEndEvent: Signal var paginationLoading: Driver var refreshClearEvent: Signal @@ -46,8 +47,8 @@ class HistoryViewModel: HistoryViewModelType { } private var disposeBag = DisposeBag() - private let submissionModel = BehaviorRelay<[Submission]>(value: []) - private let historyData = BehaviorRelay<[Submission]>(value: []) + private let submissionModel = BehaviorRelay<[SubmissionVO]>(value: []) + private let historyData = BehaviorRelay<[SubmissionVO]>(value: []) // private let favoriteModel @@ -63,7 +64,7 @@ class HistoryViewModel: HistoryViewModelType { //Navigation Top right refresh private let refreshClearEvent = PublishRelay() - var sendSubmission = PublishRelay() + var sendSubmission = PublishRelay() func transform(input: Input) -> Output { @@ -87,10 +88,10 @@ class HistoryViewModel: HistoryViewModelType { let filteredHistoryData = - Driver<[Submission]>.combineLatest(historyData.asDriver(), + Driver<[SubmissionVO]>.combineLatest(historyData.asDriver(), input.currentSegment) { submissions, segment in - let filteredSubmissions = submissions.filter{ $0.statusCode?.checkIsEqual(with: segment) ?? false} + let filteredSubmissions = submissions.filter{ $0.statusCode.checkIsEqual(with: segment) } return filteredSubmissions } @@ -132,7 +133,7 @@ class HistoryViewModel: HistoryViewModelType { .compactMap { try $0.get() } let fetchedSubmission = vm.requestSubmission().do(onNext: { _ in vm.currentPage += 1 }) - return Observable<[Submission]>.concat([localTempSubmission, fetchedSubmission]) + return Observable<[SubmissionVO]>.concat([localTempSubmission, fetchedSubmission]) } .subscribe(with: self, onNext: { vm, data in @@ -191,10 +192,14 @@ class HistoryViewModel: HistoryViewModelType { } } - private func requestSubmission() -> Observable<[Submission]> { + private func requestSubmission() -> Observable<[SubmissionVO]> { return service - .getSubmission(query: Query.getSubmission(offest: self.currentPage), - cache: nil) + .getSubmission(.SUB_LIST(arg: GRAR.init(offset: self.currentPage)), cache: nil) + .map { frinfo in + frinfo.0.map { fr in fr.toDomain() } + } +// .getSubmission(query: Query.getSubmission(offest: self.currentPage), +// cache: nil) .asObservable() } @@ -213,7 +218,9 @@ class HistoryViewModel: HistoryViewModelType { // .subscribe(with: self, onSuccess: { vm , me in // Log.debug(me) // }) - _ = service.getSubmission(query: Query.getSubmission(),cache: .fetchIgnoringCacheCompletely) +// _ = service.getSubmission(query: Query.getSubmission(),cache: .fetchIgnoringCacheCompletely) + _ = service.getMeSubmissions(.SUB_LIST(arg: GRAR.init(offset: 0))) + .map { $0.map { fr in fr.toDomain() }} .subscribe(with: self, onSuccess: { vm, value in vm.submissionModel.accept([]) vm.historyData.accept([]) diff --git a/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewModel/LoginViewModel.swift b/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewModel/LoginViewModel.swift index 50d8ca8e..1776dc72 100644 --- a/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewModel/LoginViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewModel/LoginViewModel.swift @@ -114,7 +114,7 @@ class LoginViewModel: LoginViewModelProtocol, Stepper{ .do(onNext: { [weak self] _ in self?.loginLoadingEvent.accept(.success(false)) }) .do(onError: { [weak self] _ in self?.loginLoadingEvent.accept(.failure(LoginError.timeOut)) }) .flatMap { token in self.saveToken(token: token) } - .subscribe(with: self,onSuccess: { owner, user in owner.saveUserInfo(user: user) }, + .subscribe(with: self,onSuccess: { owner, user in owner.saveUserInfo(memeber: user) }, onError: { owner , err in Log.error(err) }) } @@ -127,7 +127,7 @@ class LoginViewModel: LoginViewModelProtocol, Stepper{ .do(onNext: { [weak self] _ in self?.loginLoadingEvent.accept(.success(false))}) .do(onError: { [weak self] err in self?.loginLoadingEvent.accept(.failure(LoginError.timeOut)) }) .flatMap { token in self.saveToken(token: token) } - .subscribe(with: self,onSuccess: { owner, user in owner.saveUserInfo(user: user)}, + .subscribe(with: self,onSuccess: { owner, user in owner.saveUserInfo(memeber: user)}, onError: { owner , err in Log.error(err) }) } @@ -139,25 +139,23 @@ class LoginViewModel: LoginViewModelProtocol, Stepper{ .flatMap { token in Log.debug(KeychainItem.currentAccessToken) return self.saveToken(token: token) } - .subscribe(with: self,onSuccess: { owner, user in owner.saveUserInfo(user: user) }, + .subscribe(with: self,onSuccess: { owner, user in owner.saveUserInfo(memeber: user) }, onError: { owner , err in Log.error(err) }) } - private func saveToken(token: CSTokenDTO) -> Maybe { + private func saveToken(token: CSTokenDTO) -> Maybe { UserManager.shared.saveTokenInfo(with: TokenInfo(expiresIn: token.expiresIn, tokenType: token.tokenType)) KeychainItem.saveTokens(access: token.accessToken, refresh: token.refreshToken) return apolloService.getMe(.ME) - .map { fr in - - } + .map { fr in fr.toDomain() } } - private func saveUserInfo(user: User){ - UserManager.shared.saveUser(with: user) + private func saveUserInfo(memeber: MemberVO){ + UserManager.shared.saveUser(with: memeber) steps.accept(CodestackStep.userLoggedIn(nil, nil)) } } diff --git a/Projects/CodestackApp/Sources/Presentation/Main/HomeView/CollectionViewCell/PRSubmissionHistoryCell.swift b/Projects/CodestackApp/Sources/Presentation/Main/HomeView/CollectionViewCell/PRSubmissionHistoryCell.swift index 96d71eac..6d6f15c5 100644 --- a/Projects/CodestackApp/Sources/Presentation/Main/HomeView/CollectionViewCell/PRSubmissionHistoryCell.swift +++ b/Projects/CodestackApp/Sources/Presentation/Main/HomeView/CollectionViewCell/PRSubmissionHistoryCell.swift @@ -46,7 +46,7 @@ final class PRSubmissionHistoryCell: UICollectionViewCell { }() - var onRecentPageData = PublishRelay() + var onRecentPageData = PublishRelay() var onStatus = PublishRelay() var cellDisposeBag = DisposeBag() @@ -60,12 +60,12 @@ final class PRSubmissionHistoryCell: UICollectionViewCell { addAutoLayout() onRecentPageData - .asDriver(onErrorJustReturn: .init(_problem: Problem(title: ""))) + .asDriver(onErrorJustReturn: .sample) .drive(onNext: {[weak self] submission in guard let self = self else {return} - self.titleLabel.text = submission.problem?.title + self.titleLabel.text = submission.problem.title - if let solvedDate = submission.createdAt?.toDateStringKST(format: .FULL){ + if let solvedDate = submission.createdAt.toDateStringKST(format: .FULL){ let dateString = DateCalculator().caluculateTime(solvedDate) self.dateLabel.text = dateString } diff --git a/Projects/CodestackApp/Sources/Presentation/Main/ViewController/ViewController.swift b/Projects/CodestackApp/Sources/Presentation/Main/ViewController/ViewController.swift index 9d2b2dfd..7b6ee60f 100644 --- a/Projects/CodestackApp/Sources/Presentation/Main/ViewController/ViewController.swift +++ b/Projects/CodestackApp/Sources/Presentation/Main/ViewController/ViewController.swift @@ -166,7 +166,7 @@ class ViewController: UIViewController{ let recentModelSelected = recentPagesCollectionView.rx - .modelSelected(Submission.self) + .modelSelected(SubmissionVO.self) .asSignal() .throttle(.seconds(1), latest: false) .map { print("touch value"); return $0 } @@ -192,7 +192,7 @@ class ViewController: UIViewController{ guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PRSubmissionHistoryCell.identifier, for: indexPath) as? PRSubmissionHistoryCell else {return UICollectionViewCell()} cell.onRecentPageData.accept(item) - cell.onStatus.accept(item.statusCode?.convertSolveStatus() ?? .none) + cell.onStatus.accept(item.statusCode.convertSolveStatus()) return cell }, configureSupplementaryView: diff --git a/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/HomeViewModel.swift b/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/HomeViewModel.swift index 51ff2d41..74a70ea8 100644 --- a/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/HomeViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/HomeViewModel.swift @@ -16,7 +16,7 @@ import Data protocol HomeViewModelType: ViewModelType, Stepper, AnyObject { - var sendSubmission: PublishRelay { get set } + var sendSubmission: PublishRelay { get set } } typealias SourceCode = String @@ -29,7 +29,7 @@ final class HomeViewModel: HomeViewModelType { var rightSwipeGesture: Observable var leftSwipeGesture: Observable var leftNavigationButtonEvent: Signal - var recentModelSelected: Signal + var recentModelSelected: Signal var emptyDataButton: Signal var alramTapped: Observable } @@ -46,8 +46,8 @@ final class HomeViewModel: HomeViewModelType { private var repository: DBRepository //MARK: - Input - var sendSubmission = PublishRelay() - var updateSubmission = PublishRelay() + var sendSubmission = PublishRelay() + var updateSubmission = PublishRelay() //MARK: - output private var submissions = PublishRelay<[RecentSubmission]>() @@ -75,7 +75,7 @@ final class HomeViewModel: HomeViewModelType { private func updateSubmissionBinding() { updateSubmission .map { $0 } - .withLatestFrom(submissions) { [weak self] (updatedSubmission, subs) -> [Submission] in + .withLatestFrom(submissions) { [weak self] (updatedSubmission, subs) -> [SubmissionVO] in guard let self else { return [] } let originalSubmissions = subs.flatMap { $0.items } return self.updateSubmissionList(will: updatedSubmission, original: originalSubmissions) @@ -158,8 +158,8 @@ final class HomeViewModel: HomeViewModelType { } .compactMap { submission in // TODO: 확인후 변경 바람º - let _submission: Submission = submission - return _submission.problem?.toProblemList(submission) + let _submission: SubmissionVO = submission + return _submission.problemVO.toProblemList(submission) } .map { CodestackStep.recentSolveList($0)} .emit(to: steps) @@ -168,35 +168,22 @@ final class HomeViewModel: HomeViewModelType { return Output(submissions: self.submissions.asDriver(onErrorJustReturn: [])) } - func fetchProblem(using submission: Submission) -> Signal { + func fetchProblem(using submission: SubmissionVO) -> Signal { service - .request(type: ProblemFR.self, graph: .PR_BY_ID(submission.problem!.id)) + .getProblemByID(.PR_BY_ID(submission.problem.id)) .map { prFR in - let problem = prFR.toDomain() + let problemVO = prFR.toDomain() + var submission = submission + submission.problemVO = problemVO + return submission } .asSignal(onErrorRecover: { error in Log.error("\(error)") return .just(SubmissionVO.sample) }) -// .asSignal(onErrorRecover: { error in -// -// }) -// .getProblemByID(query: GetProblemByIdQuery(id: submission.problem!.id)) -// .map { value in Submission(_problem: value) } -// .map { sub in -// var fetchedSubmission = sub -// fetchedSubmission.id = submission.id -// fetchedSubmission.sourceCode = submission.sourceCode -// fetchedSubmission.language = submission.language -// return fetchedSubmission -// } -// .asSignal(onErrorRecover: { error in -// var submission = submission -// return .just(submission.ifNetworkErorr()) -// }) } - func fetchedSubmission() -> Signal<[Submission]> { + func fetchedSubmission() -> Signal<[SubmissionVO]> { repository .fetchProblemState() .map { problemState in @@ -204,8 +191,8 @@ final class HomeViewModel: HomeViewModelType { return state .submissions .sorted(by: { s1,s2 in - if let createdAt1 = s1.createdAt?.toDateKST(), - let createdAt2 = s2.createdAt?.toDateKST() { + if let createdAt1 = s1.createdAt.toDateKST(), + let createdAt2 = s2.createdAt.toDateKST() { return createdAt1 > createdAt2 } return false @@ -217,25 +204,21 @@ final class HomeViewModel: HomeViewModelType { .asSignal(onErrorJustReturn: []) } - private func updateSubmissionList(will updateSubmission: Submission, - original submissions: [Submission]) -> [Submission] { - guard - let id = updateSubmission.problem?.id - else { - return submissions - } + private func updateSubmissionList(will updateSubmission: SubmissionVO, + original submissions: [SubmissionVO]) -> [SubmissionVO] { + let id = updateSubmission.problem.id - let originalIDs = submissions.compactMap(\.problem?.id) + let originalIDs = submissions.map(\.problem.id) let flag = originalIDs.contains(id) if flag { - let newSubmissions: [Submission] = submissions.map { - $0.problem?.id == updateSubmission.problem?.id ? updateSubmission : $0 + let newSubmissions: [SubmissionVO] = submissions.map { + $0.problem.id == updateSubmission.problem.id ? updateSubmission : $0 } - let sortedSubmissions: [Submission] = newSubmissions.sorted(by: { first, second in - if let date1 = first.createdAt?.toDateKST(), - let date2 = second.createdAt?.toDateKST() { + let sortedSubmissions: [SubmissionVO] = newSubmissions.sorted(by: { first, second in + if let date1 = first.createdAt.toDateKST(), + let date2 = second.createdAt.toDateKST() { return date1 > date2 } return true diff --git a/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/RecentPageModel.swift b/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/RecentPageModel.swift index 50d518cd..593b6c56 100644 --- a/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/RecentPageModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/RecentPageModel.swift @@ -10,17 +10,24 @@ import RxDataSources //typealias RecentSubmissionSection = SectionModel -extension Submission: IdentifiableType, Equatable{ - static func == (lhs: Submission, rhs: Submission) -> Bool { - lhs.id == rhs.id - } - - var identity: some Hashable{ +// MARK: 확인후 제거 +//extension Submission: IdentifiableType, Equatable{ +// static func == (lhs: Submission, rhs: Submission) -> Bool { +// lhs.id == rhs.id +// } +// +// var identity: some Hashable{ +// return self.id +// } +//} + +extension SubmissionVO: IdentifiableType { + public var identity: some Hashable { return self.id } } -extension Array where Element == Submission { +extension Array where Element == SubmissionVO { func toRecentModels(title: String = "최근 제출 10개") -> [RecentSubmission] { return [RecentSubmission(headerTitle: title, items: self)] } @@ -32,7 +39,7 @@ struct RecentSubmission { } extension RecentSubmission: AnimatableSectionModelType { - typealias Item = Submission + typealias Item = SubmissionVO var identity: String { return headerTitle diff --git a/Projects/CodestackApp/Sources/Presentation/MyPage/View/ProfileView.swift b/Projects/CodestackApp/Sources/Presentation/MyPage/View/ProfileView.swift index 9f0512bf..62749e06 100644 --- a/Projects/CodestackApp/Sources/Presentation/MyPage/View/ProfileView.swift +++ b/Projects/CodestackApp/Sources/Presentation/MyPage/View/ProfileView.swift @@ -75,12 +75,14 @@ class ProfileView: UIView{ var disposeBag = DisposeBag() /// Binder 뷰에 프로필 데이터 바인딩 - var profileBinder: Binder { + var profileBinder: Binder { Binder(self){ [weak self] target ,value in guard let self else { return } - self.nameLabel.text = value.username ?? "Unknown" - self.rank.text = value.rank ?? "N/A" + self.nameLabel.text = value.username // ?? "Unknown" + + // TODO: Rank 어떻게 해야돼ㅣㅁ? + self.rank.text = "N/A" } } diff --git a/Projects/CodestackApp/Sources/Presentation/MyPage/ViewModel/MyPageViewModel.swift b/Projects/CodestackApp/Sources/Presentation/MyPage/ViewModel/MyPageViewModel.swift index 5ed55e8f..10c157cb 100644 --- a/Projects/CodestackApp/Sources/Presentation/MyPage/ViewModel/MyPageViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/MyPage/ViewModel/MyPageViewModel.swift @@ -23,7 +23,7 @@ class MyPageViewModel: ViewModelType, Stepper{ } struct Output{ - var userProfile: Driver + var userProfile: Driver var loading: Driver } @@ -43,7 +43,7 @@ class MyPageViewModel: ViewModelType, Stepper{ self.apolloService = dependency.apolloService } - private let userProfile = BehaviorRelay(value: UserManager.shared.profile) + private let userProfile = BehaviorRelay(value: UserManager.shared.profile) func transform(input: Input) -> Output { @@ -60,10 +60,11 @@ class MyPageViewModel: ViewModelType, Stepper{ .asObservable() .observe(on: ConcurrentDispatchQueueScheduler(qos: .background)) .withUnretained(self) - .flatMap{ vm, _ in vm.apolloService.getMe(query: Query.getMe()).asObservable() } - .subscribe(with: self, onNext: { vm, user in - vm.userProfile.accept(user) - loading.onNext(.loaded(user.profileImage)) + .flatMap{ vm, _ in vm.apolloService.getMe(.ME) } + .map { $0.toDomain() } + .subscribe(with: self, onNext: { vm, member in + vm.userProfile.accept(member) + loading.onNext(.loaded(member.profileImage)) },onError: { vm, err in loading.onNext(.loaded(nil)) }).disposed(by: disposeBag) diff --git a/Projects/Data/Sources/Network/API/GraphQLAPI/DefaultGraphQLAPI.swift b/Projects/Data/Sources/Network/API/GraphQLAPI/DefaultGraphQLAPI.swift index 727cf1e9..7821c27a 100644 --- a/Projects/Data/Sources/Network/API/GraphQLAPI/DefaultGraphQLAPI.swift +++ b/Projects/Data/Sources/Network/API/GraphQLAPI/DefaultGraphQLAPI.swift @@ -14,24 +14,23 @@ import CodestackAPI public final class DefaultGraphQLAPI { public struct Dependency { - var tokenService: TokenAcquisitionService var baseURL: URL + let configuration: URLSessionConfiguration - public init(tokenService: TokenAcquisitionService, baseURL: URL) { - self.tokenService = tokenService + public init(baseURL: URL, + configuration: URLSessionConfiguration) { self.baseURL = baseURL + self.configuration = configuration } } - private var tokenService: TokenAcquisitionService? private let client: ApolloClient private var disposeBag = DisposeBag() private let store = ApolloStore(cache: InMemoryNormalizedCache()) - public init(base url: URL, session client: URLSessionClient) { - + private init(base url: URL, session client: URLSessionClient) { self.client = ApolloClient( networkTransport: RequestChainNetworkTransport( interceptorProvider: NetworkInterceptorProvider( @@ -46,14 +45,11 @@ public final class DefaultGraphQLAPI { } public convenience init(dependency: Dependency) { - - let configuration: URLSessionConfiguration = .default - let sessionClient = URLSessionClient( - sessionConfiguration: configuration, + sessionConfiguration: dependency.configuration, callbackQueue: .main ) - + self.init(base: dependency.baseURL,session: sessionClient) } } diff --git a/Projects/Data/Sources/Network/API/GraphQLAPI/RxApollo.swift b/Projects/Data/Sources/Network/API/GraphQLAPI/RxApollo.swift index 35b82c19..6d8cccc1 100644 --- a/Projects/Data/Sources/Network/API/GraphQLAPI/RxApollo.swift +++ b/Projects/Data/Sources/Network/API/GraphQLAPI/RxApollo.swift @@ -46,6 +46,7 @@ extension Reactive where Base: ApolloClientProtocol { switch result { case let .success(gqlResult): if let errors = gqlResult.errors { + Log.debug("error: \(errors)") observer(.error(ApolloError.gqlErrors(errors))) } else if let data = gqlResult.data { observer(.success(data)) diff --git a/Projects/Data/Sources/Network/API/Provider/API.swift b/Projects/Data/Sources/Network/API/Provider/API.swift index 8ff71128..aaf8c535 100644 --- a/Projects/Data/Sources/Network/API/Provider/API.swift +++ b/Projects/Data/Sources/Network/API/Provider/API.swift @@ -40,8 +40,10 @@ extension API { public static func GRAPH_MUTATION(path: GRAPH) -> any GraphQLMutation { switch path { - case .SUBMIT_SUB(let languageID, let problemID, let sourceCode): - return SubmitSubmissionMutation(languageId: languageID, problemId: problemID, sourceCode: sourceCode) + case .SUBMIT_SUB(let grsubmit): + return SubmitSubmissionMutation(languageId: grsubmit.languageId, + problemId: grsubmit.problemId, + sourceCode: grsubmit.sourceCode) case .UPDATE_NICKNAME(let nickname): return UpdateNickNameMutation(nickname: nickname) @@ -54,17 +56,17 @@ extension API { switch path { case .PR_BY_ID(let id): return FetchProblemByIdQuery.init(id: id) - case .PR_LIST(let offset, let sort, let order): - return FetchProblemsQuery.init(offset: .init(integerLiteral: offset), - sort: .init(stringLiteral: sort), - order: .init(stringLiteral: order)) + case .PR_LIST(let grar): + return FetchProblemsQuery.init(offset: .init(integerLiteral: grar.offset), + sort: .init(stringLiteral: grar.sort), + order: .init(stringLiteral: grar.order)) - case .SER_PR_LIST(let keyword, let limit, let order, let sort): - return FetchSearchproblemsQuery.init(keyword: keyword, limit: limit, order: order, sort: sort) - case .SUB_LIST(let offset, let sort, let order): - return FetchSubmissionsQuery.init(offset: .init(integerLiteral: offset), - sort: .init(stringLiteral: sort), - order: .init(stringLiteral: order)) + case .SER_PR_LIST(let keyword, let grar): + return FetchSearchproblemsQuery.init(keyword: keyword, limit: grar.limit, order: grar.order, sort: grar.sort) + case .SUB_LIST(let grar): + return FetchSubmissionsQuery.init(offset: .init(integerLiteral: grar.offset), + sort: .init(stringLiteral: grar.sort), + order: .init(stringLiteral: grar.order)) case .SUB_BY_PR_ID(let offset, let problemID): return FetchSubmissionByProblemIdQuery.init(offset: offset, problemId: problemID) case .SUB_BY_ID(let id): diff --git a/Projects/Data/Sources/Network/API/Provider/GRAPH.swift b/Projects/Data/Sources/Network/API/Provider/GRAPH.swift index 7a8e265a..f17da79b 100644 --- a/Projects/Data/Sources/Network/API/Provider/GRAPH.swift +++ b/Projects/Data/Sources/Network/API/Provider/GRAPH.swift @@ -8,23 +8,67 @@ import Foundation + public enum GRAPH { - case PR_BY_ID(ID) // 특정 ID의 문제 - case PR_LIST(offset: Int, sort: String, order: String) // 문제 리스트 - /// dfasf - case SER_PR_LIST(keyword: String, limit: Int, order: String, sort: String) // 문제 검색 리스트 - case SUB_LIST(offset: Int, sort: String, order: String) // 제출 리스트 - case SUB_BY_PR_ID(offset: Int, problemId: Double) // 제출 By 문제 아이디 - case SUB_BY_ID(id: ID) // 제출 By ID - case SUB_LIST_ME(username: String) // 나의 제출 리스트 - case ME_SUB_HISTORY(limit: Int, offset: Int) // 나의 제출 기록 (날짜와 아이디만) - case ME // 멤버 조회 + /// 특정 ID의 문제 + case PR_BY_ID(ID) + /// 문제 리스트 + case PR_LIST(arg: GRAR) + + /// 문제 검색 리스트 + case SER_PR_LIST(keyword: String, arg: GRAR) + + /// 제출 리스트 + case SUB_LIST(arg: GRAR) + /// 제출 By 문제 아이디 + case SUB_BY_PR_ID(offset: Int, problemId: Double) + /// 제출 By ID + case SUB_BY_ID(id: ID) + /// 나의 제출 리스트/ + case SUB_LIST_ME(username: String) + /// 나의 제출 기록 (날짜와 아이디만) + case ME_SUB_HISTORY(limit: Int, offset: Int) + /// 멤버 조회 + case ME - case SOLVE_PR_LIST(username: String) // solve한 문제 리스트 - case LANG_LIST(offset: Int) // 언어 리스트 - case TAG_LIST // Tag 리스트 + /// solve한 문제 리스트 + case SOLVE_PR_LIST(username: String) + /// 언어 리스트/ + case LANG_LIST(offset: Int) + /// Tag 리스트 + case TAG_LIST + /// 닉네임 업데이트/ + case UPDATE_NICKNAME(nickname: String) + /// 코드 제출하기/ + case SUBMIT_SUB(submit: GRSubmit) +} + + +/// Graph Argument +public struct GRAR { + public let limit: Int + public let offset: Int + public let sort: String + public let order: String + + public init(limit: Int = 20, offset: Int, sort: String = "id", order: String = "asc") { + self.limit = limit + self.offset = offset + self.sort = sort + self.order = order + } +} + +/// Graph Argument +public struct GRSubmit { + public let languageId: ID + public let problemId: ID + public let sourceCode: String - case UPDATE_NICKNAME(nickname: String) // 닉네임 업데이트 - case SUBMIT_SUB(languageId: ID, problemId: ID,sourceCode: String) // 코드 제출하기 + public init(languageId: ID, problemId: ID, sourceCode: String) { + self.languageId = languageId + self.problemId = problemId + self.sourceCode = sourceCode + } } diff --git a/Projects/Data/Sources/Network/API/RestAPI/DefaultRestAPI.swift b/Projects/Data/Sources/Network/API/RestAPI/DefaultRestAPI.swift index 7d042a06..bfe1857b 100644 --- a/Projects/Data/Sources/Network/API/RestAPI/DefaultRestAPI.swift +++ b/Projects/Data/Sources/Network/API/RestAPI/DefaultRestAPI.swift @@ -25,7 +25,7 @@ public final class DefaultRestAPI { public var initialToken = RefreshToken(refresh: KeychainItem.currentRefreshToken) - private lazy var tokenAcquisionService: TokenAcquisitionService + lazy var tokenAcquisionService: TokenAcquisitionService = TokenAcquisitionService(initialToken: initialToken, getToken: reissueToken(token:), max: 2, diff --git a/Projects/Data/Sources/Network/API/RestAPI/RestAPI-extension.swift b/Projects/Data/Sources/Network/API/RestAPI/RestAPI-extension.swift index 79a6babc..8f63f0fd 100644 --- a/Projects/Data/Sources/Network/API/RestAPI/RestAPI-extension.swift +++ b/Projects/Data/Sources/Network/API/RestAPI/RestAPI-extension.swift @@ -7,6 +7,8 @@ // import Foundation +import RxSwift +import Global extension DefaultRestAPI: RestAPI {