From a4b636ddc8cec757d266a9896760191868b77bf7 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Thu, 14 Dec 2023 11:47:06 +0900 Subject: [PATCH 01/47] =?UTF-8?q?feat=20#373:=20Future=20Extension=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Core/Extension/Future+.swift | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 iOS/traveline/Sources/Core/Extension/Future+.swift diff --git a/iOS/traveline/Sources/Core/Extension/Future+.swift b/iOS/traveline/Sources/Core/Extension/Future+.swift new file mode 100644 index 0000000..b51559e --- /dev/null +++ b/iOS/traveline/Sources/Core/Extension/Future+.swift @@ -0,0 +1,42 @@ +// +// Future+.swift +// traveline +// +// Created by 김태현 on 12/14/23. +// Copyright © 2023 traveline. All rights reserved. +// + +import Combine +import Foundation + +extension Future where Failure == Error { + + /// async 응답 결과를 Future publisher로 변환합니다. + /// - Parameter asyncFulfill: 변환할 async 응답 + convenience init(_ asyncFulfill: @escaping () async throws -> Output) { + self.init { promise in + Task { + do { + let result = try await asyncFulfill() + promise(.success(result)) + } catch { + promise(.failure(error)) + } + } + } + } +} + +extension Future where Failure == Never { + + /// async 응답 결과를 Future publisher로 변환합니다. + /// - Parameter asyncFulfill: 변환할 async 응답 + convenience init(_ asyncFulfill: @escaping () async -> Output) { + self.init { promise in + Task { + let result = await asyncFulfill() + promise(.success(result)) + } + } + } +} From 49da3cb7ccac91096a8f92c49776d4275ea0e722 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Thu, 14 Dec 2023 11:47:46 +0900 Subject: [PATCH 02/47] =?UTF-8?q?refactor=20#373:=20Future=20Extension?= =?UTF-8?q?=EC=9D=84=20=ED=86=B5=ED=95=9C=20UseCase=20Future-Task=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Domain/UseCase/AutoLoginUseCase.swift | 15 ++--- .../Sources/Domain/UseCase/HomeUseCase.swift | 26 ++------ .../Sources/Domain/UseCase/LoginUseCase.swift | 17 ++--- .../Domain/UseCase/MyPostingListUseCase.swift | 13 +--- .../UseCase/ProfileEditingUseCase.swift | 52 +++++---------- .../Domain/UseCase/SettingUseCase.swift | 16 ++--- .../Domain/UseCase/SideMenuUseCase.swift | 12 +--- .../UseCase/TimelineDetailUseCase.swift | 26 +++----- .../Domain/UseCase/TimelineUseCase.swift | 63 +++++-------------- .../UseCase/TimelineWritingUseCase.swift | 35 +++-------- .../Domain/UseCase/TravelUseCase.swift | 27 ++------ 11 files changed, 79 insertions(+), 223 deletions(-) diff --git a/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift index 9eb1dfa..f21a865 100644 --- a/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift @@ -22,16 +22,11 @@ final class AutoLoginUseCaseImpl: AutoLoginUseCase { } func requestLogin() -> AnyPublisher { - return Future { promise in - Task { - do { - let accessToken = try await self.repository.refresh() - KeychainList.accessToken = accessToken - promise(.success(true)) - } catch { - promise(.failure(error)) - } - } + return Future { + let accessToken = try await self.repository.refresh() + KeychainList.accessToken = accessToken + return true }.eraseToAnyPublisher() } + } diff --git a/iOS/traveline/Sources/Domain/UseCase/HomeUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/HomeUseCase.swift index 8c84de3..91beace 100644 --- a/iOS/traveline/Sources/Domain/UseCase/HomeUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/HomeUseCase.swift @@ -26,16 +26,9 @@ final class HomeUseCaseImpl: HomeUseCase { } func fetchSearchList(with query: SearchQuery) -> AnyPublisher { - return Future { promise in - Task { [weak self] in - guard let self else { return } - do { - let travelList = try await self.repository.fetchPostingList(with: query) - promise(.success(travelList)) - } catch { - promise(.failure(error)) - } - } + return Future { + let travelList = try await self.repository.fetchPostingList(with: query) + return travelList }.eraseToAnyPublisher() } @@ -68,16 +61,9 @@ final class HomeUseCaseImpl: HomeUseCase { } func fetchRelatedKeyword(_ keyword: String) -> AnyPublisher { - return Future { promise in - Task { [weak self] in - guard let self else { return } - do { - let relatedKeyword = try await self.repository.fetchPostingTitleList(keyword) - promise(.success(relatedKeyword)) - } catch { - promise(.failure(error)) - } - } + return Future { + let relatedKeyword = try await self.repository.fetchPostingTitleList(keyword) + return relatedKeyword }.eraseToAnyPublisher() } diff --git a/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift index 3c5d351..45ee1e6 100644 --- a/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift @@ -22,17 +22,12 @@ final class LoginUseCaseImpl: LoginUseCase { } func requestLogin(with info: AppleLoginRequest) -> AnyPublisher { - return Future { promise in - Task { - do { - let tlToken = try await self.repository.appleLogin(with: info) - KeychainList.accessToken = tlToken.accessToken - KeychainList.refreshToken = tlToken.refreshToken - promise(.success(true)) - } catch { - promise(.failure(error)) - } - } + return Future { + let tlToken = try await self.repository.appleLogin(with: info) + KeychainList.accessToken = tlToken.accessToken + KeychainList.refreshToken = tlToken.refreshToken + return true }.eraseToAnyPublisher() } + } diff --git a/iOS/traveline/Sources/Domain/UseCase/MyPostingListUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/MyPostingListUseCase.swift index 8932708..bc3cd05 100644 --- a/iOS/traveline/Sources/Domain/UseCase/MyPostingListUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/MyPostingListUseCase.swift @@ -22,16 +22,9 @@ final class MyPostListUseCaseImpl: MyPostListUseCase { } func fetchMyPostList() -> AnyPublisher { - return Future { promise in - Task { [weak self] in - guard let self else { return } - do { - let travelList = try await self.repository.fetchMyPostingList() - promise(.success(travelList)) - } catch { - promise(.failure(error)) - } - } + return Future { + let travelList = try await self.repository.fetchMyPostingList() + return travelList }.eraseToAnyPublisher() } diff --git a/iOS/traveline/Sources/Domain/UseCase/ProfileEditingUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/ProfileEditingUseCase.swift index b1be020..a81932c 100644 --- a/iOS/traveline/Sources/Domain/UseCase/ProfileEditingUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/ProfileEditingUseCase.swift @@ -33,38 +33,20 @@ final class ProfileEditingUseCaseImpl: ProfileEditingUseCase { } func fetchProfile() -> AnyPublisher { - return Future { promise in - Task { [weak self] in - guard let self else { return } - do { - profile = try await self.repository.fetchUserInfo() - promise(.success(profile)) - } catch { - promise(.failure(error)) - } - } + return Future { + self.profile = try await self.repository.fetchUserInfo() + return self.profile }.eraseToAnyPublisher() } func validate(nickname: String) -> AnyPublisher { - return Future { promise in - Task { - guard self.isTooShort(nickname) == false else { - return promise(.success(.tooShort)) - } - guard self.isValidStringLength(nickname) else { - return promise(.success(.exceededStringLength)) - } - guard self.isChanged(nickname) else { - return promise(.success(.unchanged)) - } - do { - let isDuplicated = try await self.repository.checkDuplication(name: nickname) - promise(isDuplicated ? .success(.duplicated) : .success(.available)) - } catch { - promise(.failure(error)) - } - } + return Future { + guard self.isTooShort(nickname) == false else { return .tooShort } + guard self.isValidStringLength(nickname) else { return .exceededStringLength } + guard self.isChanged(nickname) else { return .unchanged } + + let isDuplicated = try await self.repository.checkDuplication(name: nickname) + return isDuplicated ? .duplicated : .available }.eraseToAnyPublisher() } @@ -80,16 +62,10 @@ final class ProfileEditingUseCaseImpl: ProfileEditingUseCase { } func update(name: String, imageData: Data?) -> AnyPublisher { - return Future { promise in - Task { [weak self] in - guard let self else { return } - do { - let profile = try await self.repository.updateUserInfo(name: name, imageData: imageData) - promise(.success(profile)) - } catch { - promise(.failure(error)) - } - } + return Future { + let profile = try await self.repository.updateUserInfo(name: name, imageData: imageData) + return profile }.eraseToAnyPublisher() } + } diff --git a/iOS/traveline/Sources/Domain/UseCase/SettingUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/SettingUseCase.swift index 6f36bd2..dbb03c5 100644 --- a/iOS/traveline/Sources/Domain/UseCase/SettingUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/SettingUseCase.swift @@ -31,17 +31,11 @@ final class SettingUseCaseImpl: SettingUseCase { return repository.requestAppleId() } - func requestWithdrawal(_ reqeust: WithdrawRequest) -> AnyPublisher { - return Future { promise in - Task { - do { - let result = try await self.repository.withdrawal(reqeust) - KeychainList.allClear() - promise(.success(result)) - } catch { - promise(.failure(error)) - } - } + func requestWithdrawal(_ request: WithdrawRequest) -> AnyPublisher { + return Future { + let result = try await self.repository.withdrawal(request) + KeychainList.allClear() + return result }.eraseToAnyPublisher() } diff --git a/iOS/traveline/Sources/Domain/UseCase/SideMenuUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/SideMenuUseCase.swift index 876d26b..d1744a8 100644 --- a/iOS/traveline/Sources/Domain/UseCase/SideMenuUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/SideMenuUseCase.swift @@ -22,15 +22,9 @@ final class SideMenuUseCaseImpl: SideMenuUseCase { } func fetchProfile() -> AnyPublisher { - return Future { promise in - Task { - do { - let profile = try await self.repository.fetchUserInfo() - promise(.success(profile)) - } catch { - promise(.failure(error)) - } - } + return Future { + let profile = try await self.repository.fetchUserInfo() + return profile }.eraseToAnyPublisher() } diff --git a/iOS/traveline/Sources/Domain/UseCase/TimelineDetailUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/TimelineDetailUseCase.swift index a7cdaed..c12f150 100644 --- a/iOS/traveline/Sources/Domain/UseCase/TimelineDetailUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/TimelineDetailUseCase.swift @@ -23,29 +23,17 @@ final class TimelineDetailUseCaseImpl: TimelineDetailUseCase { } func fetchTimelineDetail(with id: String) -> AnyPublisher { - return Future { promise in - Task { [weak self] in - guard let self else { return } - do { - let detailInfo = try await self.repository.fetchTimelineDetailInfo(id: id) - promise(.success(detailInfo)) - } catch { - promise(.failure(error)) - } - } + return Future { + let detailInfo = try await self.repository.fetchTimelineDetailInfo(id: id) + return detailInfo }.eraseToAnyPublisher() } func deleteTimeline(id: String) -> AnyPublisher { - return Future { promise in - Task { - do { - let result = try await self.repository.deleteTimeline(id: id) - promise(.success(result)) - } catch { - promise(.failure(error)) - } - } + return Future { + let result = try await self.repository.deleteTimeline(id: id) + return result }.eraseToAnyPublisher() } + } diff --git a/iOS/traveline/Sources/Domain/UseCase/TimelineUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/TimelineUseCase.swift index 8f66de4..e18b0c4 100644 --- a/iOS/traveline/Sources/Domain/UseCase/TimelineUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/TimelineUseCase.swift @@ -29,30 +29,17 @@ final class TimelineUseCaseImpl: TimelineUseCase { } func fetchTimelineInfo(id: TravelID) -> AnyPublisher { - return Future { promise in - Task { - do { - let travelInfo = try await self.postingRepository.fetchTimelineInfo(id: id) - promise(.success(travelInfo)) - } catch { - promise(.failure(error)) - } - } + return Future { + let travelInfo = try await self.postingRepository.fetchTimelineInfo(id: id) + return travelInfo }.eraseToAnyPublisher() } func fetchTimelineList(id: TravelID, day: Int) -> AnyPublisher { - return Future { promise in - Task { - do { - let timelineList = try await self.timelineRepository.fetchTimelineList(id: id, day: day) - promise(.success(timelineList)) - } catch { - promise(.failure(error)) - } - } - } - .eraseToAnyPublisher() + return Future { + let timelineList = try await self.timelineRepository.fetchTimelineList(id: id, day: day) + return timelineList + }.eraseToAnyPublisher() } func calculateDate(from startDate: String, with day: Int) -> String? { @@ -64,41 +51,23 @@ final class TimelineUseCaseImpl: TimelineUseCase { } func deleteTravel(id: TravelID) -> AnyPublisher { - return Future { promise in - Task { - do { - let result = try await self.postingRepository.deletePosting(id: id) - promise(.success(result)) - } catch { - promise(.failure(error)) - } - } + return Future { + let result = try await self.postingRepository.deletePosting(id: id) + return result }.eraseToAnyPublisher() } func reportTravel(id: TravelID) -> AnyPublisher { - return Future { promise in - Task { - do { - let result = try await self.postingRepository.postReport(id: id) - promise(.success(result)) - } catch { - promise(.failure(error)) - } - } + return Future { + let result = try await self.postingRepository.postReport(id: id) + return result }.eraseToAnyPublisher() } func likeTravel(id: TravelID) -> AnyPublisher { - return Future { promise in - Task { - do { - let result = try await self.postingRepository.postLike(id: id) - promise(.success(result)) - } catch { - promise(.failure(error)) - } - } + return Future { + let result = try await self.postingRepository.postLike(id: id) + return result }.eraseToAnyPublisher() } diff --git a/iOS/traveline/Sources/Domain/UseCase/TimelineWritingUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/TimelineWritingUseCase.swift index 2a44dba..ec588fe 100644 --- a/iOS/traveline/Sources/Domain/UseCase/TimelineWritingUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/TimelineWritingUseCase.swift @@ -25,15 +25,8 @@ final class TimelineWritingUseCaseImpl: TimelineWritingUseCase { } func requestCreateTimeline(with info: TimelineDetailRequest) -> AnyPublisher { - return Future { promise in - Task { - do { - try await self.repository.createTimelineDetail(with: info) - promise(.success(Void())) - } catch { - promise(.failure(error)) - } - } + return Future { + try await self.repository.createTimelineDetail(with: info) }.eraseToAnyPublisher() } @@ -42,28 +35,16 @@ final class TimelineWritingUseCaseImpl: TimelineWritingUseCase { return .just([]).setFailureType(to: Error.self).eraseToAnyPublisher() } - return Future { promise in - Task { - do { - let placeList = try await self.repository.fetchTimelinePlaces(keyword: keyword, offset: offset) - promise(.success(placeList)) - } catch { - promise(.failure(error)) - } - } + return Future { + let placeList = try await self.repository.fetchTimelinePlaces(keyword: keyword, offset: offset) + return placeList }.eraseToAnyPublisher() } func putTimeline(id: String, info: TimelineDetailRequest) -> AnyPublisher { - return Future { promise in - Task { - do { - let result = try await self.repository.putTimeline(id: id, info: info) - promise(.success(result)) - } catch { - promise(.failure(error)) - } - } + return Future { + let result = try await self.repository.putTimeline(id: id, info: info) + return result }.eraseToAnyPublisher() } diff --git a/iOS/traveline/Sources/Domain/UseCase/TravelUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/TravelUseCase.swift index 170d32b..d7d6da4 100644 --- a/iOS/traveline/Sources/Domain/UseCase/TravelUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/TravelUseCase.swift @@ -44,31 +44,16 @@ final class TravelUseCaseImpl: TravelUseCase { } func createTravel(data: TravelRequest) -> AnyPublisher { - return Future { promise in - Task { - do { - let id = try await self.repository.postPosting(data: data) - promise(.success(id)) - } catch { - promise(.failure(error)) - } - } + return Future { + let id = try await self.repository.postPosting(data: data) + return id }.eraseToAnyPublisher() } func putTravel(id: TravelID, data: TravelRequest) -> AnyPublisher { - return Future { promise in - Task { - do { - let id = try await self.repository.putPosting( - id: id, - data: data - ) - promise(.success(id)) - } catch { - promise(.failure(error)) - } - } + return Future { + let id = try await self.repository.putPosting(id: id, data: data) + return id }.eraseToAnyPublisher() } From 5ec7d251b86b6cc479781de16e4a49da46efe26b Mon Sep 17 00:00:00 2001 From: kth1210 Date: Thu, 14 Dec 2023 17:47:00 +0900 Subject: [PATCH 03/47] =?UTF-8?q?feat=20#380:=20TimelineDetailRepository?= =?UTF-8?q?=20->=20Translate=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/API/TimelineDetailEndPoint.swift | 6 +++++- .../TimelineTranslatedResponseDTO.swift | 19 +++++++++++++++++++ .../Mock/TimelineDetailRepositoryMock.swift | 6 ++++++ .../TimelineDetailRepositoryImpl.swift | 9 +++++++++ .../Timeline/TimelineTranslatedInfo.swift | 15 +++++++++++++++ .../TimelineDetailRepository.swift | 1 + 6 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 iOS/traveline/Sources/Data/Network/DataMapping/TimelineTranslatedResponseDTO.swift create mode 100644 iOS/traveline/Sources/Domain/Model/Timeline/TimelineTranslatedInfo.swift diff --git a/iOS/traveline/Sources/Data/Network/API/TimelineDetailEndPoint.swift b/iOS/traveline/Sources/Data/Network/API/TimelineDetailEndPoint.swift index b7f234a..c475c8c 100644 --- a/iOS/traveline/Sources/Data/Network/API/TimelineDetailEndPoint.swift +++ b/iOS/traveline/Sources/Data/Network/API/TimelineDetailEndPoint.swift @@ -14,6 +14,7 @@ enum TimelineDetailEndPoint { case fetchPlaceList(String, Int) case putTimeline(String, TimelineDetailRequestDTO) case deleteTimeline(String) + case translateTimeline(String) } extension TimelineDetailEndPoint: EndPoint { @@ -34,6 +35,9 @@ extension TimelineDetailEndPoint: EndPoint { case .deleteTimeline(let id): return "\(curPath)/\(id)" + case .translateTimeline(let id): + return "\(curPath)/\(id)/translate" + default: return curPath } @@ -41,7 +45,7 @@ extension TimelineDetailEndPoint: EndPoint { var httpMethod: HTTPMethod { switch self { - case .specificTimeline, .fetchPlaceList: + case .specificTimeline, .fetchPlaceList, .translateTimeline: return .GET case .createTimeline: diff --git a/iOS/traveline/Sources/Data/Network/DataMapping/TimelineTranslatedResponseDTO.swift b/iOS/traveline/Sources/Data/Network/DataMapping/TimelineTranslatedResponseDTO.swift new file mode 100644 index 0000000..240f3d6 --- /dev/null +++ b/iOS/traveline/Sources/Data/Network/DataMapping/TimelineTranslatedResponseDTO.swift @@ -0,0 +1,19 @@ +// +// TimelineTranslatedResponseDTO.swift +// traveline +// +// Created by 김태현 on 12/14/23. +// Copyright © 2023 traveline. All rights reserved. +// + +import Foundation + +struct TimelineTranslatedResponseDTO: Decodable { + let description: String +} + +extension TimelineTranslatedResponseDTO { + func toDomain() -> TimelineTranslatedInfo { + return .init(description: description) + } +} diff --git a/iOS/traveline/Sources/Data/Repository/Mock/TimelineDetailRepositoryMock.swift b/iOS/traveline/Sources/Data/Repository/Mock/TimelineDetailRepositoryMock.swift index 9d0ead7..049636f 100644 --- a/iOS/traveline/Sources/Data/Repository/Mock/TimelineDetailRepositoryMock.swift +++ b/iOS/traveline/Sources/Data/Repository/Mock/TimelineDetailRepositoryMock.swift @@ -39,4 +39,10 @@ final class TimelineDetailRepositoryMock: TimelineDetailRepository { return true } + + func fetchTimelineTranslatedInfo(id: String) async throws -> TimelineTranslatedInfo { + try await Task.sleep(nanoseconds: 1_000_000_000) + + return TimelineTranslatedInfo.empty + } } diff --git a/iOS/traveline/Sources/Data/Repository/TimelineDetailRepositoryImpl.swift b/iOS/traveline/Sources/Data/Repository/TimelineDetailRepositoryImpl.swift index 5db6111..4cb6222 100644 --- a/iOS/traveline/Sources/Data/Repository/TimelineDetailRepositoryImpl.swift +++ b/iOS/traveline/Sources/Data/Repository/TimelineDetailRepositoryImpl.swift @@ -55,4 +55,13 @@ final class TimelineDetailRepositoryImpl: TimelineDetailRepository { return deleteTimelineDTO } + func fetchTimelineTranslatedInfo(id: String) async throws -> TimelineTranslatedInfo { + let translateTimelineDTO = try await network.request( + endPoint: TimelineDetailEndPoint.translateTimeline(id), + type: TimelineTranslatedResponseDTO.self + ) + + return translateTimelineDTO.toDomain() + } + } diff --git a/iOS/traveline/Sources/Domain/Model/Timeline/TimelineTranslatedInfo.swift b/iOS/traveline/Sources/Domain/Model/Timeline/TimelineTranslatedInfo.swift new file mode 100644 index 0000000..93c9acd --- /dev/null +++ b/iOS/traveline/Sources/Domain/Model/Timeline/TimelineTranslatedInfo.swift @@ -0,0 +1,15 @@ +// +// TimelineTranslatedInfo.swift +// traveline +// +// Created by 김태현 on 12/14/23. +// Copyright © 2023 traveline. All rights reserved. +// + +import Foundation + +struct TimelineTranslatedInfo { + let description: String + + static let empty: TimelineTranslatedInfo = .init(description: Literal.empty) +} diff --git a/iOS/traveline/Sources/Domain/RepositoryInterface/TimelineDetailRepository.swift b/iOS/traveline/Sources/Domain/RepositoryInterface/TimelineDetailRepository.swift index 0f479f1..3cdfc50 100644 --- a/iOS/traveline/Sources/Domain/RepositoryInterface/TimelineDetailRepository.swift +++ b/iOS/traveline/Sources/Domain/RepositoryInterface/TimelineDetailRepository.swift @@ -14,4 +14,5 @@ protocol TimelineDetailRepository { func fetchTimelinePlaces(keyword: String, offset: Int) async throws -> TimelinePlaceList func putTimeline(id: String, info: TimelineDetailRequest) async throws -> Bool func deleteTimeline(id: String) async throws -> Bool + func fetchTimelineTranslatedInfo(id: String) async throws -> TimelineTranslatedInfo } From db280914db2169c5852279808743befa0495a14a Mon Sep 17 00:00:00 2001 From: kth1210 Date: Thu, 14 Dec 2023 17:47:16 +0900 Subject: [PATCH 04/47] =?UTF-8?q?feat=20#380:=20TimelineDetailUseCase=20->?= =?UTF-8?q?=20Translate=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Domain/UseCase/TimelineDetailUseCase.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/iOS/traveline/Sources/Domain/UseCase/TimelineDetailUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/TimelineDetailUseCase.swift index c12f150..817b567 100644 --- a/iOS/traveline/Sources/Domain/UseCase/TimelineDetailUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/TimelineDetailUseCase.swift @@ -12,6 +12,7 @@ import Foundation protocol TimelineDetailUseCase { func fetchTimelineDetail(with id: String) -> AnyPublisher func deleteTimeline(id: String) -> AnyPublisher + func fetchTranslateTimelineDetail(with id: String) -> AnyPublisher } final class TimelineDetailUseCaseImpl: TimelineDetailUseCase { @@ -36,4 +37,10 @@ final class TimelineDetailUseCaseImpl: TimelineDetailUseCase { }.eraseToAnyPublisher() } + func fetchTranslateTimelineDetail(with id: String) -> AnyPublisher { + return Future { + let translatedInfo = try await self.repository.fetchTimelineTranslatedInfo(id: id) + return translatedInfo + }.eraseToAnyPublisher() + } } From 2cff01148ca062ef95b90dc063ed3a1bae9fb76b Mon Sep 17 00:00:00 2001 From: kth1210 Date: Thu, 14 Dec 2023 17:47:36 +0900 Subject: [PATCH 05/47] =?UTF-8?q?feat=20#380:=20TimelineDetailVC=20->=20?= =?UTF-8?q?=EB=B2=88=EC=97=AD=20=EA=B8=B0=EB=8A=A5=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Core/Constant/Literal.swift | 1 + .../VC/TimelineDetailVC.swift | 21 ++++++++++++++++++ .../ViewModel/TimelineDetailViewModel.swift | 22 +++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/iOS/traveline/Sources/Core/Constant/Literal.swift b/iOS/traveline/Sources/Core/Constant/Literal.swift index d1761ee..b6d60c1 100644 --- a/iOS/traveline/Sources/Core/Constant/Literal.swift +++ b/iOS/traveline/Sources/Core/Constant/Literal.swift @@ -117,6 +117,7 @@ enum Literal { static let modify: String = "수정하기" static let delete: String = "삭제하기" static let report: String = "신고하기" + static let translate: String = "번역하기" } enum Query { diff --git a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift index 6d0afb2..15edcb0 100644 --- a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift @@ -113,6 +113,9 @@ final class TimelineDetailVC: UIViewController { if isOwner { menuItems = [ + .init(title: Literal.Action.translate, handler: { [weak self] _ in + self?.viewModel.sendAction(.translateTimeline) + }), .init(title: Literal.Action.modify, handler: { [weak self] _ in self?.viewModel.sendAction(.editTimeline) }), @@ -120,6 +123,12 @@ final class TimelineDetailVC: UIViewController { self?.viewModel.sendAction(.deleteTimeline) }) ] + } else { + menuItems = [ + .init(title: Literal.Action.translate, handler: { [weak self] _ in + self?.viewModel.sendAction(.translateTimeline) + }) + ] } tlNavigationBar.addRightButton( @@ -239,5 +248,17 @@ private extension TimelineDetailVC { owner.navigationController?.pushViewController(timelineEditVC, animated: true) } .store(in: &cancellables) + + viewModel.state + .map(\.isTranslated) + .dropFirst() + .withUnretained(self) + .sink { owner, isTranslated in + let description = isTranslated + ? owner.viewModel.currentState.timelineTranslatedInfo.description + : owner.viewModel.currentState.timelineDetailInfo.description + owner.contentView.setText(to: description) + } + .store(in: &cancellables) } } diff --git a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/ViewModel/TimelineDetailViewModel.swift b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/ViewModel/TimelineDetailViewModel.swift index 108f85c..9f1902f 100644 --- a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/ViewModel/TimelineDetailViewModel.swift +++ b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/ViewModel/TimelineDetailViewModel.swift @@ -13,6 +13,7 @@ enum TimelineDetailAction: BaseAction { case viewWillAppear case editTimeline case deleteTimeline + case translateTimeline } enum TimelineDetailSideEffect: BaseSideEffect { @@ -32,13 +33,16 @@ enum TimelineDetailSideEffect: BaseSideEffect { case timelineDetailError(TimelineDetailError) case popToTimeline(Bool) case showTimelineDetailEditing + case loadTimelineTranslatedInfo(TimelineTranslatedInfo) } struct TimelineDetailState: BaseState { var timelineDetailInfo: TimelineDetailInfo = .empty + var timelineTranslatedInfo: TimelineTranslatedInfo = .empty var isOwner: Bool = false var isDeleteCompleted: Bool = false var isEdit: Bool = false + var isTranslated: Bool = false } final class TimelineDetailViewModel: BaseViewModel { @@ -61,6 +65,9 @@ final class TimelineDetailViewModel: BaseViewModel SideEffectPublisher { + return timelineDetailUseCase.fetchTranslateTimelineDetail(with: id) + .map { translatedInfo in + return .loadTimelineTranslatedInfo(translatedInfo) + } + .catch { _ in + return Just(TimelineDetailSideEffect.timelineDetailError(.deleteFailed)) + } + .eraseToAnyPublisher() + } } From 67dfea4be37b67437085720f2c69d1dde26183d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EC=9D=B8?= <0inn1220@gmail.com> Date: Fri, 12 Jan 2024 16:23:30 +0900 Subject: [PATCH 06/47] =?UTF-8?q?fix=20#382:=20=EB=B2=88=EB=93=A4=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EB=94=94=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 인증서 변경에 따른 번들 아이디 수정 --- .../EnvPlugin/ProjectDescriptionHelpers/Environment.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/Plugins/EnvPlugin/ProjectDescriptionHelpers/Environment.swift b/iOS/Plugins/EnvPlugin/ProjectDescriptionHelpers/Environment.swift index 1f3d867..5a5cb22 100644 --- a/iOS/Plugins/EnvPlugin/ProjectDescriptionHelpers/Environment.swift +++ b/iOS/Plugins/EnvPlugin/ProjectDescriptionHelpers/Environment.swift @@ -4,6 +4,6 @@ public extension Project { enum Environmnet { public static let workspace = "traveline" public static let deploymentTarget = DeploymentTarget.iOS(targetVersion: "16.0", devices: [.iphone]) - public static let bundleName = "kr.codesquad.boostcamp8.traveline" + public static let bundleName = "com.boostcamp8.traveline" } } From fe4a0858b7698aa29cbcf3dfb17a8492827e0042 Mon Sep 17 00:00:00 2001 From: otoolz Date: Fri, 12 Jan 2024 16:40:23 +0900 Subject: [PATCH 07/47] =?UTF-8?q?feat=20#384:=20=EC=97=A0=ED=8B=B0?= =?UTF-8?q?=EB=B7=B0=20=EA=B4=80=EB=A0=A8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/empty.imageset/Contents.json | 23 ++++++++++++++++++ .../empty.imageset/Glyph_ undefined 1.png | Bin 0 -> 2605 bytes .../empty.imageset/Glyph_ undefined 2.png | Bin 0 -> 4198 bytes .../empty.imageset/Glyph_ undefined.png | Bin 0 -> 1431 bytes .../Sources/DesignSystem/View/EmptyView.swift | 21 ++++++++++++++++ 5 files changed, 44 insertions(+) create mode 100644 iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Contents.json create mode 100644 iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Glyph_ undefined 1.png create mode 100644 iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Glyph_ undefined 2.png create mode 100644 iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Glyph_ undefined.png create mode 100644 iOS/traveline/Sources/DesignSystem/View/EmptyView.swift diff --git a/iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Contents.json b/iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Contents.json new file mode 100644 index 0000000..b2a331d --- /dev/null +++ b/iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Glyph_ undefined.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Glyph_ undefined 1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Glyph_ undefined 2.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Glyph_ undefined 1.png b/iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Glyph_ undefined 1.png new file mode 100644 index 0000000000000000000000000000000000000000..75d5ea89540522ec64a92b129d62a00911c1dc9b GIT binary patch literal 2605 zcmV+|3exq7P)>$^b~n`Z0t4I` zSG}VYJb{EsUEtt`5RUEtK*A&~@Jzuw2#gxQ86-?EjI7USEaB87TV;B3x}y*9Dc!$w zK|yY(K=eZ`@!PG-%1otb{2pm~)$nxT|EEO(hua_35w!U}I9>Y*%km%gE`2c0Iu)3# zU^JADt5X~l>)`hf>JVB$pMx(X5D7L}wPImgg;%89E@Uz}7KwiCwa5ZW(fJ8;Y#yB9-}kM}M`$Ni?DfJQo*W!I)WwDh;E= z;EdO%4q}QNMPM>stV=QDyuZ+J0*QexO|D^R+^g203r=t&%Zf`am(}g!SoUuFgG$mp zSUY7~4*9vCp@n?~0SPK_D{-+~#?_lwSl|gH43>FwfK$IE%ah9pE^;t|+pP59d~q@> z#m!+2FlE(T6EdDZOJ)!CWw*MLfpf36_>!W9Z(q= zZ#6BRd<5Vb?*iKiedcZ<=#)A=e4p(HVyNj3FYmeq_ z4CWeD4^7-E%rV)k(dDfLQ{WqjxQ*DdyVkHrw893S@X~^9>-`GNce>g-Q5ztTopR}l z0{v+KkAL;hihZ@s3G{IDy=G(9w=&ICaa1VIo4K@g&n z+y-!36n|8}9(sbtHts36AwP${yk#-&5sRb*c7y_X344igkC;4yme>trdr$_jPtmV- z{`{kQ4-s&L0%dlY?HTm^9$Y?+^`r+Bg(XXav{@`fz*rTm_)X6HhBH>EYG?Q0Kn#0s?})1Z#B#AGVbrv>5l`FubmRfGAL)O=DsU{K&OwY#26Q#%#!2AH^wWC{_LD(IEH0IG%rSVM)^81X9G48=)==csZezXT)VAcGg zp*K+xjiD}OSkS$#+Zgw7;j)xnT2x^RY?GGMT!i4_5;oL@D){7w2vq3+hxm6dp{6nJ z!6a|EmI0GC^WfTXK}E<{HOE%)7y=UG9xkv!<+^T%VpC&VfZs(oY==xeSy&%gO#5{E zBKWtWp{WHc_#1>?;zi)br%hOuyy02~Oj6HPDL4>;*BlXe&1oZWUUxyYFxv$-S;IB< zv{YJfPr*j>bHn8z0ylkV27+&zrlmPBY2&KQ>#k`n0{cem#%U?rvDC(s)il@5K=4h| z>Rk>@_$^pBf9U7PSya~dzt3;Iae(TEl4z`^p0-X|SgZwEDywRfF z`k;DUhbg!!o5!27yS%)3smI_6+Tp-DUm}6CB~lQWb9`N<6&#}xczmA&FGyI*LRWCp zAt!LQL=w9T&v*3~X7+T08M6vF!TF-|6f|QY&(MGxz*eh6rV~lc*m`$i7f={0b~Tq= zk&RFasGFsS<7$3};oOI7S8C|Y!cBY-sP$7<&!-&sqwFxeFUjl=HWsbsiyRCqZmc=@ zeMxSIKU&pC__7BbkmC6g$u0QfwB$9-52ISJu}@O?6Tp=r`ne88S)a#~bHSH?S5{%H6U zzaOoHO=5P#j(i}r*znlLL*_+bM)H#6(hr4%b26MgdF9?quwJa_Yj=!L=JO-UT)RV-y(1@D;DS$YfM<#SOLm*o!0HL&a5fh0Vx&%nBeVBPD~S zX9~8OWQ{I*D&6xr8&?%73^gY6c&--W26X@hT`sg?wmgyPm~*U{l)G%xB{So>D&hv) zFhB%f4{j;48JB&Zf*k{TFgd568zPBIL|O3v5irkOq^JDTkQfCwGhS&>ZNw0lh!)ZD zBL^f;`=kBPSj&?#X2VtP`-r5A*d*~om(-2##Jrd z$VQ|PP`*g5o3;|q)kP#`r!iS}zBu2O39Fu;Yl7Foe8v=C^WRn+-zZDL_cELMl(xgR zZEfZ(V7Rd#Y}ikyr=RyC=*~u9n3$xtPx(Sj(+xP@DY%K;Sj;g}$?cWhiTJRr;xDh9 zZISoEag%IRC2{&vj|VDjAVKVKBi|&Q2yHlkEeMPW;Gu;KBPB}8p^`4{!#&Pv8 z2u}_PleEA%K9aQ^Zt78n&msV;5_)LE+Dhh zpph#*4dD# P00000NkvXXu0mjfMxV>? literal 0 HcmV?d00001 diff --git a/iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Glyph_ undefined 2.png b/iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Glyph_ undefined 2.png new file mode 100644 index 0000000000000000000000000000000000000000..fe4d797e9265a936623c179cccfc0e2fdf7bb112 GIT binary patch literal 4198 zcmaJ_c{CIb@JH5S-S>T@+-DY%a~(@rM{X_F5sNHYSJW44-J+6mTSSPJ+*i3;WLKNB zNT}S&rd?O!r{90Se}3=H%x7la{4?*(ym_BVx3ht=vx>0N(b2J6S(;rx!;Ud6<2zXw%bfo5$u{+EMe5x=%|=@(S`;q?j@PpRh3lFoK~rk%ue@ zK_l@k-N#q^OIFbdj|{be8>`OzN}`^_+uJ#>^VVXdZsSiIQagbXN7{4 zB}?UfcQbOZy3c>02GM?c9@;%V8XNY8g0btU<)B(0l*qV%2#~5bW9((37H}p?%Kfj; z5Uzv!lU&C6O=mlf7iGeza*O|!JbZA=_b+#cTOeS%-*Ex3>^uZ%Tmz{4;+M6UmAFkB z=}rCC{J0ZAR8Q9Q@Oob*I0wA%eth2p=liD=&ceT7dlxPicQK!XpNH?#phX0{wMd+W z!mS?ex+FgXwOM;%389Px2ed ztzk5>e&!XIV%74elg1#1hsNGJ-fS_AwV&)L0Sl}&y{Oe}l+E$WOw3|PkQrsQW`4F4 zV_B~kaL`eDOucH5lr`#1CWOB>%j{_16zVY!==8#U#O23p_;{MnDC{W%dIttRXLMe6 zk?MPEde2=xCh+z;QGa<{S~E&YX8pGH1`@tu)bv+4_w{QAdJ91D5-%yG$(eGzYqn_e z=C7MgtIQ`eU4dlCoC$GA@2MZMqZFh!8#BJ><@dON2qDKnqG8iTE-cU8knDwY_Sz{} zJiQ-XoXP3!dfSn!3GvhO*6&p4iRa(%>p!Sj^fH{Xu^l!!#85KjgXp^F6qj40*P^=)dk?+p|lB36P#?MhANv`@Wq|tW7Q!x1&4Blg`VZ^I3 z{EhDY#S#Y!7JqXwaQBNcxT@0hQo^f)t!&)e3QRDs>h{~eTO>#-__m3L5syYR(2zDx zz8Ax2h?7L&L)56}k?xFL&LkCepV9=f0@7RVbwzdXyvqVK{ORBU>3h&+mpR|l$9{(K(=7eVYtI>WWOiBwB=s@8 zBg8sxw*Ix?d1y^OYEE5LUMZ`WQrhxGSj@|>#}03gqXZJq`$vISo&)qF4-53d91=mb z_mD48rH(JUjhwy)sT1bPY(A@K1wKBv^MkZ3sZYQDLf4H>+F&YxQ(3w3Y7aoQ7NJ`dJ&mlM*9F5?S`z&kA_cH&6>!WD9h^J*;l7-;gVm{eD(q*Z$t%Fsm0HvE;To`B=90eAd>A_%@VovU^LgY zG-Ey!(bHx)+VT@rumqNWCRl3tPs#%uT1-X2>_a#Da_C9 z1!jGuKh35c)bs$wxevN~PpegNt#qD$NIFLNb3W@bu4L7-asRFN39?UI+dE48H3brA z$<*pMGiS*dSVf*1q<0IHsMY=D?Z0Ryeo2gpltc7ZH;aI zP@6T5@{(`6G3XH`rT$hDFy@Civy>&7`t`t~ zVf_-3U-a8`IK52POSuQqG25q6m-ouJjNg5UN>U3EQ_rJ4Occz~W)5}$fkkZQ>9_;SH7kOLM;X!#hIDofIW#33LXjB>8zU>0hYe`CWPIM|T!9?Bp zMOIXl4&Hr9R(Mf^y?Yz%&(FxU8SXd(%}AC|RyDsB^8@&=k!&Z&i_E4~Ee7f>n#K=s zP}IfpZ^e3#SZ}L0jKcNKIdYvP9d_3FcL!S+=8TT0hp zAM!QYQsX|7qZ4hYCyq_)KYdev6%S@i>ln6;ci29;#M?dYd^b*8q(9ej@V@r#I~9xg z{I4{AqMoGn;P72D1)IY5hL*ZK;L<)hNFTuT;HIaXKL!iI&{yB$($v5QUtYV!ge5G8 zOaNiO&Q_KiZ19CBT6jIKPHj=Wp^g%_->J_K_>$Xo$h z%^Di6NzunMSZgCYQeR9B=F`6NDQc1Cfx`#@%uJ95Q(vunlF=R!d}r7L6(#g4KivdW zxDKCU5$?)Q?*|p8tvl=!?Cz}=rQz6NR8!v`o%Y_G5OK3d_Gn$Hl3X_VwBS1Es6y0y zR-psz)fy|7sfpa4Fmu#`J1>i$Da?4ix~`U)`AfX_2C`WxpP$q>W=@mRBr8tFb&uH- z#iB@5pYo^C?)hp>^MOr$Q`d`^pUaVDuCWv=A+Vnge+xCPDCA@9%C5ve0>w_+fE?)f zdfYB)dtn=vq#~&KT8?V<-Wh9t1Jq(W;r<(lmfv!_tR^^9Gks(s@EAWN>|S;qd+zTPQjyH-9X0SbSdN@?Jr2KgBEC23vs>_8{4u$Q!8wF+>fp{4E7E&R1RKU|f~ zIAlq%!PTd4aw-~#&%Z;6;q@u#1zq3L4l8)IALjM3o8)GNf@8bvN;=mMbv(gK)u@v7 z@?2TXgCdcmseAf7FmyT!Syih#phSSr zsxN=G)`+HjHr(S!Pj=%e&6^tQFfwe)*I{--D{P0WP>B$*H%2a0t+-eHGfs`d8FE~~ zSG;4(pXuD&`EA}Pls=WHnklYC;&^ySQ0$q;Z9!)KLwaC*Og_0@MI2%s1j9Z(8}qUC zZZ>7?Ga}R%Wwf`$MeeQ*ePt28;J?%?o<)*%+mz$JusKBHYo~WV7LIOYwC$iVc(+?= zTYM92F4?N>kZNxRs|Rv2EPec#vVz~ST*#77L|<5WzW6F|{;>+Y8{8D)%VjlH-;S;# zJdS^q*_7ua`%$|veaV6M7Smc-Uld|OuVeqc?}BbEcO1@G5ZT zfJCtX7OEOTlD0bY+f6kjzqizINO+d{|ZWen00fp>l|njoL}!P z2&97O&+1~e&M~`snspf{;jGm7K2nCm9OHli=^2bi3^(+kq^hc3J8B5y5U1}IsTt-#OtVLy1VHUV_t zDSwZB;Oak4N(z&B$rX8n_Qa{&PBM)%gvG*uLxZVj%|L8~f7(w->266h*xHgHIWLXb z^HZpYj%J&)7M;8){vr`CckEn~X}w5%MhtLl_GV)Ju#bPs1Nwi-H}inS38!EIrztR{ R=HEww&dS`z3=ctI{s-7P;{pHx literal 0 HcmV?d00001 diff --git a/iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Glyph_ undefined.png b/iOS/traveline/Resources/Images.xcassets/Common/empty.imageset/Glyph_ undefined.png new file mode 100644 index 0000000000000000000000000000000000000000..5f36c0db6dea278b7cc0d4d641bd1cee57a601a9 GIT binary patch literal 1431 zcmV;I1!($-P)1FgQ*(#<_z z3t9?655>=ZzsAz8YKL0DPD}d!RKCxMX1E++W))^S^o}}-gvK>U& zMHaGkadIV&Fw!YS0@sZ;UZSnCj?knj0N#XIX6Y%}`Q%!BpuYsPHFJ^Hee;5s(ptQg z)1II&<}*!pMti{GRL(F{AI)db-sjNXpX z^ca19sOqy*OW(iAJB-m@usIoOucG7ct^1KaB35;DXp&VO%P81_!#6+B=P&GKL7gLK z8P`LM?%23Z$lQnjQRo3pihGbAyUIBUV+kHwk5UQ%oN*UceZ0h$Rpt4e@_rb=px2s- z7E-ItLsuauVJS;9nuc!ZEPLqiOI*(J|E^Ac8C-_TiXNTg|0q;`bsy8>-r}%9l>=ZS zvqFT;uF-*%a<@##H<%!zU8ohs8Zs^>5kglJCqzeN?{%}B{-I@?5UA6qS99U6HS zuoRU)VM|dZZ8^bX!alcGDch=T>8JH5p?@EVdE?-q;M|t558l91$<0Hd_TfsS!G;ly zSfQRt$hQo(n6!6v;OnQ-NKOAWb8r=M&TwHkupt1i!Vv|a2)*E!Xghr9`>jcq@1^BS z+Xm&Cfj5Q+dE3FG_2VHT0>8+za<;{t7%YDWSW%g6s9?s}A5ATyb;9;68Az4AoY7Cw z&z)~NKz;~$n?5PeeyTi_5+o9Jh~fZHq$Mp#HdJ=GvvATjSw<8~i#w{ln^zPFtb&^u zGrOtZr;v`%@_~G%6+%j$;Db1p_!O6CRNV{C^WYqhdB`bAr&O@`Bz1b?v8nn>Ib!(R z_+rVyGim@P-{<6(ESFadUP?NlrHM}&abwOONs4>a_oA*IU&36md1P!HkZub`RzMC< zjqVqD=#?XO9FXP)X-{}UmeeHFE@y_9(gJC=c1ms=Zg3x@U1T{%eeCHx2-=olRQw<- z)xlR;v;jw6Nw7`{BCO<@Bkp#72NX}J_6}^9wef-~QY}IhUeG>V*sE5Ul2L3AQLrtq zBviWxXYuF;j<6-LT$>K}gyNxOcCyS)u9~G~E?u7=6!8e$i+nfkR znX30$p)lFL#*uNCypmRIY3{IVX_=OgZY>q2@0fIE?W4_|YM->LE;V4g2EC(24;goF z Date: Fri, 12 Jan 2024 20:04:42 +0900 Subject: [PATCH 08/47] =?UTF-8?q?feat=20#384:=20TLEmptyView=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/DesignSystem/Common/TLImage.swift | 1 + .../Sources/DesignSystem/View/EmptyView.swift | 21 ---- .../DesignSystem/View/TLEmptyView.swift | 101 ++++++++++++++++++ 3 files changed, 102 insertions(+), 21 deletions(-) delete mode 100644 iOS/traveline/Sources/DesignSystem/View/EmptyView.swift create mode 100644 iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift diff --git a/iOS/traveline/Sources/DesignSystem/Common/TLImage.swift b/iOS/traveline/Sources/DesignSystem/Common/TLImage.swift index 5b52c10..8c7a0de 100644 --- a/iOS/traveline/Sources/DesignSystem/Common/TLImage.swift +++ b/iOS/traveline/Sources/DesignSystem/Common/TLImage.swift @@ -33,6 +33,7 @@ enum TLImage { static let close = TravelineAsset.Images.closeMedium.image static let logo = TravelineAsset.Images.travelineLogo.image static let`default` = TravelineAsset.Images.default.image + static let empty = TravelineAsset.Images.empty.image } enum Travel { diff --git a/iOS/traveline/Sources/DesignSystem/View/EmptyView.swift b/iOS/traveline/Sources/DesignSystem/View/EmptyView.swift deleted file mode 100644 index 89a3873..0000000 --- a/iOS/traveline/Sources/DesignSystem/View/EmptyView.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// EmptyView.swift -// traveline -// -// Created by KiWoong Hong on 2024/01/12. -// Copyright © 2024 traveline. All rights reserved. -// - -import UIKit - -class EmptyView: UIView { - - /* - // Only override draw() if you perform custom drawing. - // An empty implementation adversely affects performance during animation. - override func draw(_ rect: CGRect) { - // Drawing code - } - */ - -} diff --git a/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift b/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift new file mode 100644 index 0000000..9c5e316 --- /dev/null +++ b/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift @@ -0,0 +1,101 @@ +// +// TLEmptyView.swift +// traveline +// +// Created by KiWoong Hong on 2024/01/12. +// Copyright © 2024 traveline. All rights reserved. +// + +import UIKit + +class TLEmptyView: UIView { + + private enum Constants { + static let emptyText: String = "아직 작성된 글이 없어요!" + static let shareText: String = "나만의 여행 경험을 공유해보세요 :)" + } + + private enum Metric { + static let imageToLabelSpacing: CGFloat = 16 + static let labelToLabelSpacing: CGFloat = 12 + static let bottomConstants: CGFloat = UIScreen.main.bounds.width / 3 * 2 + } + + // MARK: - UI Components + + private let stackView: UIStackView = { + let view = UIStackView() + view.translatesAutoresizingMaskIntoConstraints = false + view.axis = .vertical + view.alignment = .center + view.distribution = .fill + + return view + }() + + private let imageView: UIImageView = { + let view = UIImageView(image: TLImage.Common.empty) + view.translatesAutoresizingMaskIntoConstraints = false + + return view + }() + + private let emptyLabel: TLLabel = { + let label = TLLabel(font: .subtitle2, color: TLColor.white) + label.setText(to: Constants.emptyText) + label.translatesAutoresizingMaskIntoConstraints = false + + return label + }() + + private let shareLabel: TLLabel = { + let label = TLLabel(font: .body2, color: TLColor.white) + label.setText(to: Constants.shareText) + label.translatesAutoresizingMaskIntoConstraints = false + + return label + }() + + // MARK: - initialize + + init() { + super.init(frame: .zero) + + setupAttributes() + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +// MARK: - Setup Functions + +private extension TLEmptyView { + + func setupAttributes() { + backgroundColor = TLColor.black + } + + func setupLayout() { + addSubview(stackView) + stackView.addArrangedSubviews( + imageView, + emptyLabel, + shareLabel + ) + + NSLayoutConstraint.activate([ + stackView.topAnchor.constraint(equalTo: imageView.topAnchor), + stackView.leadingAnchor.constraint(equalTo: leadingAnchor), + stackView.trailingAnchor.constraint(equalTo: trailingAnchor), + stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Metric.bottomConstants) + ]) + + stackView.setCustomSpacing(Metric.imageToLabelSpacing, after: imageView) + stackView.setCustomSpacing(Metric.labelToLabelSpacing, after: emptyLabel) + } + +} From ea6c4cb9e94974685113845bc3628a3f4cb13da4 Mon Sep 17 00:00:00 2001 From: otoolz Date: Fri, 12 Jan 2024 20:13:13 +0900 Subject: [PATCH 09/47] =?UTF-8?q?feat=20#384:=20EmptyView=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift index b77df95..e25ed06 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift @@ -52,6 +52,8 @@ final class TimelineVC: UIViewController { return collectionView }() + private let emptyView: TLEmptyView = .init() + private let createPostingButton: TLFloatingButton = .init(style: .create) // MARK: - Properties @@ -145,6 +147,7 @@ final class TimelineVC: UIViewController { private extension TimelineVC { func setupAttributes() { view.backgroundColor = TLColor.black + timelineCollectionView.backgroundView = emptyView } func setupLayout() { @@ -199,6 +202,7 @@ private extension TimelineVC { .withUnretained(self) .sink { owner, cardlist in owner.setupData(list: cardlist) + owner.timelineCollectionView.backgroundView?.isHidden = !cardlist.isEmpty } .store(in: &cancellables) From 9e84389fb525dd7af4f8cc1c90c94ffe6e89972f Mon Sep 17 00:00:00 2001 From: otoolz Date: Sat, 13 Jan 2024 05:18:02 +0900 Subject: [PATCH 10/47] =?UTF-8?q?chore=20#394:=20TLToastView=20Layout=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/traveline/Sources/DesignSystem/View/TLToastView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/DesignSystem/View/TLToastView.swift b/iOS/traveline/Sources/DesignSystem/View/TLToastView.swift index 4eca813..1b5e282 100644 --- a/iOS/traveline/Sources/DesignSystem/View/TLToastView.swift +++ b/iOS/traveline/Sources/DesignSystem/View/TLToastView.swift @@ -65,7 +65,7 @@ final class TLToastView: UIView { translatesAutoresizingMaskIntoConstraints = false alpha = 1.0 NSLayoutConstraint.activate([ - bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -24), + bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor, constant: -24), leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: Metric.margin), trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -Metric.margin), heightAnchor.constraint(equalToConstant: Metric.toastHeight) From e2afd474f3d7ada7f1a1a5876fef66121a5929e4 Mon Sep 17 00:00:00 2001 From: otoolz Date: Sat, 13 Jan 2024 05:20:09 +0900 Subject: [PATCH 11/47] =?UTF-8?q?feat=20#394:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=A0=9C=EB=AA=A9=20=EA=B8=80=EC=9E=90?= =?UTF-8?q?=EC=88=98=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TravelFeature/TravelScene/VC/TravelVC.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift b/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift index 7a87e20..9a6d580 100644 --- a/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift +++ b/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift @@ -20,6 +20,8 @@ final class TravelVC: UIViewController { } private enum Constants { + static let titleLimit: Int = 14 + static let titleLimitToastMessage = "제목은 1 - 14자 이내만 가능합니다." static let title: String = "여행 생성" static let textFieldPlaceholder: String = "제목 *" static let done: String = "완료" @@ -144,6 +146,7 @@ final class TravelVC: UIViewController { private extension TravelVC { func setupAttributes() { + view.keyboardLayoutGuide.followsUndockedKeyboard = true view.backgroundColor = TLColor.black titleTextField.placeholder = Constants.textFieldPlaceholder baseScrollView.delegate = self @@ -354,6 +357,14 @@ extension TravelVC: UITextFieldDelegate { dismissKeyboard() return true } + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + let text = textField.text ?? "" + if text.count + string.count > Constants.titleLimit { + return false + } + return true + } } // MARK: - TLBottomSheetDelegate From 7bf9a3a54e0251457fdff8d9dde50396dff7c982 Mon Sep 17 00:00:00 2001 From: otoolz Date: Sat, 13 Jan 2024 05:22:04 +0900 Subject: [PATCH 12/47] =?UTF-8?q?feat=20#394:=20toast=20view=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift b/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift index 9a6d580..36881e8 100644 --- a/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift +++ b/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift @@ -361,6 +361,8 @@ extension TravelVC: UITextFieldDelegate { func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let text = textField.text ?? "" if text.count + string.count > Constants.titleLimit { + let textLimitToast = TLToastView(type: .failure, message: Constants.titleLimitToastMessage) + textLimitToast.show(in: self.view) return false } return true From 0e42acc7cf66abe35f5c0a0098ad52617db2a13d Mon Sep 17 00:00:00 2001 From: kth1210 Date: Sun, 14 Jan 2024 11:36:55 +0900 Subject: [PATCH 13/47] =?UTF-8?q?fix=20#383:=20=EC=88=98=EC=A0=95=20->=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20=EC=8B=9C=20=EB=8B=A4=EC=8B=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=ED=99=94=EB=A9=B4=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=ED=95=A0=20=EC=88=98=20=EC=97=86=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 화면 이동 시 isEdit을 초기화하는 코드 추가 --- .../TimelineDetailScene/VC/TimelineDetailVC.swift | 2 +- .../ViewModel/TimelineDetailViewModel.swift | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift index 15edcb0..990ddeb 100644 --- a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift @@ -235,7 +235,6 @@ private extension TimelineDetailVC { viewModel.state .map(\.isEdit) .filter { $0 } - .removeDuplicates() .withUnretained(self) .sink { owner, _ in let timelineDetailInfo = owner.viewModel.currentState.timelineDetailInfo @@ -246,6 +245,7 @@ private extension TimelineDetailVC { timelineDetailInfo: timelineDetailInfo ) owner.navigationController?.pushViewController(timelineEditVC, animated: true) + owner.viewModel.sendAction(.movedToEdit) } .store(in: &cancellables) diff --git a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/ViewModel/TimelineDetailViewModel.swift b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/ViewModel/TimelineDetailViewModel.swift index 9f1902f..13ce77d 100644 --- a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/ViewModel/TimelineDetailViewModel.swift +++ b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/ViewModel/TimelineDetailViewModel.swift @@ -14,6 +14,7 @@ enum TimelineDetailAction: BaseAction { case editTimeline case deleteTimeline case translateTimeline + case movedToEdit } enum TimelineDetailSideEffect: BaseSideEffect { @@ -34,6 +35,7 @@ enum TimelineDetailSideEffect: BaseSideEffect { case popToTimeline(Bool) case showTimelineDetailEditing case loadTimelineTranslatedInfo(TimelineTranslatedInfo) + case resetIsEditStatus } struct TimelineDetailState: BaseState { @@ -68,6 +70,9 @@ final class TimelineDetailViewModel: BaseViewModel Date: Sun, 14 Jan 2024 12:18:08 +0900 Subject: [PATCH 14/47] =?UTF-8?q?fix=20#383:=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20=EC=8B=9C=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20=EC=9D=B4=EB=AF=B8=EC=A7=80=EA=B0=80=20=EB=85=B8?= =?UTF-8?q?=EC=B6=9C=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SelectImageButton에서 hasImage 프로퍼티의 업데이트 타이밍과 updateView 함수 호출의 타이밍이 맞지 않아 발생한 문제였음 --- .../VC/TimelineWritingVC.swift | 2 +- .../View/SelectImageButton.swift | 23 +++++++------------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift index 8ee8ad6..a918176 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift @@ -165,7 +165,6 @@ final class TimelineWritingVC: UIViewController { @objc private func imageButtonCancelTapped() { selectImageButton.setImage(nil) viewModel.sendAction(.imageDidChange(nil)) - selectImageButton.updateView() } @objc private func locationButtonCancelTapped() { @@ -336,6 +335,7 @@ private extension TimelineWritingVC { viewModel.state .map(\.imageURLString) + .removeDuplicates() .withUnretained(self) .sink { owner, imageURLString in owner.selectImageButton.setImage(urlString: imageURLString) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/View/SelectImageButton.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/View/SelectImageButton.swift index 5b44f1d..547ade5 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/View/SelectImageButton.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/View/SelectImageButton.swift @@ -21,7 +21,7 @@ final class SelectImageButton: UIView { // MARK: - UI Components - let view: UIView = { + private let view: UIView = { let view = UIView() view.layer.cornerRadius = Metric.cornerRadius view.clipsToBounds = true @@ -41,7 +41,7 @@ final class SelectImageButton: UIView { let imageView: UIImageView = .init() - let selectView: UIStackView = { + private let selectView: UIStackView = { let view = UIStackView() view.axis = .vertical view.alignment = .center @@ -51,26 +51,19 @@ final class SelectImageButton: UIView { return view }() - let selectViewIcon: UIImageView = { + private let selectViewIcon: UIImageView = { let view = UIImageView(image: TLImage.Common.album) view.contentMode = .scaleAspectFit return view }() - let selectViewLabel: TLLabel = { + private let selectViewLabel: TLLabel = { let label = TLLabel(font: TLFont.body2, color: TLColor.white) label.textAlignment = .center return label }() - // MARK: - Properties - - private var hasImage: Bool { - imageView.image != nil - } - let width = Metric.viewWidth + Metric.buttonOffset - // MARK: - initialize init() { @@ -86,7 +79,7 @@ final class SelectImageButton: UIView { // MARK: - Functions - func updateView() { + private func updateView(hasImage: Bool) { selectView.isHidden = hasImage imageView.isHidden = !hasImage cancelButton.isHidden = !hasImage @@ -94,12 +87,12 @@ final class SelectImageButton: UIView { func setImage(_ image: UIImage?) { imageView.image = image - updateView() + updateView(hasImage: image != nil) } func setImage(urlString: String?, imagePath: String? = nil) { imageView.setImage(from: urlString, imagePath: imagePath) - updateView() + updateView(hasImage: urlString != nil) } } @@ -111,7 +104,7 @@ private extension SelectImageButton { func setupAttributes() { selectViewLabel.setText(to: "선택") view.backgroundColor = TLColor.backgroundGray - updateView() + updateView(hasImage: false) } func setupLayout() { From f8503c22f34edd71813cccc3847a3210f01402d4 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Sun, 14 Jan 2024 13:41:27 +0900 Subject: [PATCH 15/47] =?UTF-8?q?fix=20#383:=20=EC=A7=80=EC=86=8D=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20=EC=8B=9C=20=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=EC=83=98=ED=94=8C=EB=A7=81=EC=9D=B4=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A0=81=EC=9A=A9=EB=90=98=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존의 이미지인지 확인하는 state 프로퍼티 추가 --- .../TimelineWritingScene/VC/TimelineWritingVC.swift | 5 ++++- .../ViewModel/TimelineWritingViewModel.swift | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift index a918176..18049a3 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift @@ -465,7 +465,10 @@ extension TimelineWritingVC: LocationSearchDelegate { extension TimelineWritingVC: TLNavigationBarDelegate { func rightButtonDidTapped() { if let selectedImage = selectImageButton.imageView.image { - let image = selectedImage.downSampling() + var image: UIImage? = selectedImage + if !viewModel.currentState.isOriginImage { + image = image?.downSampling() + } let imageData = image?.jpegData(compressionQuality: 1) viewModel.sendAction(.tapCompleteButton(imageData)) } else { diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift index d8184db..039474a 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift @@ -55,6 +55,7 @@ enum TimelineWritingSideEffect: BaseSideEffect { } struct TimelineWritingState: BaseState { + var isOriginImage: Bool = false var isCompletable: Bool = false var timelineDetailRequest: TimelineDetailRequest = .empty var popToTimeline: Bool = false @@ -190,6 +191,7 @@ final class TimelineWritingViewModel: BaseViewModel Date: Sun, 14 Jan 2024 13:55:17 +0900 Subject: [PATCH 16/47] =?UTF-8?q?fix=20#383:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=EB=A5=BC=20=EB=B3=80=EA=B2=BD=ED=96=88=EC=9D=84=20=EB=95=8C=20?= =?UTF-8?q?=EB=8B=A4=EC=9A=B4=EC=83=98=ED=94=8C=EB=A7=81=EC=9D=B4=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이미지가 변경되었음을 알리는 action 추가 --- .../TimelineWritingScene/VC/TimelineWritingVC.swift | 10 +++++++--- .../ViewModel/TimelineWritingViewModel.swift | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift index 18049a3..eb359fb 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift @@ -163,8 +163,7 @@ final class TimelineWritingVC: UIViewController { } @objc private func imageButtonCancelTapped() { - selectImageButton.setImage(nil) - viewModel.sendAction(.imageDidChange(nil)) + changeImage(to: nil) } @objc private func locationButtonCancelTapped() { @@ -172,6 +171,11 @@ final class TimelineWritingVC: UIViewController { viewModel.sendAction(.placeDidChange(.emtpy)) } + private func changeImage(to image: UIImage?) { + selectImageButton.setImage(image) + viewModel.sendAction(.imageDidChange) + } + private func actionKeyboardWillShow(_ keyboardFrame: CGRect) { self.scrollView.contentInset.bottom = keyboardFrame.size.height scrollView.scrollRectToVisible(textView.frame, animated: true) @@ -439,7 +443,7 @@ extension TimelineWritingVC: PHPickerViewControllerDelegate { guard let self = self else { return } DispatchQueue.main.async { guard let selectedImage = image as? UIImage else { return } - self.selectImageButton.setImage(selectedImage) + self.changeImage(to: selectedImage) } } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift index 039474a..b15661a 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift @@ -17,7 +17,7 @@ enum TimelineWritingAction: BaseAction { case metaDataTime(String) case searchPlace(String) case placeDidChange(TimelinePlace) - case imageDidChange(Data?) + case imageDidChange case placeDidScrollToBottom case tapCompleteButton(Data?) case configTimelineDetailInfo(TimelineDetailInfo) @@ -43,7 +43,7 @@ enum TimelineWritingSideEffect: BaseSideEffect { case updateTitleState(String) case updateContentState(String) case updateTimeState(String) - case updateImageState(Data?) + case updateImageState case updatePlaceState(TimelinePlace) case updatePlaceKeyword(String) case fetchPlaceList(TimelinePlaceList) @@ -114,8 +114,8 @@ final class TimelineWritingViewModel: BaseViewModel Date: Sun, 14 Jan 2024 14:17:46 +0900 Subject: [PATCH 17/47] =?UTF-8?q?fix=20#383:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=EB=A7=8C=20=EB=B3=80=EA=B2=BD=EB=90=90=EC=9D=84=20=EC=8B=9C=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20=EB=B2=84=ED=8A=BC=20=ED=99=9C=EC=84=B1?= =?UTF-8?q?=ED=99=94=20=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=98=84?= =?UTF-8?q?=EC=83=81=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/TimelineWritingViewModel.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift index b15661a..35815dc 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift @@ -188,6 +188,7 @@ final class TimelineWritingViewModel: BaseViewModel Date: Sun, 14 Jan 2024 22:58:34 +0900 Subject: [PATCH 18/47] =?UTF-8?q?add=20#384:=20=EA=B2=80=EC=83=89=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=EC=9A=A9=20=EC=97=A0=ED=8B=B0=EB=B7=B0=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/errorCircle.imageset/Contents.json | 23 ++++++++++++++++++ .../Glyph_ undefined 1.png | Bin 0 -> 2696 bytes .../Glyph_ undefined 2.png | Bin 0 -> 4223 bytes .../errorCircle.imageset/Glyph_ undefined.png | Bin 0 -> 1509 bytes .../Sources/DesignSystem/Common/TLImage.swift | 1 + 5 files changed, 24 insertions(+) create mode 100644 iOS/traveline/Resources/Images.xcassets/Common/errorCircle.imageset/Contents.json create mode 100644 iOS/traveline/Resources/Images.xcassets/Common/errorCircle.imageset/Glyph_ undefined 1.png create mode 100644 iOS/traveline/Resources/Images.xcassets/Common/errorCircle.imageset/Glyph_ undefined 2.png create mode 100644 iOS/traveline/Resources/Images.xcassets/Common/errorCircle.imageset/Glyph_ undefined.png diff --git a/iOS/traveline/Resources/Images.xcassets/Common/errorCircle.imageset/Contents.json b/iOS/traveline/Resources/Images.xcassets/Common/errorCircle.imageset/Contents.json new file mode 100644 index 0000000..b2a331d --- /dev/null +++ b/iOS/traveline/Resources/Images.xcassets/Common/errorCircle.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Glyph_ undefined.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Glyph_ undefined 1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Glyph_ undefined 2.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/traveline/Resources/Images.xcassets/Common/errorCircle.imageset/Glyph_ undefined 1.png b/iOS/traveline/Resources/Images.xcassets/Common/errorCircle.imageset/Glyph_ undefined 1.png new file mode 100644 index 0000000000000000000000000000000000000000..c63e2fcd26d82f3ba0e7d6d51e92f695c8b17793 GIT binary patch literal 2696 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 Date: Sun, 14 Jan 2024 23:07:34 +0900 Subject: [PATCH 19/47] =?UTF-8?q?chore=20#384:=20EmptyView=20=EB=B6=84?= =?UTF-8?q?=EA=B8=B0=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DesignSystem/View/TLEmptyView.swift | 57 +++++++++++++++---- .../TimelineScene/VC/TimelineVC.swift | 2 +- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift b/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift index 9c5e316..bc73405 100644 --- a/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift +++ b/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift @@ -10,9 +10,36 @@ import UIKit class TLEmptyView: UIView { - private enum Constants { - static let emptyText: String = "아직 작성된 글이 없어요!" - static let shareText: String = "나만의 여행 경험을 공유해보세요 :)" + enum EmptyViewType { + case search + case timeline + + var image: UIImage { + switch self { + case .search: + return TLImage.Common.errorCircle + case .timeline: + return TLImage.Common.empty + } + } + + var firstText: String { + switch self { + case .search: + return "검색 결과가 없어요!" + case .timeline: + return "아직 작성된 글이 없어요!" + } + } + + var secondText: String { + switch self { + case .search: + return "다른 키워드로 검색해보세요 :)" + case .timeline: + return "나만의 여행 경험을 공유해보세요 :)" + } + } } private enum Metric { @@ -34,31 +61,34 @@ class TLEmptyView: UIView { }() private let imageView: UIImageView = { - let view = UIImageView(image: TLImage.Common.empty) + let view = UIImageView() view.translatesAutoresizingMaskIntoConstraints = false return view }() - private let emptyLabel: TLLabel = { + private let firstLabel: TLLabel = { let label = TLLabel(font: .subtitle2, color: TLColor.white) - label.setText(to: Constants.emptyText) label.translatesAutoresizingMaskIntoConstraints = false return label }() - private let shareLabel: TLLabel = { + private let secondLabel: TLLabel = { let label = TLLabel(font: .body2, color: TLColor.white) - label.setText(to: Constants.shareText) label.translatesAutoresizingMaskIntoConstraints = false return label }() + // MARK: - properties + + private let emptyViewType: EmptyViewType + // MARK: - initialize - init() { + init(type: EmptyViewType) { + self.emptyViewType = type super.init(frame: .zero) setupAttributes() @@ -77,14 +107,17 @@ private extension TLEmptyView { func setupAttributes() { backgroundColor = TLColor.black + imageView.image = emptyViewType.image + firstLabel.setText(to: emptyViewType.firstText) + secondLabel.setText(to: emptyViewType.secondText) } func setupLayout() { addSubview(stackView) stackView.addArrangedSubviews( imageView, - emptyLabel, - shareLabel + firstLabel, + secondLabel ) NSLayoutConstraint.activate([ @@ -95,7 +128,7 @@ private extension TLEmptyView { ]) stackView.setCustomSpacing(Metric.imageToLabelSpacing, after: imageView) - stackView.setCustomSpacing(Metric.labelToLabelSpacing, after: emptyLabel) + stackView.setCustomSpacing(Metric.labelToLabelSpacing, after: firstLabel) } } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift index e25ed06..85b3590 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift @@ -52,7 +52,7 @@ final class TimelineVC: UIViewController { return collectionView }() - private let emptyView: TLEmptyView = .init() + private let emptyView: TLEmptyView = .init(type: .timeline) private let createPostingButton: TLFloatingButton = .init(style: .create) From e08014b79136fae1fb6681c152a400bee05113e3 Mon Sep 17 00:00:00 2001 From: otoolz Date: Mon, 15 Jan 2024 06:03:43 +0900 Subject: [PATCH 20/47] =?UTF-8?q?feat=20#384:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=97=A0=ED=8B=B0=EB=B7=B0=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HomeFeature/HomeScene/VC/HomeVC.swift | 5 +++++ .../HomeScene/View/HomeListView.swift | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift index 6e9dcf9..b8527c3 100644 --- a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift +++ b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift @@ -161,6 +161,11 @@ private extension HomeVC { .withUnretained(self) .sink { owner, list in owner.homeListView.setupData(list: list) + if list.isEmpty { + owner.homeListView.showEmptyView() + } else { + owner.homeListView.hideEmptyView() + } } .store(in: &cancellables) diff --git a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/View/HomeListView.swift b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/View/HomeListView.swift index 0cd7e35..849462f 100644 --- a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/View/HomeListView.swift +++ b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/View/HomeListView.swift @@ -66,6 +66,8 @@ final class HomeListView: UIView { return refresh }() + private let emptyView: TLEmptyView = .init(type: .search) + // MARK: - Properties private typealias DataSource = UICollectionViewDiffableDataSource @@ -94,6 +96,7 @@ final class HomeListView: UIView { setupLayout() setupDataSource() + setupAttributes() setupSnapshot() } @@ -224,6 +227,14 @@ final class HomeListView: UIView { } } + func hideEmptyView() { + homeCollectionView.backgroundView?.isHidden = true + } + + func showEmptyView() { + homeCollectionView.backgroundView?.isHidden = false + } + @objc private func refreshList() { didRefreshHomeList.send(Void()) } @@ -232,6 +243,11 @@ final class HomeListView: UIView { // MARK: - Setup Functions extension HomeListView { + private func setupAttributes() { + homeCollectionView.backgroundView = emptyView + homeCollectionView.backgroundView?.isHidden = true + } + private func setupLayout() { addSubviews(homeCollectionView) From 8c4bfc56a97f6ddff2baaa69973f038ac34da368 Mon Sep 17 00:00:00 2001 From: otoolz Date: Tue, 16 Jan 2024 00:35:40 +0900 Subject: [PATCH 21/47] =?UTF-8?q?chore=20#384:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=8B=9C=20=EC=97=A0=ED=8B=B0=EB=B7=B0=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/DesignSystem/View/TLEmptyView.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift b/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift index bc73405..65dac97 100644 --- a/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift +++ b/iOS/traveline/Sources/DesignSystem/View/TLEmptyView.swift @@ -40,12 +40,20 @@ class TLEmptyView: UIView { return "나만의 여행 경험을 공유해보세요 :)" } } + + var bottomConstants: CGFloat { + switch self { + case .search: + return UIScreen.main.bounds.width + case .timeline: + return UIScreen.main.bounds.width / 3 * 2 + } + } } private enum Metric { static let imageToLabelSpacing: CGFloat = 16 static let labelToLabelSpacing: CGFloat = 12 - static let bottomConstants: CGFloat = UIScreen.main.bounds.width / 3 * 2 } // MARK: - UI Components @@ -124,7 +132,7 @@ private extension TLEmptyView { stackView.topAnchor.constraint(equalTo: imageView.topAnchor), stackView.leadingAnchor.constraint(equalTo: leadingAnchor), stackView.trailingAnchor.constraint(equalTo: trailingAnchor), - stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Metric.bottomConstants) + stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -emptyViewType.bottomConstants) ]) stackView.setCustomSpacing(Metric.imageToLabelSpacing, after: imageView) From d82b7fd7ca21878660c8965be16224059b00fe3e Mon Sep 17 00:00:00 2001 From: kth1210 Date: Tue, 16 Jan 2024 01:14:01 +0900 Subject: [PATCH 22/47] =?UTF-8?q?chore=20#383:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20-=20=EC=82=BC=ED=95=AD=20=EC=97=B0?= =?UTF-8?q?=EC=82=B0=EC=9E=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimelineWritingScene/VC/TimelineWritingVC.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift index eb359fb..0d56b06 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift @@ -469,10 +469,7 @@ extension TimelineWritingVC: LocationSearchDelegate { extension TimelineWritingVC: TLNavigationBarDelegate { func rightButtonDidTapped() { if let selectedImage = selectImageButton.imageView.image { - var image: UIImage? = selectedImage - if !viewModel.currentState.isOriginImage { - image = image?.downSampling() - } + let image = viewModel.currentState.isOriginImage ? selectedImage : selectedImage.downSampling() let imageData = image?.jpegData(compressionQuality: 1) viewModel.sendAction(.tapCompleteButton(imageData)) } else { From afec52015aef35f23880b940519ad4000438de1f Mon Sep 17 00:00:00 2001 From: otoolz Date: Tue, 16 Jan 2024 02:09:44 +0900 Subject: [PATCH 23/47] =?UTF-8?q?chore=20#384:=20loading=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=ED=9B=84=20=EC=97=A0=ED=8B=B0=20=EB=B7=B0=20?= =?UTF-8?q?=EB=9C=A8=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimelineFeature/TimelineScene/VC/TimelineVC.swift | 10 +++++++++- .../TimelineScene/ViewModel/TimelineViewModel.swift | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift index 85b3590..49a258c 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift @@ -148,6 +148,7 @@ private extension TimelineVC { func setupAttributes() { view.backgroundColor = TLColor.black timelineCollectionView.backgroundView = emptyView + timelineCollectionView.backgroundView?.isHidden = true } func setupLayout() { @@ -202,7 +203,6 @@ private extension TimelineVC { .withUnretained(self) .sink { owner, cardlist in owner.setupData(list: cardlist) - owner.timelineCollectionView.backgroundView?.isHidden = !cardlist.isEmpty } .store(in: &cancellables) @@ -249,6 +249,14 @@ private extension TimelineVC { owner.navigationController?.popViewController(animated: true) } .store(in: &cancellables) + + viewModel.state + .map(\.isEmptyList) + .withUnretained(self) + .sink { owner, isEmptyList in + owner.timelineCollectionView.backgroundView?.isHidden = !isEmptyList + } + .store(in: &cancellables) } } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/ViewModel/TimelineViewModel.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/ViewModel/TimelineViewModel.swift index 1bab99b..bc7bf70 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/ViewModel/TimelineViewModel.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/ViewModel/TimelineViewModel.swift @@ -68,6 +68,7 @@ struct TimelineState: BaseState { var isEdit: Bool = false var deleteCompleted: Bool = false var errorMsg: String? + var isEmptyList: Bool = false } final class TimelineViewModel: BaseViewModel { @@ -141,6 +142,7 @@ final class TimelineViewModel: BaseViewModel Date: Tue, 16 Jan 2024 02:36:15 +0900 Subject: [PATCH 24/47] =?UTF-8?q?chore=20#384:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=97=A0=ED=8B=B0=EB=B7=B0=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=A3=BC=EA=B8=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HomeFeature/HomeScene/VC/HomeVC.swift | 19 +++++++++++++------ .../HomeScene/ViewModel/HomeState.swift | 1 + .../HomeScene/ViewModel/HomeViewModel.swift | 3 +++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift index b8527c3..2cb3895 100644 --- a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift +++ b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift @@ -113,6 +113,7 @@ private extension HomeVC { self.navigationItem.backBarButtonItem = backBarButtonItem homeSearchView.isHidden = true + homeListView.hideEmptyView() } func setupLayout() { @@ -156,16 +157,10 @@ private extension HomeVC { viewModel.state .map(\.travelList) - .dropFirst() .removeDuplicates() .withUnretained(self) .sink { owner, list in owner.homeListView.setupData(list: list) - if list.isEmpty { - owner.homeListView.showEmptyView() - } else { - owner.homeListView.hideEmptyView() - } } .store(in: &cancellables) @@ -254,6 +249,18 @@ private extension HomeVC { owner.navigationController?.pushViewController(travelVC, animated: true) } .store(in: &cancellables) + + viewModel.state + .map(\.isEmptyResult) + .withUnretained(self) + .sink { owner, isEmpty in + if isEmpty { + owner.homeListView.showEmptyView() + } else { + owner.homeListView.hideEmptyView() + } + } + .store(in: &cancellables) } func bindListView() { diff --git a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/ViewModel/HomeState.swift b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/ViewModel/HomeState.swift index f890b40..7f46da4 100644 --- a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/ViewModel/HomeState.swift +++ b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/ViewModel/HomeState.swift @@ -27,6 +27,7 @@ struct HomeState: BaseState { var resultFilters: FilterDictionary = .make() var curFilter: Filter? = .emtpy var moveToTravelWriting: Bool = false + var isEmptyResult: Bool = false var isSearching: Bool { homeViewType == .recent || homeViewType == .related diff --git a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/ViewModel/HomeViewModel.swift b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/ViewModel/HomeViewModel.swift index 6851543..853f57d 100644 --- a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/ViewModel/HomeViewModel.swift +++ b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/ViewModel/HomeViewModel.swift @@ -90,6 +90,7 @@ final class HomeViewModel: BaseViewModel newState.travelList = searchResult.travelList newState.homeViewType = .result newState.resultFilters = .make() + newState.isEmptyResult = searchResult.travelList.isEmpty newState.searchQuery = .init( keyword: searchResult.keyword, offset: 2 @@ -99,10 +100,12 @@ final class HomeViewModel: BaseViewModel newState.travelList = travelList newState.searchQuery.offset = 2 newState.searchQuery.keyword = nil + newState.isEmptyResult = travelList.isEmpty case let .showNewList(travelList): newState.travelList = travelList newState.searchQuery.offset = 2 + newState.isEmptyResult = travelList.isEmpty case let .showFilter(type): newState.curFilter = (state.homeViewType == .home) ? state.homeFilters[type] : state.resultFilters[type] From 35997984af54484beb9ab9d9edff2eb4ed92e2af Mon Sep 17 00:00:00 2001 From: kth1210 Date: Tue, 16 Jan 2024 16:26:12 +0900 Subject: [PATCH 25/47] =?UTF-8?q?chore=20#401:=20DTO,=20Model=EC=97=90?= =?UTF-8?q?=EC=84=9C=20place=20nullable=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Network/DataMapping/TimelineDetailResponseDTO.swift | 2 +- .../Sources/Data/Network/DataMapping/TimelineResponseDTO.swift | 2 +- .../Sources/Domain/Model/Timeline/TimelineCardInfo.swift | 2 +- .../Sources/Domain/Model/Timeline/TimelineDetailInfo.swift | 2 +- .../Sources/Domain/UseCase/TimelineWritingUseCase.swift | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/iOS/traveline/Sources/Data/Network/DataMapping/TimelineDetailResponseDTO.swift b/iOS/traveline/Sources/Data/Network/DataMapping/TimelineDetailResponseDTO.swift index 98f01f1..7dbaa0d 100644 --- a/iOS/traveline/Sources/Data/Network/DataMapping/TimelineDetailResponseDTO.swift +++ b/iOS/traveline/Sources/Data/Network/DataMapping/TimelineDetailResponseDTO.swift @@ -18,7 +18,7 @@ struct TimelineDetailResponseDTO: Decodable { let coordX: Double? let coordY: Double? let date: String - let place: String + let place: String? let time: String let isOwner: Bool let posting: PostingID diff --git a/iOS/traveline/Sources/Data/Network/DataMapping/TimelineResponseDTO.swift b/iOS/traveline/Sources/Data/Network/DataMapping/TimelineResponseDTO.swift index aed7eca..bc0550e 100644 --- a/iOS/traveline/Sources/Data/Network/DataMapping/TimelineResponseDTO.swift +++ b/iOS/traveline/Sources/Data/Network/DataMapping/TimelineResponseDTO.swift @@ -22,7 +22,7 @@ struct TimelineResponseDTO: Decodable { let imagePath: String? let coordX: Double? let coordY: Double? - let place: String + let place: String? let time: String } diff --git a/iOS/traveline/Sources/Domain/Model/Timeline/TimelineCardInfo.swift b/iOS/traveline/Sources/Domain/Model/Timeline/TimelineCardInfo.swift index 62e55c8..ca11e65 100644 --- a/iOS/traveline/Sources/Domain/Model/Timeline/TimelineCardInfo.swift +++ b/iOS/traveline/Sources/Domain/Model/Timeline/TimelineCardInfo.swift @@ -15,7 +15,7 @@ struct TimelineCardInfo: Hashable { let thumbnailURL: String? let imagePath: String? let title: String - let place: String + let place: String? let content: String let time: String let latitude: Double? diff --git a/iOS/traveline/Sources/Domain/Model/Timeline/TimelineDetailInfo.swift b/iOS/traveline/Sources/Domain/Model/Timeline/TimelineDetailInfo.swift index 21140bb..23b8dc9 100644 --- a/iOS/traveline/Sources/Domain/Model/Timeline/TimelineDetailInfo.swift +++ b/iOS/traveline/Sources/Domain/Model/Timeline/TimelineDetailInfo.swift @@ -19,7 +19,7 @@ struct TimelineDetailInfo: Hashable { let coordX: Double? let coordY: Double? let date: String - let location: String + let location: String? let time: String let isOwner: Bool diff --git a/iOS/traveline/Sources/Domain/UseCase/TimelineWritingUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/TimelineWritingUseCase.swift index ec588fe..a99cee8 100644 --- a/iOS/traveline/Sources/Domain/UseCase/TimelineWritingUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/TimelineWritingUseCase.swift @@ -55,7 +55,7 @@ final class TimelineWritingUseCaseImpl: TimelineWritingUseCase { time: info.time, date: info.date, place: .init( - title: info.location, + title: info.location ?? Literal.empty, address: Literal.empty, latitude: info.coordY ?? 0.0, longitude: info.coordX ?? 0.0 From c527425f3a4fcc24159a31b653c649021bffb8ee Mon Sep 17 00:00:00 2001 From: kth1210 Date: Tue, 16 Jan 2024 16:38:28 +0900 Subject: [PATCH 26/47] =?UTF-8?q?chore=20#401:=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=83=81=EC=84=B8=ED=99=94=EB=A9=B4=20->?= =?UTF-8?q?=20=EB=93=B1=EB=A1=9D=EB=90=9C=20=EC=9E=A5=EC=86=8C=20=EC=97=86?= =?UTF-8?q?=EC=9D=84=20=EC=8B=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimelineDetailScene/VC/TimelineDetailVC.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift index 990ddeb..7cd4fdb 100644 --- a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift @@ -98,7 +98,12 @@ final class TimelineDetailVC: UIViewController { titleLabel.setText(to: info.title) dateLabel.setText(to: info.date) timeLabel.setText(to: info.time) - locationLabel.setText(to: info.location) + if let location = info.location { + locationLabel.setText(to: location) + locationLabel.isHidden = false + } else { + locationLabel.isHidden = true + } contentView.setText(to: info.description) guard let url = info.imageURL else { imageView.isHidden = true From 0a7287f9b5b3f8484e2fd8ced2a62d99e73b2948 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Tue, 16 Jan 2024 16:38:59 +0900 Subject: [PATCH 27/47] =?UTF-8?q?chore=20#401:=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=B9=B4=EB=93=9C=20->=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=EB=90=9C=20=EC=9E=A5=EC=86=8C=20=EC=9E=88=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=EB=A7=8C=20=EC=9E=A5=EC=86=8C=EB=A5=BC=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimelineFeature/TimelineScene/View/TimelineCardView.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/View/TimelineCardView.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/View/TimelineCardView.swift index 2807024..ee36b4b 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/View/TimelineCardView.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/View/TimelineCardView.swift @@ -53,7 +53,9 @@ final class TimelineCardView: UIView { func setData(cardInfo: TimelineCardInfo) { titleLabel.setText(to: cardInfo.title) - subtitleLabel.setText(to: cardInfo.place) + if let place = cardInfo.place { + subtitleLabel.setText(to: place) + } contentLabel.setText(to: cardInfo.content) thumbnailImageView.setImage(from: cardInfo.thumbnailURL, imagePath: cardInfo.imagePath) thumbnailImageView.isHidden = cardInfo.thumbnailURL == nil From 47bdd3e14b33f9363d65ce12e1d02e4f9cb83389 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Tue, 16 Jan 2024 16:40:01 +0900 Subject: [PATCH 28/47] =?UTF-8?q?chore=20#401:=20=EC=9E=A5=EC=86=8C=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=B2=84=ED=8A=BC=20=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95,=20=ED=83=80=EC=9E=84=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95=20=EC=8B=9C=20=EB=93=B1=EB=A1=9D?= =?UTF-8?q?=EB=90=9C=20=EC=9E=A5=EC=86=8C=EA=B0=80=20=EC=9E=88=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=EB=A7=8C=20=EC=9E=A5=EC=86=8C=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimelineWritingScene/VC/TimelineWritingVC.swift | 4 ++-- .../TimelineWritingScene/View/SelectedLocationButton.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift index 0d56b06..bc6181c 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift @@ -29,7 +29,7 @@ final class TimelineWritingVC: UIViewController { static let contentPlaceholder: String = "내용을 입력해주세요. *" static let complete: String = "완료" static let selectTime: String = "시간선택" - static let selectLocation: String = "선택한 장소" + static let selectLocation: String = "장소 선택" static let alertContentVCKey = "contentViewController" static let metaDataKey = "{Exif}" static let dateKey = "DateTimeOriginal" @@ -331,7 +331,7 @@ private extension TimelineWritingVC { owner.textView.textColor = TLColor.white owner.textView.text = detail.content } - if let place = detail.place { + if let place = detail.place, !place.title.isEmpty { owner.selectLocation.setText(to: place.title) } } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/View/SelectedLocationButton.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/View/SelectedLocationButton.swift index 8666d41..bea6c9a 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/View/SelectedLocationButton.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/View/SelectedLocationButton.swift @@ -11,7 +11,7 @@ import UIKit final class SelectLocationButton: UIView { private enum Constants { - static let defaultText: String = "선택한 장소" + static let defaultText: String = "장소 선택" static let labelHeight: CGFloat = 24 static let spacing: CGFloat = 6 static let buttonSpacing: CGFloat = 2 From 497cd24881b94b944e02793e71fbe4d9980ee5c1 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Mon, 22 Jan 2024 16:29:23 +0900 Subject: [PATCH 29/47] =?UTF-8?q?feat=20#398:=20UIViewController+=20->=20s?= =?UTF-8?q?howToast=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core/Extension/UIViewController+.swift | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 iOS/traveline/Sources/Core/Extension/UIViewController+.swift diff --git a/iOS/traveline/Sources/Core/Extension/UIViewController+.swift b/iOS/traveline/Sources/Core/Extension/UIViewController+.swift new file mode 100644 index 0000000..bc7c0af --- /dev/null +++ b/iOS/traveline/Sources/Core/Extension/UIViewController+.swift @@ -0,0 +1,24 @@ +// +// UIViewController+.swift +// traveline +// +// Created by 김태현 on 1/16/24. +// Copyright © 2024 traveline. All rights reserved. +// + +import UIKit + +extension UIViewController { + + /// 토스트 메세지를 노출합니다. + /// - Parameters: + /// - message: 노출할 메세지 + /// - type: 토스트 메세지 타입 (실패, 성공) + /// - followsUndockedKeyboard: 키보드 위치를 트래킹하려면 true로 설정 + func showToast(message: String, type: TLToastView.ToastType, followsUndockedKeyboard: Bool = false) { + let toast: TLToastView = .init(type: type, message: message, followsUndockedKeyboard: followsUndockedKeyboard) + view.keyboardLayoutGuide.followsUndockedKeyboard = followsUndockedKeyboard + toast.show(in: view) + } + +} From 44920f937803b041ea74e62340593dedb77c704a Mon Sep 17 00:00:00 2001 From: kth1210 Date: Mon, 22 Jan 2024 16:30:02 +0900 Subject: [PATCH 30/47] =?UTF-8?q?chore=20#398:=20TLToastView=20->=20follow?= =?UTF-8?q?sUndockedKeyboard=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 키보드 추적 여부 지정 --- .../Sources/DesignSystem/View/TLToastView.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/iOS/traveline/Sources/DesignSystem/View/TLToastView.swift b/iOS/traveline/Sources/DesignSystem/View/TLToastView.swift index 1b5e282..e3e30df 100644 --- a/iOS/traveline/Sources/DesignSystem/View/TLToastView.swift +++ b/iOS/traveline/Sources/DesignSystem/View/TLToastView.swift @@ -37,12 +37,14 @@ final class TLToastView: UIView { private let toastType: ToastType private var message: String + var followsUndockedKeyboard: Bool // MARK: - initialize - init(type: ToastType = .success, message: String = "") { + init(type: ToastType = .success, message: String = "", followsUndockedKeyboard: Bool = false) { self.toastType = type self.message = message + self.followsUndockedKeyboard = followsUndockedKeyboard super.init(frame: .zero) setupAttributes() @@ -65,7 +67,10 @@ final class TLToastView: UIView { translatesAutoresizingMaskIntoConstraints = false alpha = 1.0 NSLayoutConstraint.activate([ - bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor, constant: -24), + bottomAnchor.constraint( + equalTo: followsUndockedKeyboard ? view.keyboardLayoutGuide.topAnchor : view.safeAreaLayoutGuide.bottomAnchor, + constant: -8 + ), leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: Metric.margin), trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -Metric.margin), heightAnchor.constraint(equalToConstant: Metric.toastHeight) @@ -99,9 +104,8 @@ private extension TLToastView { NSLayoutConstraint.activate([ contentLabel.centerXAnchor.constraint(equalTo: centerXAnchor), - contentLabel.centerYAnchor.constraint(equalTo: centerYAnchor), + contentLabel.centerYAnchor.constraint(equalTo: centerYAnchor) ]) } } - From aa1fb91cc11361362f55039b31783e6364c60c38 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Mon, 22 Jan 2024 16:30:33 +0900 Subject: [PATCH 31/47] =?UTF-8?q?chore=20#398:=20TravelVC=20->=20=ED=86=A0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=B7=B0=20=ED=82=A4=EB=B3=B4=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EC=A0=81=20=EC=97=AC=EB=B6=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift b/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift index 36881e8..3f07a94 100644 --- a/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift +++ b/iOS/traveline/Sources/Feature/TravelFeature/TravelScene/VC/TravelVC.swift @@ -361,7 +361,7 @@ extension TravelVC: UITextFieldDelegate { func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let text = textField.text ?? "" if text.count + string.count > Constants.titleLimit { - let textLimitToast = TLToastView(type: .failure, message: Constants.titleLimitToastMessage) + let textLimitToast = TLToastView(type: .failure, message: Constants.titleLimitToastMessage, followsUndockedKeyboard: true) textLimitToast.show(in: self.view) return false } From 27e94ec097984c93fdb130e4f6cbd28b1fd91bd0 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Mon, 22 Jan 2024 16:33:07 +0900 Subject: [PATCH 32/47] =?UTF-8?q?feat=20#398:=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=9E=91=EC=84=B1=20->=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=99=94=EB=A9=B4=20=EA=B0=84=20=ED=86=A0=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../VC/TimelineDetailVC.swift | 8 ++++++ .../VC/TimelineWritingVC.swift | 26 ++++++++++--------- .../ViewModel/TimelineWritingViewModel.swift | 4 +-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift index 7cd4fdb..6179ce4 100644 --- a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift @@ -267,3 +267,11 @@ private extension TimelineDetailVC { .store(in: &cancellables) } } + +// MARK: - TimelineWriting Delegate + +extension TimelineDetailVC: TimelineWritingDelegate { + func showToast(isSuccess: Bool, message: String) { + showToast(message: message, type: isSuccess ? .success : .failure) + } +} diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift index bc6181c..646f161 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift @@ -10,6 +10,10 @@ import Combine import PhotosUI import UIKit +protocol TimelineWritingDelegate: AnyObject { + func showToast(isSuccess: Bool, message: String) +} + final class TimelineWritingVC: UIViewController { private enum Metric { @@ -33,6 +37,8 @@ final class TimelineWritingVC: UIViewController { static let alertContentVCKey = "contentViewController" static let metaDataKey = "{Exif}" static let dateKey = "DateTimeOriginal" + static let didFinishWritingWithSuccess: String = "타임라인 작성을 완료했어요 !" + static let didFinishWritingWithFailure: String = "타임라인 작성에 실패했어요." } // MARK: - UI Components @@ -74,6 +80,8 @@ final class TimelineWritingVC: UIViewController { private var cancellables: Set = .init() private var viewModel: TimelineWritingViewModel + weak var delegate: TimelineWritingDelegate? + // MARK: - Initialize init(viewModel: TimelineWritingViewModel) { @@ -358,16 +366,6 @@ private extension TimelineWritingVC { } .store(in: &cancellables) - viewModel.state - .map(\.popToTimeline) - .filter { $0 } - .removeDuplicates() - .withUnretained(self) - .sink { owner, _ in - owner.navigationController?.popViewController(animated: true) - } - .store(in: &cancellables) - viewModel.state .map(\.timelineDetailRequest.time) .removeDuplicates() @@ -379,11 +377,15 @@ private extension TimelineWritingVC { viewModel.state .map(\.isEditCompleted) - .filter { $0 } .removeDuplicates() + .dropFirst() .withUnretained(self) - .sink { owner, _ in + .sink { owner, isSuccess in owner.navigationController?.popViewController(animated: true) + owner.delegate?.showToast( + isSuccess: isSuccess, + message: isSuccess ? Constants.didFinishWritingWithSuccess : Constants.didFinishWritingWithFailure + ) } .store(in: &cancellables) } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift index 35815dc..0c8187b 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/ViewModel/TimelineWritingViewModel.swift @@ -162,10 +162,10 @@ final class TimelineWritingViewModel: BaseViewModel Date: Mon, 22 Jan 2024 16:34:48 +0900 Subject: [PATCH 33/47] =?UTF-8?q?feat=20#398:=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=83=81=EC=84=B8=20->=20=ED=83=80?= =?UTF-8?q?=EC=9E=84=EB=9D=BC=EC=9D=B8=20=ED=99=94=EB=A9=B4=20=EA=B0=84=20?= =?UTF-8?q?=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EB=A9=94=EC=84=B8=EC=A7=80=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../VC/TimelineDetailVC.swift | 20 +++++++++++++++++-- .../TimelineScene/VC/TimelineVC.swift | 16 +++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift index 6179ce4..cd09062 100644 --- a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift @@ -9,6 +9,10 @@ import Combine import UIKit +protocol TimelineDetailDelegate: AnyObject { + func showToast(isSuccess: Bool, message: String) +} + final class TimelineDetailVC: UIViewController { private enum Metric { @@ -19,6 +23,11 @@ final class TimelineDetailVC: UIViewController { static let belowLine: CGFloat = 4 } + private enum Constants { + static let didFinishDeleteWithSuccess: String = "타임라인 삭제를 완료했어요 !" + static let didFinishDeleteWithFailure: String = "타임라인 삭제에 실패했어요." + } + // MARK: - UI Components private lazy var tlNavigationBar: TLNavigationBar = .init(vc: self) @@ -65,6 +74,7 @@ final class TimelineDetailVC: UIViewController { private var cancellables: Set = .init() private let viewModel: TimelineDetailViewModel + weak var delegate: TimelineDetailDelegate? // MARK: - Initialize @@ -230,10 +240,15 @@ private extension TimelineDetailVC { viewModel.state .map(\.isDeleteCompleted) - .filter { $0 } + .removeDuplicates() + .dropFirst() .withUnretained(self) - .sink { owner, _ in + .sink { owner, isSuccess in owner.navigationController?.popViewController(animated: true) + owner.delegate?.showToast( + isSuccess: isSuccess, + message: isSuccess ? Constants.didFinishDeleteWithSuccess : Constants.didFinishDeleteWithFailure + ) } .store(in: &cancellables) @@ -249,6 +264,7 @@ private extension TimelineDetailVC { day: timelineDetailInfo.day, timelineDetailInfo: timelineDetailInfo ) + timelineEditVC.delegate = owner owner.navigationController?.pushViewController(timelineEditVC, animated: true) owner.viewModel.sendAction(.movedToEdit) } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift index 49a258c..73627b7 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift @@ -219,12 +219,15 @@ private extension TimelineVC { .compactMap(\.timelineWritingInfo) .withUnretained(self) .sink { owner, info in - let vc = VCFactory.makeTimelineWritingVC( + let timelineWritingVC = VCFactory.makeTimelineWritingVC( id: info.id, date: info.date, day: info.day ) - owner.navigationController?.pushViewController(vc, animated: true) + + timelineWritingVC.delegate = owner + + owner.navigationController?.pushViewController(timelineWritingVC, animated: true) } .store(in: &cancellables) @@ -413,6 +416,7 @@ extension TimelineVC: UICollectionViewDelegate { if indexPath.section == 0 { return } let timelineDetailVC = VCFactory.makeTimelineDetailVC(with: viewModel.currentState.timelineCardList[indexPath.row].detailId) + timelineDetailVC.delegate = self navigationController?.pushViewController(timelineDetailVC, animated: true) } @@ -438,6 +442,14 @@ extension TimelineVC: TimelineDateHeaderDelegate { } } +// MARK: - TimelineWriting, TimelineDetail Delegate + +extension TimelineVC: TimelineWritingDelegate, TimelineDetailDelegate { + func showToast(isSuccess: Bool, message: String) { + showToast(message: message, type: isSuccess ? .success : .failure) + } +} + @available(iOS 17, *) #Preview { UINavigationController(rootViewController: VCFactory.makeTimelineVC(id: .empty)) From bfba7cf969370232055009b8eb1c2491ffd341d9 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Mon, 22 Jan 2024 16:43:11 +0900 Subject: [PATCH 34/47] =?UTF-8?q?feat=20#398:=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20->=20=ED=99=88=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EA=B0=84=20=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HomeFeature/HomeScene/VC/HomeVC.swift | 9 ++++++++ .../TimelineScene/VC/TimelineVC.swift | 21 ++++++++++++++++--- .../ViewModel/TimelineViewModel.swift | 6 +++--- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift index 2cb3895..d537b7c 100644 --- a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift +++ b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift @@ -269,6 +269,7 @@ private extension HomeVC { .sink { owner, idx in let id = owner.viewModel.currentState.travelList[idx].id let timelineVC = VCFactory.makeTimelineVC(id: TravelID(value: id)) + timelineVC.delegate = owner owner.navigationController?.pushViewController( timelineVC, animated: true @@ -354,6 +355,14 @@ extension HomeVC: TLBottomSheetDelegate { } } +// MARK: - Timeline Delegate + +extension HomeVC: TimelineDelegate { + func showToast(isSuccess: Bool, message: String) { + showToast(message: message, type: isSuccess ? .success : .failure) + } +} + @available(iOS 17, *) #Preview("HomeVC") { let homeVC = VCFactory.makeHomeVC() diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift index 73627b7..4d0e471 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift @@ -9,6 +9,10 @@ import Combine import UIKit +protocol TimelineDelegate: AnyObject { + func showToast(isSuccess: Bool, message: String) +} + final class TimelineVC: UIViewController { private enum Metric { @@ -22,6 +26,11 @@ final class TimelineVC: UIViewController { } } + private enum Constants { + static let didFinishDeleteWithSuccess: String = "여행 삭제를 완료했어요 !" + static let didFinishDeleteWithFailure: String = "여행 삭제에 실패했어요." + } + private enum TimelineSection: Int { case travelInfo case timeline @@ -65,6 +74,7 @@ final class TimelineVC: UIViewController { private var cancellables: Set = .init() private let viewModel: TimelineViewModel + weak var delegate: TimelineDelegate? // MARK: - Initializer @@ -245,11 +255,16 @@ private extension TimelineVC { .store(in: &cancellables) viewModel.state - .map(\.deleteCompleted) - .filter { $0 } + .map(\.isDeleteCompleted) + .removeDuplicates() + .dropFirst() .withUnretained(self) - .sink { owner, _ in + .sink { owner, isSuccess in owner.navigationController?.popViewController(animated: true) + owner.delegate?.showToast( + isSuccess: isSuccess, + message: isSuccess ? Constants.didFinishDeleteWithSuccess : Constants.didFinishDeleteWithFailure + ) } .store(in: &cancellables) diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/ViewModel/TimelineViewModel.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/ViewModel/TimelineViewModel.swift index bc7bf70..255bdab 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/ViewModel/TimelineViewModel.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/ViewModel/TimelineViewModel.swift @@ -66,7 +66,7 @@ struct TimelineState: BaseState { var date: String? var timelineWritingInfo: TimelineWritingInfo? var isEdit: Bool = false - var deleteCompleted: Bool = false + var isDeleteCompleted: Bool = false var errorMsg: String? var isEmptyList: Bool = false } @@ -130,7 +130,7 @@ final class TimelineViewModel: BaseViewModel Date: Mon, 22 Jan 2024 16:43:51 +0900 Subject: [PATCH 35/47] =?UTF-8?q?feat=20#398:=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=88=98=EC=A0=95=20->=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EA=B0=84=20=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/App/RootContainerVC.swift | 9 ++++++++ .../VC/ProfileEditingVC.swift | 22 ++++++++++++++++++- .../ViewModel/ProfileEditingViewModel.swift | 8 +++---- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/iOS/traveline/Sources/App/RootContainerVC.swift b/iOS/traveline/Sources/App/RootContainerVC.swift index 4592d20..6919331 100644 --- a/iOS/traveline/Sources/App/RootContainerVC.swift +++ b/iOS/traveline/Sources/App/RootContainerVC.swift @@ -175,6 +175,7 @@ extension RootContainerVC: SideMenuDelegate { switch menuItem { case .profileEdit: let profileEditingVC = VCFactory.makeProfileEditingVC() + profileEditingVC.delegate = self navigationVC?.pushViewController(profileEditingVC, animated: true) case .myPostList: let myPostListVC = VCFactory.makeMyPostListVC() @@ -210,3 +211,11 @@ extension RootContainerVC: UIGestureRecognizerDelegate { return false } } + +// MARK: - TimelineWriting Delegate + +extension RootContainerVC: ProfileEditingDelegate { + func showToast(isSuccess: Bool, message: String) { + showToast(message: message, type: isSuccess ? .success : .failure) + } +} diff --git a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift index b9a290e..950b48a 100644 --- a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift +++ b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift @@ -10,6 +10,10 @@ import Combine import PhotosUI import UIKit +protocol ProfileEditingDelegate: AnyObject { + func showToast(isSuccess: Bool, message: String) +} + final class ProfileEditingVC: UIViewController { private enum Metric { @@ -33,6 +37,8 @@ final class ProfileEditingVC: UIViewController { static let selectBaseImage: String = "기본 이미지" static let selectInAlbum: String = "앨범에서 선택" static let close: String = "닫기" + static let didFinishEditProfileWithSuccess: String = "프로필 수정을 완료했어요 !" + static let didFinishEditProfileWithFailure: String = "프로필 수정에 실패했어요." } // MARK: - UI Components @@ -92,6 +98,7 @@ final class ProfileEditingVC: UIViewController { private var cancellables: Set = .init() private let viewModel: ProfileEditingViewModel + weak var delegate: ProfileEditingDelegate? // MARK: - Initialize @@ -245,6 +252,20 @@ extension ProfileEditingVC { owner.imageView.setImage(from: profile.imageURL, imagePath: profile.imagePath) } .store(in: &cancellables) + + viewModel.state + .map(\.isSuccessEditProfile) + .removeDuplicates() + .dropFirst() + .withUnretained(self) + .sink { owner, isSuccess in + owner.navigationController?.popViewController(animated: true) + owner.delegate?.showToast( + isSuccess: isSuccess, + message: isSuccess ? Constants.didFinishEditProfileWithSuccess : Constants.didFinishEditProfileWithFailure + ) + } + .store(in: &cancellables) } } @@ -277,6 +298,5 @@ extension ProfileEditingVC: PHPickerViewControllerDelegate { extension ProfileEditingVC: TLNavigationBarDelegate { func rightButtonDidTapped() { viewModel.sendAction(.tapCompleteButton(imageView.image?.jpegData(compressionQuality: 1))) - self.navigationController?.popViewController(animated: true) } } diff --git a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/ViewModel/ProfileEditingViewModel.swift b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/ViewModel/ProfileEditingViewModel.swift index 9beaad6..f017fbb 100644 --- a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/ViewModel/ProfileEditingViewModel.swift +++ b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/ViewModel/ProfileEditingViewModel.swift @@ -26,10 +26,10 @@ enum ProfileEditingSideEffect: BaseSideEffect { } struct ProfileEditingState: BaseState { - var isCompletable: Bool = false var profile: Profile = .empty var caption: CaptionOptions = .init(validateType: .unchanged) + var isSuccessEditProfile: Bool = false } struct CaptionOptions { @@ -89,15 +89,15 @@ final class ProfileEditingViewModel: BaseViewModel Date: Tue, 23 Jan 2024 17:38:01 +0900 Subject: [PATCH 36/47] =?UTF-8?q?chore=20#407:=20UserDefaults=EC=97=90=20?= =?UTF-8?q?=EC=B2=AB=20=EC=A7=84=EC=9E=85=20(=EB=A1=9C=EA=B7=B8=EC=9D=B8)?= =?UTF-8?q?=20=EC=97=AC=EB=B6=80=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 자동 로그인 시도 시 검증 --- iOS/traveline/Sources/Core/Constant/UserDefaultsList.swift | 1 + .../Sources/Domain/UseCase/AutoLoginUseCase.swift | 7 +++++++ iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift | 1 + 3 files changed, 9 insertions(+) diff --git a/iOS/traveline/Sources/Core/Constant/UserDefaultsList.swift b/iOS/traveline/Sources/Core/Constant/UserDefaultsList.swift index 7cf7fa3..065a2de 100644 --- a/iOS/traveline/Sources/Core/Constant/UserDefaultsList.swift +++ b/iOS/traveline/Sources/Core/Constant/UserDefaultsList.swift @@ -11,4 +11,5 @@ import Foundation enum UserDefaultsList { @UserDefaultsWrapper(key: "userResponseDTO") static var userResponseDTO @UserDefaultsWrapper<[String]>(key: "recentSearchKeyword") static var recentSearchKeyword + @UserDefaultsWrapper(key: "isFirstEntry") static var isFirstEntry } diff --git a/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift index f21a865..d906514 100644 --- a/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift @@ -22,6 +22,13 @@ final class AutoLoginUseCaseImpl: AutoLoginUseCase { } func requestLogin() -> AnyPublisher { + guard let isFirstEntry = UserDefaultsList.isFirstEntry, + isFirstEntry else { + return Just(false) + .setFailureType(to: Error.self) + .eraseToAnyPublisher() + } + return Future { let accessToken = try await self.repository.refresh() KeychainList.accessToken = accessToken diff --git a/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift index 45ee1e6..745fbf7 100644 --- a/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift @@ -26,6 +26,7 @@ final class LoginUseCaseImpl: LoginUseCase { let tlToken = try await self.repository.appleLogin(with: info) KeychainList.accessToken = tlToken.accessToken KeychainList.refreshToken = tlToken.refreshToken + UserDefaultsList.isFirstEntry = false return true }.eraseToAnyPublisher() } From 1c17a4de8dfbbece289d555ad9e1cb78da046a1d Mon Sep 17 00:00:00 2001 From: kth1210 Date: Tue, 23 Jan 2024 17:46:15 +0900 Subject: [PATCH 37/47] =?UTF-8?q?chore=20#407:=20isFirstEntry=20=EC=97=AC?= =?UTF-8?q?=EB=B6=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift index d906514..96068f7 100644 --- a/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/AutoLoginUseCase.swift @@ -23,7 +23,7 @@ final class AutoLoginUseCaseImpl: AutoLoginUseCase { func requestLogin() -> AnyPublisher { guard let isFirstEntry = UserDefaultsList.isFirstEntry, - isFirstEntry else { + !isFirstEntry else { return Just(false) .setFailureType(to: Error.self) .eraseToAnyPublisher() From 2e0e81cf9bb68ae23bb46784fd9fdfdebaaff1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EC=9D=B8?= <0inn1220@gmail.com> Date: Tue, 23 Jan 2024 21:35:18 +0900 Subject: [PATCH 38/47] =?UTF-8?q?add=20#413:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20=EC=84=9C=EB=B2=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EnvPlugin/ProjectDescriptionHelpers/InfoPlist.swift | 3 ++- iOS/traveline/Sources/Core/Constant/Literal.swift | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/iOS/Plugins/EnvPlugin/ProjectDescriptionHelpers/InfoPlist.swift b/iOS/Plugins/EnvPlugin/ProjectDescriptionHelpers/InfoPlist.swift index 6cdf241..3fef8af 100644 --- a/iOS/Plugins/EnvPlugin/ProjectDescriptionHelpers/InfoPlist.swift +++ b/iOS/Plugins/EnvPlugin/ProjectDescriptionHelpers/InfoPlist.swift @@ -25,6 +25,7 @@ public extension Project { ] ] ], - "BaseURL": "$(BASE_URL)" + "ProdURL": "$(PROD_URL)", + "DevURL": "$(DEV_URL)" ] } diff --git a/iOS/traveline/Sources/Core/Constant/Literal.swift b/iOS/traveline/Sources/Core/Constant/Literal.swift index b6d60c1..0f624dd 100644 --- a/iOS/traveline/Sources/Core/Constant/Literal.swift +++ b/iOS/traveline/Sources/Core/Constant/Literal.swift @@ -13,7 +13,8 @@ enum Literal { static let boundary: String = "Boundary-\(UUID().uuidString)" enum InfoPlistKey { - static let baseURL: String = "BaseURL" + static let devURL: String = "DevURL" + static let prodURL: String = "ProdURL" } enum Tag { From 3bfbef42a962d539a7f56a917641f82ee1175a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EC=9D=B8?= <0inn1220@gmail.com> Date: Tue, 23 Jan 2024 21:37:22 +0900 Subject: [PATCH 39/47] =?UTF-8?q?feat=20#413:=20baseURL=20=EC=A0=84?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=EB=AC=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/traveline/Sources/Data/Network/Base/EndPoint.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/Data/Network/Base/EndPoint.swift b/iOS/traveline/Sources/Data/Network/Base/EndPoint.swift index e217d54..1f10d2d 100644 --- a/iOS/traveline/Sources/Data/Network/Base/EndPoint.swift +++ b/iOS/traveline/Sources/Data/Network/Base/EndPoint.swift @@ -19,7 +19,11 @@ protocol EndPoint { extension EndPoint { var baseURL: String? { - return Bundle.main.object(forInfoDictionaryKey: Literal.InfoPlistKey.baseURL) as? String + #if DEBUG + return Bundle.main.object(forInfoDictionaryKey: Literal.InfoPlistKey.devURL) as? String + #else + return Bundle.main.object(forInfoDictionaryKey: Literal.InfoPlistKey.prodURL) as? String + #endif } var body: Encodable? { From 1f295ddeea38aea94f772f7db4613fdbe234c503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EC=9D=B8?= <0inn1220@gmail.com> Date: Tue, 23 Jan 2024 22:15:00 +0900 Subject: [PATCH 40/47] =?UTF-8?q?fix=20#412:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EC=8B=9C=20=EC=9C=A0=EC=A0=80=20=EA=B4=80=EB=A0=A8=20UserDe?= =?UTF-8?q?faults=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift b/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift index 45ee1e6..9344ca5 100644 --- a/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift +++ b/iOS/traveline/Sources/Domain/UseCase/LoginUseCase.swift @@ -23,6 +23,7 @@ final class LoginUseCaseImpl: LoginUseCase { func requestLogin(with info: AppleLoginRequest) -> AnyPublisher { return Future { + UserDefaultsList.userResponseDTO = nil let tlToken = try await self.repository.appleLogin(with: info) KeychainList.accessToken = tlToken.accessToken KeychainList.refreshToken = tlToken.refreshToken From e3f18496b7baa3a97cfb705bbe00716f4dc5077b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EC=9D=B8?= <0inn1220@gmail.com> Date: Tue, 23 Jan 2024 22:51:08 +0900 Subject: [PATCH 41/47] =?UTF-8?q?fix=20#412:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EB=B7=B0=20scaleAspectFill=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/traveline/Sources/DesignSystem/View/TLList/TLInfoView.swift | 2 ++ .../MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift | 1 + .../Feature/SideFeature/SideScene/View/ProfileLabel.swift | 2 +- .../TimelineFeature/TimelineScene/View/TimelineCardView.swift | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/iOS/traveline/Sources/DesignSystem/View/TLList/TLInfoView.swift b/iOS/traveline/Sources/DesignSystem/View/TLList/TLInfoView.swift index 320e7ba..57b97a4 100644 --- a/iOS/traveline/Sources/DesignSystem/View/TLList/TLInfoView.swift +++ b/iOS/traveline/Sources/DesignSystem/View/TLList/TLInfoView.swift @@ -39,6 +39,7 @@ final class TLInfoView: UIView { imageView.backgroundColor = TLColor.gray imageView.layer.cornerRadius = Metric.radius imageView.clipsToBounds = true + imageView.contentMode = .scaleAspectFill return imageView }() @@ -62,6 +63,7 @@ final class TLInfoView: UIView { imageView.backgroundColor = TLColor.gray imageView.layer.cornerRadius = Metric.profileImageSize / 2 imageView.clipsToBounds = true + imageView.contentMode = .scaleAspectFill return imageView }() diff --git a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift index b9a290e..5abd14c 100644 --- a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift +++ b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift @@ -45,6 +45,7 @@ final class ProfileEditingVC: UIViewController { view.layer.cornerRadius = Metric.imageWidth / 2 view.backgroundColor = TLColor.backgroundGray view.clipsToBounds = true + view.contentMode = .scaleAspectFill return view }() diff --git a/iOS/traveline/Sources/Feature/SideFeature/SideScene/View/ProfileLabel.swift b/iOS/traveline/Sources/Feature/SideFeature/SideScene/View/ProfileLabel.swift index 484476d..15c584b 100644 --- a/iOS/traveline/Sources/Feature/SideFeature/SideScene/View/ProfileLabel.swift +++ b/iOS/traveline/Sources/Feature/SideFeature/SideScene/View/ProfileLabel.swift @@ -22,11 +22,11 @@ final class ProfileLabel: UIView { private let imageView: UIImageView = { let view = UIImageView() - view.image = UIImage(systemName: "person.fill") view.contentMode = .center view.layer.cornerRadius = Metric.imageWidth / 2 view.backgroundColor = TLColor.backgroundGray view.clipsToBounds = true + view.contentMode = .scaleAspectFill return view }() diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/View/TimelineCardView.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/View/TimelineCardView.swift index ee36b4b..3d1b8c2 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/View/TimelineCardView.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/View/TimelineCardView.swift @@ -78,6 +78,7 @@ private extension TimelineCardView { thumbnailImageView.backgroundColor = TLColor.disabledGray thumbnailImageView.layer.cornerRadius = 12.0 thumbnailImageView.clipsToBounds = true + thumbnailImageView.contentMode = .scaleAspectFill } func setupLayout() { From 44cda717121c5cf0ae4e1b1b227b15a5e47cf7cf Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 24 Jan 2024 13:32:07 +0900 Subject: [PATCH 42/47] =?UTF-8?q?chore=20#398:=20Delegate=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=ED=86=B5=EC=9D=BC=20=EB=B0=8F=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UIViewControllerToastDelegate 생성 --- iOS/traveline/Sources/App/RootContainerVC.swift | 4 ++-- .../Delegate/UIViewControllerToastDelegate.swift | 13 +++++++++++++ .../Feature/HomeFeature/HomeScene/VC/HomeVC.swift | 4 ++-- .../ProfileEditingScene/VC/ProfileEditingVC.swift | 8 ++------ .../TimelineDetailScene/VC/TimelineDetailVC.swift | 12 ++++-------- .../TimelineScene/VC/TimelineVC.swift | 12 ++++-------- .../TimelineWritingScene/VC/TimelineWritingVC.swift | 8 ++------ 7 files changed, 29 insertions(+), 32 deletions(-) create mode 100644 iOS/traveline/Sources/Core/Delegate/UIViewControllerToastDelegate.swift diff --git a/iOS/traveline/Sources/App/RootContainerVC.swift b/iOS/traveline/Sources/App/RootContainerVC.swift index 6919331..aede8ef 100644 --- a/iOS/traveline/Sources/App/RootContainerVC.swift +++ b/iOS/traveline/Sources/App/RootContainerVC.swift @@ -214,8 +214,8 @@ extension RootContainerVC: UIGestureRecognizerDelegate { // MARK: - TimelineWriting Delegate -extension RootContainerVC: ProfileEditingDelegate { - func showToast(isSuccess: Bool, message: String) { +extension RootContainerVC: UIViewControllerToastDelegate { + func viewControllerDidFinishAction(isSuccess: Bool, message: String) { showToast(message: message, type: isSuccess ? .success : .failure) } } diff --git a/iOS/traveline/Sources/Core/Delegate/UIViewControllerToastDelegate.swift b/iOS/traveline/Sources/Core/Delegate/UIViewControllerToastDelegate.swift new file mode 100644 index 0000000..12fd454 --- /dev/null +++ b/iOS/traveline/Sources/Core/Delegate/UIViewControllerToastDelegate.swift @@ -0,0 +1,13 @@ +// +// UIViewControllerToastDelegate.swift +// traveline +// +// Created by 김태현 on 1/24/24. +// Copyright © 2024 traveline. All rights reserved. +// + +import Foundation + +protocol UIViewControllerToastDelegate: AnyObject { + func viewControllerDidFinishAction(isSuccess: Bool, message: String) +} diff --git a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift index d537b7c..c263a34 100644 --- a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift +++ b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift @@ -357,8 +357,8 @@ extension HomeVC: TLBottomSheetDelegate { // MARK: - Timeline Delegate -extension HomeVC: TimelineDelegate { - func showToast(isSuccess: Bool, message: String) { +extension HomeVC: UIViewControllerToastDelegate { + func viewControllerDidFinishAction(isSuccess: Bool, message: String) { showToast(message: message, type: isSuccess ? .success : .failure) } } diff --git a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift index 950b48a..2e232d0 100644 --- a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift +++ b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift @@ -10,10 +10,6 @@ import Combine import PhotosUI import UIKit -protocol ProfileEditingDelegate: AnyObject { - func showToast(isSuccess: Bool, message: String) -} - final class ProfileEditingVC: UIViewController { private enum Metric { @@ -98,7 +94,7 @@ final class ProfileEditingVC: UIViewController { private var cancellables: Set = .init() private let viewModel: ProfileEditingViewModel - weak var delegate: ProfileEditingDelegate? + weak var delegate: UIViewControllerToastDelegate? // MARK: - Initialize @@ -260,7 +256,7 @@ extension ProfileEditingVC { .withUnretained(self) .sink { owner, isSuccess in owner.navigationController?.popViewController(animated: true) - owner.delegate?.showToast( + owner.delegate?.viewControllerDidFinishAction( isSuccess: isSuccess, message: isSuccess ? Constants.didFinishEditProfileWithSuccess : Constants.didFinishEditProfileWithFailure ) diff --git a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift index cd09062..09bd4f0 100644 --- a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift @@ -9,10 +9,6 @@ import Combine import UIKit -protocol TimelineDetailDelegate: AnyObject { - func showToast(isSuccess: Bool, message: String) -} - final class TimelineDetailVC: UIViewController { private enum Metric { @@ -74,7 +70,7 @@ final class TimelineDetailVC: UIViewController { private var cancellables: Set = .init() private let viewModel: TimelineDetailViewModel - weak var delegate: TimelineDetailDelegate? + weak var delegate: UIViewControllerToastDelegate? // MARK: - Initialize @@ -245,7 +241,7 @@ private extension TimelineDetailVC { .withUnretained(self) .sink { owner, isSuccess in owner.navigationController?.popViewController(animated: true) - owner.delegate?.showToast( + owner.delegate?.viewControllerDidFinishAction( isSuccess: isSuccess, message: isSuccess ? Constants.didFinishDeleteWithSuccess : Constants.didFinishDeleteWithFailure ) @@ -286,8 +282,8 @@ private extension TimelineDetailVC { // MARK: - TimelineWriting Delegate -extension TimelineDetailVC: TimelineWritingDelegate { - func showToast(isSuccess: Bool, message: String) { +extension TimelineDetailVC: UIViewControllerToastDelegate { + func viewControllerDidFinishAction(isSuccess: Bool, message: String) { showToast(message: message, type: isSuccess ? .success : .failure) } } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift index 4d0e471..99201b9 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift @@ -9,10 +9,6 @@ import Combine import UIKit -protocol TimelineDelegate: AnyObject { - func showToast(isSuccess: Bool, message: String) -} - final class TimelineVC: UIViewController { private enum Metric { @@ -74,7 +70,7 @@ final class TimelineVC: UIViewController { private var cancellables: Set = .init() private let viewModel: TimelineViewModel - weak var delegate: TimelineDelegate? + weak var delegate: UIViewControllerToastDelegate? // MARK: - Initializer @@ -261,7 +257,7 @@ private extension TimelineVC { .withUnretained(self) .sink { owner, isSuccess in owner.navigationController?.popViewController(animated: true) - owner.delegate?.showToast( + owner.delegate?.viewControllerDidFinishAction( isSuccess: isSuccess, message: isSuccess ? Constants.didFinishDeleteWithSuccess : Constants.didFinishDeleteWithFailure ) @@ -459,8 +455,8 @@ extension TimelineVC: TimelineDateHeaderDelegate { // MARK: - TimelineWriting, TimelineDetail Delegate -extension TimelineVC: TimelineWritingDelegate, TimelineDetailDelegate { - func showToast(isSuccess: Bool, message: String) { +extension TimelineVC: UIViewControllerToastDelegate { + func viewControllerDidFinishAction(isSuccess: Bool, message: String) { showToast(message: message, type: isSuccess ? .success : .failure) } } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift index 646f161..fccc777 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift @@ -10,10 +10,6 @@ import Combine import PhotosUI import UIKit -protocol TimelineWritingDelegate: AnyObject { - func showToast(isSuccess: Bool, message: String) -} - final class TimelineWritingVC: UIViewController { private enum Metric { @@ -80,7 +76,7 @@ final class TimelineWritingVC: UIViewController { private var cancellables: Set = .init() private var viewModel: TimelineWritingViewModel - weak var delegate: TimelineWritingDelegate? + weak var delegate: UIViewControllerToastDelegate? // MARK: - Initialize @@ -382,7 +378,7 @@ private extension TimelineWritingVC { .withUnretained(self) .sink { owner, isSuccess in owner.navigationController?.popViewController(animated: true) - owner.delegate?.showToast( + owner.delegate?.viewControllerDidFinishAction( isSuccess: isSuccess, message: isSuccess ? Constants.didFinishWritingWithSuccess : Constants.didFinishWritingWithFailure ) From 02da11ff38344735e6cd8bc095f4153c2e80ccbe Mon Sep 17 00:00:00 2001 From: kth1210 Date: Sun, 28 Jan 2024 14:59:38 +0900 Subject: [PATCH 43/47] =?UTF-8?q?rename=20#398:=20Toast=20Delegate=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/traveline/Sources/App/RootContainerVC.swift | 2 +- ...IViewControllerToastDelegate.swift => ToastDelegate.swift} | 4 ++-- .../Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift | 2 +- .../ProfileEditingScene/VC/ProfileEditingVC.swift | 2 +- .../TimelineDetailScene/VC/TimelineDetailVC.swift | 4 ++-- .../Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift | 4 ++-- .../TimelineWritingScene/VC/TimelineWritingVC.swift | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) rename iOS/traveline/Sources/Core/Delegate/{UIViewControllerToastDelegate.swift => ToastDelegate.swift} (69%) diff --git a/iOS/traveline/Sources/App/RootContainerVC.swift b/iOS/traveline/Sources/App/RootContainerVC.swift index aede8ef..0afadb7 100644 --- a/iOS/traveline/Sources/App/RootContainerVC.swift +++ b/iOS/traveline/Sources/App/RootContainerVC.swift @@ -214,7 +214,7 @@ extension RootContainerVC: UIGestureRecognizerDelegate { // MARK: - TimelineWriting Delegate -extension RootContainerVC: UIViewControllerToastDelegate { +extension RootContainerVC: ToastDelegate { func viewControllerDidFinishAction(isSuccess: Bool, message: String) { showToast(message: message, type: isSuccess ? .success : .failure) } diff --git a/iOS/traveline/Sources/Core/Delegate/UIViewControllerToastDelegate.swift b/iOS/traveline/Sources/Core/Delegate/ToastDelegate.swift similarity index 69% rename from iOS/traveline/Sources/Core/Delegate/UIViewControllerToastDelegate.swift rename to iOS/traveline/Sources/Core/Delegate/ToastDelegate.swift index 12fd454..978974d 100644 --- a/iOS/traveline/Sources/Core/Delegate/UIViewControllerToastDelegate.swift +++ b/iOS/traveline/Sources/Core/Delegate/ToastDelegate.swift @@ -1,5 +1,5 @@ // -// UIViewControllerToastDelegate.swift +// ToastDelegate.swift // traveline // // Created by 김태현 on 1/24/24. @@ -8,6 +8,6 @@ import Foundation -protocol UIViewControllerToastDelegate: AnyObject { +protocol ToastDelegate: AnyObject { func viewControllerDidFinishAction(isSuccess: Bool, message: String) } diff --git a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift index c263a34..6d30b03 100644 --- a/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift +++ b/iOS/traveline/Sources/Feature/HomeFeature/HomeScene/VC/HomeVC.swift @@ -357,7 +357,7 @@ extension HomeVC: TLBottomSheetDelegate { // MARK: - Timeline Delegate -extension HomeVC: UIViewControllerToastDelegate { +extension HomeVC: ToastDelegate { func viewControllerDidFinishAction(isSuccess: Bool, message: String) { showToast(message: message, type: isSuccess ? .success : .failure) } diff --git a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift index 2e232d0..3c78b50 100644 --- a/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift +++ b/iOS/traveline/Sources/Feature/MyPageFeature/ProfileEditingScene/VC/ProfileEditingVC.swift @@ -94,7 +94,7 @@ final class ProfileEditingVC: UIViewController { private var cancellables: Set = .init() private let viewModel: ProfileEditingViewModel - weak var delegate: UIViewControllerToastDelegate? + weak var delegate: ToastDelegate? // MARK: - Initialize diff --git a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift index 09bd4f0..9db12e3 100644 --- a/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineDetailFeature/TimelineDetailScene/VC/TimelineDetailVC.swift @@ -70,7 +70,7 @@ final class TimelineDetailVC: UIViewController { private var cancellables: Set = .init() private let viewModel: TimelineDetailViewModel - weak var delegate: UIViewControllerToastDelegate? + weak var delegate: ToastDelegate? // MARK: - Initialize @@ -282,7 +282,7 @@ private extension TimelineDetailVC { // MARK: - TimelineWriting Delegate -extension TimelineDetailVC: UIViewControllerToastDelegate { +extension TimelineDetailVC: ToastDelegate { func viewControllerDidFinishAction(isSuccess: Bool, message: String) { showToast(message: message, type: isSuccess ? .success : .failure) } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift index 99201b9..6d15bda 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineScene/VC/TimelineVC.swift @@ -70,7 +70,7 @@ final class TimelineVC: UIViewController { private var cancellables: Set = .init() private let viewModel: TimelineViewModel - weak var delegate: UIViewControllerToastDelegate? + weak var delegate: ToastDelegate? // MARK: - Initializer @@ -455,7 +455,7 @@ extension TimelineVC: TimelineDateHeaderDelegate { // MARK: - TimelineWriting, TimelineDetail Delegate -extension TimelineVC: UIViewControllerToastDelegate { +extension TimelineVC: ToastDelegate { func viewControllerDidFinishAction(isSuccess: Bool, message: String) { showToast(message: message, type: isSuccess ? .success : .failure) } diff --git a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift index fccc777..98b579c 100644 --- a/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift +++ b/iOS/traveline/Sources/Feature/TimelineFeature/TimelineWritingScene/VC/TimelineWritingVC.swift @@ -76,7 +76,7 @@ final class TimelineWritingVC: UIViewController { private var cancellables: Set = .init() private var viewModel: TimelineWritingViewModel - weak var delegate: UIViewControllerToastDelegate? + weak var delegate: ToastDelegate? // MARK: - Initialize From 995e20400b1c0dd55dc59ecb0cb76354b2abf4f5 Mon Sep 17 00:00:00 2001 From: otoolz Date: Sun, 28 Jan 2024 15:12:57 +0900 Subject: [PATCH 44/47] =?UTF-8?q?fix=20#416:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EA=B8=B0=EB=B3=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=8B=9C=20=EC=82=AC=EC=9D=B4=EB=93=9C=20=EB=B0=94?= =?UTF-8?q?=20=EC=A6=89=EC=8B=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Feature/SideFeature/SideScene/View/ProfileLabel.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iOS/traveline/Sources/Feature/SideFeature/SideScene/View/ProfileLabel.swift b/iOS/traveline/Sources/Feature/SideFeature/SideScene/View/ProfileLabel.swift index 15c584b..a8a9236 100644 --- a/iOS/traveline/Sources/Feature/SideFeature/SideScene/View/ProfileLabel.swift +++ b/iOS/traveline/Sources/Feature/SideFeature/SideScene/View/ProfileLabel.swift @@ -66,6 +66,10 @@ final class ProfileLabel: UIView { func updateProfile(_ profile: Profile) { idLabel.setText(to: profile.name) + guard profile.imageURL != Literal.empty else { + imageView.image = nil + return + } imageView.setImage(from: profile.imageURL, imagePath: profile.imagePath) } } From 2e284f492d389830a7bb2f9af2c9bc3a3cb520d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EC=9D=B8?= <0inn1220@gmail.com> Date: Sun, 18 Feb 2024 15:17:50 +0900 Subject: [PATCH 45/47] =?UTF-8?q?feat=20#395:=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=95=BD=EA=B4=80,=20=EA=B0=9C=EC=9D=B8=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=EB=B0=A9=EC=B9=A8=20url=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/traveline/Sources/Core/Constant/Literal.swift | 5 +++++ .../MyPageFeature/SettingScene/VC/SettingVC.swift | 11 ++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/iOS/traveline/Sources/Core/Constant/Literal.swift b/iOS/traveline/Sources/Core/Constant/Literal.swift index 0f624dd..bfb034a 100644 --- a/iOS/traveline/Sources/Core/Constant/Literal.swift +++ b/iOS/traveline/Sources/Core/Constant/Literal.swift @@ -202,4 +202,9 @@ enum Literal { static let drive: String = "자차" } } + + enum Setting { + static let termsOfServiceURL = "https://spiky-rat-16e.notion.site/b222abdf800e4a428a25582f2dc36290?pvs=4" + static let privacyPolicyURL = "https://spiky-rat-16e.notion.site/886a133ee0f9473a83d5f3ed8b877498?pvs=4" + } } diff --git a/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift b/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift index 01595f1..dd7875a 100644 --- a/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift +++ b/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift @@ -12,15 +12,13 @@ import SafariServices import UIKit enum ServiceGuideType: String, CaseIterable { - case license = "라이센스" - case termsOfUse = "이용약관" + case termsOfService = "이용약관" case privacyPolicy = "개인정보 처리방침" var link: String { switch self { - case .license: return "https://www.apple.com" - case .termsOfUse: return "https://www.naver.com" - case .privacyPolicy: return "https://www.daum.net" + case .termsOfService: return Literal.Setting.termsOfServiceURL + case .privacyPolicy: return Literal.Setting.privacyPolicyURL } } @@ -188,8 +186,7 @@ extension SettingVC { private func setupLayout() { view.addSubviews(tlNavigationBar, stackView) [ - serviceGuides[.license] ?? UIButton(), - serviceGuides[.termsOfUse] ?? UIButton(), + serviceGuides[.termsOfService] ?? UIButton(), serviceGuides[.privacyPolicy] ?? UIButton(), line, logoutButton, From 1d7c6df635b8ea2bd17ce2d6c1256d85731d1d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EC=9D=B8?= <0inn1220@gmail.com> Date: Sun, 18 Feb 2024 17:19:18 +0900 Subject: [PATCH 46/47] =?UTF-8?q?feat=20#395:=20=EC=98=A4=ED=94=88?= =?UTF-8?q?=EC=86=8C=EC=8A=A4=20=EB=9D=BC=EC=9D=B4=EC=84=A0=EC=8A=A4=20url?= =?UTF-8?q?=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/traveline/Sources/Core/Constant/Literal.swift | 1 + .../Feature/MyPageFeature/SettingScene/VC/SettingVC.swift | 3 +++ 2 files changed, 4 insertions(+) diff --git a/iOS/traveline/Sources/Core/Constant/Literal.swift b/iOS/traveline/Sources/Core/Constant/Literal.swift index bfb034a..610c9ff 100644 --- a/iOS/traveline/Sources/Core/Constant/Literal.swift +++ b/iOS/traveline/Sources/Core/Constant/Literal.swift @@ -206,5 +206,6 @@ enum Literal { enum Setting { static let termsOfServiceURL = "https://spiky-rat-16e.notion.site/b222abdf800e4a428a25582f2dc36290?pvs=4" static let privacyPolicyURL = "https://spiky-rat-16e.notion.site/886a133ee0f9473a83d5f3ed8b877498?pvs=4" + static let openSourceLicenseURL = "https://spiky-rat-16e.notion.site/e8725bd989ea410390b6ef568324a439?pvs=4" } } diff --git a/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift b/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift index dd7875a..1e63819 100644 --- a/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift +++ b/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift @@ -14,11 +14,13 @@ import UIKit enum ServiceGuideType: String, CaseIterable { case termsOfService = "이용약관" case privacyPolicy = "개인정보 처리방침" + case openSourceLicense = "오픈소스 라이선스" var link: String { switch self { case .termsOfService: return Literal.Setting.termsOfServiceURL case .privacyPolicy: return Literal.Setting.privacyPolicyURL + case .openSourceLicense: return Literal.Setting.openSourceLicenseURL } } @@ -188,6 +190,7 @@ extension SettingVC { [ serviceGuides[.termsOfService] ?? UIButton(), serviceGuides[.privacyPolicy] ?? UIButton(), + serviceGuides[.openSourceLicense] ?? UIButton(), line, logoutButton, withdrawalButton From 9b042f381ea8a8c9ba262ecf44e26325b47c0669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EC=9D=B8?= <0inn1220@gmail.com> Date: Sun, 18 Feb 2024 17:19:49 +0900 Subject: [PATCH 47/47] =?UTF-8?q?fix=20#395:=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EA=B8=80=EC=9E=90=20=EC=99=B8=20=EC=98=81=EC=97=AD=20=ED=84=B0?= =?UTF-8?q?=EC=B9=98=ED=95=B4=EB=8F=84=20=EB=8F=99=EC=9E=91=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Feature/MyPageFeature/SettingScene/VC/SettingVC.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift b/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift index 1e63819..a02beb1 100644 --- a/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift +++ b/iOS/traveline/Sources/Feature/MyPageFeature/SettingScene/VC/SettingVC.swift @@ -72,6 +72,7 @@ final class SettingVC: UIViewController { button.setTitle("로그아웃", for: .normal) button.setTitleColor(TLColor.white, for: .normal) button.titleLabel?.font = TLFont.body1.font + button.contentHorizontalAlignment = .leading return button }() @@ -80,6 +81,7 @@ final class SettingVC: UIViewController { button.setTitle("탈퇴하기", for: .normal) button.setTitleColor(TLColor.white, for: .normal) button.titleLabel?.font = TLFont.body1.font + button.contentHorizontalAlignment = .leading return button }() @@ -181,6 +183,7 @@ extension SettingVC { let safariVC = SFSafariViewController(url: url) self.present(safariVC, animated: true) }) + button.contentHorizontalAlignment = .leading button.addAction(action, for: .touchUpInside) } } @@ -197,6 +200,7 @@ extension SettingVC { ].forEach { stackView.addArrangedSubview($0) $0.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 16).isActive = true + $0.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 16).isActive = true } view.subviews.forEach {