From 636e6dd006af4ee3c97a3941dbb88e63452ea320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 15 Sep 2024 17:22:49 +0900 Subject: [PATCH 01/13] =?UTF-8?q?[fix]=20#120=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=95=A1=EC=85=98=20?= =?UTF-8?q?=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Search/PokitSearchFeature.swift | 18 +++++++----------- .../Sources/Search/PokitSearchView.swift | 16 ++++++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift index 33b5cf9b..26bda401 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift @@ -73,10 +73,7 @@ public struct PokitSearchFeature { guard let startDate = domain.condition.startDate else { return nil } - let formatter = DateFormatter() - formatter.dateFormat = "yyyy-MM-dd" - - return formatter.string(from: startDate) + return DateFormat.searchCondition.formatter.string(from: startDate) } var hasNext: Bool { get { domain.contentList.hasNext } @@ -335,7 +332,7 @@ private extension PokitSearchFeature { state.shareSheetItem = nil return .none case .로딩_isPresented: - return .send(.async(.컨텐츠_검색_결과_페이징_조회), animation: .pokitDissolve) + return .send(.async(.컨텐츠_검색_결과_페이징_조회)) } } @@ -350,9 +347,6 @@ private extension PokitSearchFeature { state.domain.contentList.data = [] return .none case .updateDateFilter(startDate: let startDate, endDate: let endDate): - let formatter = DateFormatter() - formatter.dateFormat = "yy.MM.dd" - state.domain.condition.startDate = startDate state.domain.condition.endDate = endDate @@ -362,11 +356,13 @@ private extension PokitSearchFeature { return .none } + let startDateString = DateFormat.dateFilter.formatter.string(from: startDate) if startDate == endDate { /// - 날짜 필터를 하루만 선택했을 경우 - state.dateFilterText = "\(formatter.string(from: startDate))" + state.dateFilterText = startDateString } else { - state.dateFilterText = "\(formatter.string(from: startDate))~\(formatter.string(from: endDate))" + let endDateString = DateFormat.dateFilter.formatter.string(from: endDate) + state.dateFilterText = "\(startDateString)~\(endDateString)" } return .none @@ -525,7 +521,7 @@ private extension PokitSearchFeature { endDate: endDateString ) ).toDomain() - await send(.inner(.컨텐츠_검색_결과_페이징_갱신(contentList)), animation: .pokitDissolve) + await send(.inner(.컨텐츠_검색_결과_페이징_갱신(contentList))) } } } diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift index 72080288..d433859c 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift @@ -15,7 +15,7 @@ public struct PokitSearchView: View { @Perception.Bindable public var store: StoreOf @FocusState - private var focused: Bool + private var focused: Bool /// - Initializer public init(store: StoreOf) { @@ -35,9 +35,12 @@ public extension PokitSearchView { PokitDivider() .padding(.top, 28) - resultList - - Spacer() + if store.isSearching { + resultList + } else { + + Spacer() + } } .background(.pokit(.bg(.base))) .ignoresSafeArea(edges: .bottom) @@ -307,16 +310,17 @@ private extension PokitSearchView { if store.hasNext { PokitLoading() - .task { await send(.로딩_isPresented, animation: .pokitDissolve).finish() } + .task { await send(.로딩_isPresented).finish() } } } .padding(.horizontal, 20) + .padding(.bottom, store.hasNext ? 0 : 36) } } else { PokitLoading() } } - .padding(.vertical, 24) + .padding(.top, 24) } } //MARK: - Preview From a67eaf054a19345727f7f044c14e2963c55d78cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 15 Sep 2024 17:27:02 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[chore]=20#120=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=95=A1=EC=85=98=20?= =?UTF-8?q?=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Search/PokitSearchFeature.swift | 4 ++-- .../Sources/Search/PokitSearchView.swift | 16 ++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift index 26bda401..0338fe18 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift @@ -332,7 +332,7 @@ private extension PokitSearchFeature { state.shareSheetItem = nil return .none case .로딩_isPresented: - return .send(.async(.컨텐츠_검색_결과_페이징_조회)) + return .send(.async(.컨텐츠_검색_결과_페이징_조회), animation: .pokitDissolve) } } @@ -521,7 +521,7 @@ private extension PokitSearchFeature { endDate: endDateString ) ).toDomain() - await send(.inner(.컨텐츠_검색_결과_페이징_갱신(contentList))) + await send(.inner(.컨텐츠_검색_결과_페이징_갱신(contentList)), animation: .pokitDissolve) } } } diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift index d433859c..72080288 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift @@ -15,7 +15,7 @@ public struct PokitSearchView: View { @Perception.Bindable public var store: StoreOf @FocusState - private var focused: Bool + private var focused: Bool /// - Initializer public init(store: StoreOf) { @@ -35,12 +35,9 @@ public extension PokitSearchView { PokitDivider() .padding(.top, 28) - if store.isSearching { - resultList - } else { - - Spacer() - } + resultList + + Spacer() } .background(.pokit(.bg(.base))) .ignoresSafeArea(edges: .bottom) @@ -310,17 +307,16 @@ private extension PokitSearchView { if store.hasNext { PokitLoading() - .task { await send(.로딩_isPresented).finish() } + .task { await send(.로딩_isPresented, animation: .pokitDissolve).finish() } } } .padding(.horizontal, 20) - .padding(.bottom, store.hasNext ? 0 : 36) } } else { PokitLoading() } } - .padding(.top, 24) + .padding(.vertical, 24) } } //MARK: - Preview From fda3d6ba91c3acfbef5685e59b5853360b35d64d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 15 Sep 2024 17:31:53 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[fix]=20#120=20DateFormatter=20=EC=BA=90?= =?UTF-8?q?=EC=8B=B1=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeatureRemind/Sources/Remind/RemindView.swift | 3 +-- .../Sources/Search/PokitSearchFeature.swift | 12 +++++++----- .../Sources/Search/Sheet/FilterBottomFeature.swift | 6 ++---- Projects/Util/Sources/Extension/DateFormat.swift | 1 + 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift b/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift index c2e2756d..6f78327d 100644 --- a/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift +++ b/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift @@ -17,11 +17,10 @@ public struct RemindView: View { /// - Properties @Perception.Bindable public var store: StoreOf - private let formatter = DateFormatter() + private let formatter = DateFormat.yearMonthDate.formatter /// - Initializer public init(store: StoreOf) { self.store = store - formatter.dateFormat = "yyyy.MM.dd" } } //MARK: - View diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift index 0338fe18..47689229 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift @@ -73,7 +73,9 @@ public struct PokitSearchFeature { guard let startDate = domain.condition.startDate else { return nil } - return DateFormat.searchCondition.formatter.string(from: startDate) + let formatter = DateFormat.searchCondition.formatter + + return formatter.string(from: startDate) } var hasNext: Bool { get { domain.contentList.hasNext } @@ -347,6 +349,8 @@ private extension PokitSearchFeature { state.domain.contentList.data = [] return .none case .updateDateFilter(startDate: let startDate, endDate: let endDate): + let formatter = DateFormat.dateFilter.formatter + state.domain.condition.startDate = startDate state.domain.condition.endDate = endDate @@ -356,13 +360,11 @@ private extension PokitSearchFeature { return .none } - let startDateString = DateFormat.dateFilter.formatter.string(from: startDate) if startDate == endDate { /// - 날짜 필터를 하루만 선택했을 경우 - state.dateFilterText = startDateString + state.dateFilterText = "\(formatter.string(from: startDate))" } else { - let endDateString = DateFormat.dateFilter.formatter.string(from: endDate) - state.dateFilterText = "\(startDateString)~\(endDateString)" + state.dateFilterText = "\(formatter.string(from: startDate))~\(formatter.string(from: endDate))" } return .none diff --git a/Projects/Feature/FeatureSetting/Sources/Search/Sheet/FilterBottomFeature.swift b/Projects/Feature/FeatureSetting/Sources/Search/Sheet/FilterBottomFeature.swift index c2db3d41..51da4855 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/Sheet/FilterBottomFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/Sheet/FilterBottomFeature.swift @@ -47,13 +47,11 @@ public struct FilterBottomFeature { var startDate: Date var endDate: Date var startDateText: String { - let fomatter = DateFormatter() - fomatter.dateFormat = "yy.MM.dd" + let fomatter = DateFormat.dateFilter.formatter return fomatter.string(from: startDate) } var endDateText: String { - let fomatter = DateFormatter() - fomatter.dateFormat = "yy.MM.dd" + let fomatter = DateFormat.dateFilter.formatter return fomatter.string(from: endDate) } diff --git a/Projects/Util/Sources/Extension/DateFormat.swift b/Projects/Util/Sources/Extension/DateFormat.swift index c0944b8c..7bb3f0f6 100644 --- a/Projects/Util/Sources/Extension/DateFormat.swift +++ b/Projects/Util/Sources/Extension/DateFormat.swift @@ -12,6 +12,7 @@ public enum DateFormat: String { case yearMonthDate = "yyyy.MM.dd" case calendarPage = "yyyy년 MM월" case searchCondition = "yyyy-MM-dd" + case dateFilter = "yy.MM.dd" } extension DateFormat { From a0f69153c1c839e06c87032d55c88e075ac90c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 15 Sep 2024 17:44:56 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[fix]=20#120=20ContentList=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=95=A1?= =?UTF-8?q?=EC=85=98=20=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ContentList/ContentListFeature.swift | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift index 0e64afaf..44ebb6cc 100644 --- a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift +++ b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift @@ -172,7 +172,7 @@ private extension ContentListFeature { state.domain.pageable.sort = [ state.isListDescending ? "createdAt,desc" : "createdAt,asc" ] - return .send(.inner(.pagenation_초기화)) + return .send(.inner(.pagenation_초기화), animation: .pokitDissolve) case .backButtonTapped: return .run { _ in await dismiss() } @@ -246,7 +246,10 @@ private extension ContentListFeature { sort: pageable.sort ) ).toDomain() - await send(.inner(.컨텐츠_목록_조회(contentList)), animation: .pokitDissolve) + await send( + .inner(.컨텐츠_목록_조회(contentList)), + animation: pageable.page == 0 ? .pokitDissolve : nil + ) } case .즐겨찾기_링크모음_조회: return .run { [pageable = state.domain.pageable] send in @@ -257,7 +260,10 @@ private extension ContentListFeature { sort: pageable.sort ) ).toDomain() - await send(.inner(.컨텐츠_목록_조회(contentList)), animation: .pokitDissolve) + await send( + .inner(.컨텐츠_목록_조회(contentList)), + animation: pageable.page == 0 ? .pokitDissolve : nil + ) } case .컨텐츠_삭제(id: let id): return .run { [id] send in @@ -270,10 +276,10 @@ private extension ContentListFeature { return .run { [type = state.contentType] send in switch type { case .unread: - await send(.async(.읽지않음_컨텐츠_조회), animation: .pokitDissolve) + await send(.async(.읽지않음_컨텐츠_조회)) break case .favorite: - await send(.async(.즐겨찾기_링크모음_조회), animation: .pokitDissolve) + await send(.async(.즐겨찾기_링크모음_조회)) break } } @@ -292,7 +298,10 @@ private extension ContentListFeature { sort: pageable.sort ) ).toDomain() - await send(.inner(.컨텐츠_목록_갱신(contentList)), animation: .pokitSpring) + await send( + .inner(.컨텐츠_목록_갱신(contentList)), + animation: pageable.page == 0 ? .pokitDissolve : nil + ) case .favorite: let contentList = try await remindClient.즐겨찾기_링크모음_조회( BasePageableRequest( @@ -301,7 +310,10 @@ private extension ContentListFeature { sort: pageable.sort ) ).toDomain() - await send(.inner(.컨텐츠_목록_갱신(contentList)), animation: .pokitSpring) + await send( + .inner(.컨텐츠_목록_갱신(contentList)), + animation: pageable.page == 0 ? .pokitDissolve : nil + ) } } From 5f48c86a22f490002de4647b742d2b452cf80f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 15 Sep 2024 18:12:16 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[fix]=20#120=20CategoryDetail=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/CategoryDetailFeature.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift index e50dc251..4dca091e 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift @@ -191,7 +191,7 @@ private extension CategoryDetailFeature { state.domain.contentList.data = nil state.domain.category = item return .run { send in - await send(.async(.카테고리_내_컨텐츠_목록_조회)) + await send(.inner(.pagenation_초기화)) await send(.inner(.pokitCategorySelectSheetPresented(false))) } @@ -258,7 +258,11 @@ private extension CategoryDetailFeature { state.kebobSelectedType = nil return .none case .pagenation_네트워크_결과(let contentList): + let list = state.domain.contentList.data ?? [] + guard let newList = contentList.data else { return .none } + state.domain.contentList = contentList + state.domain.contentList.data = list + newList return .none case .pagenation_초기화: state.domain.pageable.page = 0 @@ -289,7 +293,11 @@ private extension CategoryDetailFeature { favorites: condition.isFavoriteFlitered ) ).toDomain() - await send(.inner(.카테고리_내_컨텐츠_목록_갱신(contentList)), animation: .pokitDissolve) + if pageable.page == 0 { + await send(.inner(.카테고리_내_컨텐츠_목록_갱신(contentList)), animation: .pokitDissolve) + } else { + await send(.inner(.pagenation_네트워크_결과(contentList))) + } } case .컨텐츠_삭제(id: let id): return .run { [id] send in From d2393719ae5482f8b3275845dd850defd4fe2975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:40:55 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[fix]=20#120=20PokitSearchFeature=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EB=B3=B5=EC=9B=90=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Search/PokitSearchFeature.swift | 73 +++++++++++-------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift index 47689229..9f001bd8 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift @@ -151,7 +151,7 @@ public struct PokitSearchFeature { } public enum AsyncAction: Equatable { - case 컨텐츠_검색(size: Int) + case 컨텐츠_검색 case 최근검색어_갱신 case 자동저장_켜기_갱신 case 컨텐츠_삭제(id: Int) @@ -308,10 +308,10 @@ private extension PokitSearchFeature { return .run { [ contentList = state.domain.contentList.data ] send in - await send(.inner(.자동저장_켜기_불러오기)) - await send(.inner(.최근검색어_불러오기)) + async let _ = send(.inner(.자동저장_켜기_불러오기)) + async let _ = send(.inner(.최근검색어_불러오기)) if let contentList, !contentList.isEmpty { - await send(.async(.컨텐츠_검색(size: contentList.count))) + async let _ = send(.async(.컨텐츠_검색)) } for await _ in self.pasteboard.changes() { let url = try await pasteboard.probableWebURL() @@ -334,7 +334,7 @@ private extension PokitSearchFeature { state.shareSheetItem = nil return .none case .로딩_isPresented: - return .send(.async(.컨텐츠_검색_결과_페이징_조회), animation: .pokitDissolve) + return .send(.async(.컨텐츠_검색_결과_페이징_조회)) } } @@ -427,17 +427,16 @@ private extension PokitSearchFeature { state.domain.contentList.data = list + newList return .send(.inner(.enableIsSearching)) case .페이징_초기화: - state.domain.pageable.page = -1 + state.domain.pageable.page = 0 state.domain.contentList.data = nil - return .send(.async(.컨텐츠_검색_결과_페이징_조회), animation: .pokitDissolve) + return .send(.async(.컨텐츠_검색), animation: .pokitDissolve) } } /// - Async Effect func handleAsyncAction(_ action: Action.AsyncAction, state: inout State) -> Effect { switch action { - case let .컨텐츠_검색(size): - state.domain.pageable.page = 0 + case .컨텐츠_검색: let formatter = DateFormat.yearMonthDate.formatter var startDateString: String? = nil @@ -450,26 +449,40 @@ private extension PokitSearchFeature { } return .run { [ pageable = state.domain.pageable, - condition = state.domain.condition, - startDateString, - endDateString + condition = BaseConditionRequest( + searchWord: state.domain.condition.searchWord, + categoryIds: state.domain.condition.categoryIds, + isRead: state.domain.condition.isRead, + favorites: state.domain.condition.favorites, + startDate: startDateString, + endDate: endDateString + ) ] send in - let contentList = try await contentClient.컨텐츠_검색( - BasePageableRequest( - page: pageable.page, - size: size, - sort: pageable.sort - ), - BaseConditionRequest( - searchWord: condition.searchWord, - categoryIds: condition.categoryIds, - isRead: condition.isRead, - favorites: condition.favorites, - startDate: startDateString, - endDate: endDateString - ) - ).toDomain() - await send(.inner(.컨텐츠_목록_갱신(contentList)), animation: .pokitSpring) + let stream = AsyncThrowingStream { continuation in + Task { + for page in 0...pageable.page { + let contentList = try await contentClient.컨텐츠_검색( + BasePageableRequest( + page: page, + size: pageable.size, + sort: pageable.sort + ), + condition + ).toDomain() + continuation.yield(contentList) + } + continuation.finish() + } + } + var contentItems: BaseContentListInquiry? = nil + for try await contentList in stream { + let items = contentItems?.data ?? [] + let newItems = contentList.data ?? [] + contentItems = contentList + contentItems?.data = items + newItems + } + guard let contentItems else { return } + await send(.inner(.컨텐츠_목록_갱신(contentItems))) } case .최근검색어_갱신: guard state.isAutoSaveSearch else { return .none } @@ -523,7 +536,7 @@ private extension PokitSearchFeature { endDate: endDateString ) ).toDomain() - await send(.inner(.컨텐츠_검색_결과_페이징_갱신(contentList)), animation: .pokitDissolve) + await send(.inner(.컨텐츠_검색_결과_페이징_갱신(contentList))) } } } @@ -568,7 +581,7 @@ private extension PokitSearchFeature { guard let contentList = state.domain.contentList.data, !contentList.isEmpty else { return .none } - return .send(.async(.컨텐츠_검색(size: contentList.count))) + return .send(.async(.컨텐츠_검색)) default: return .none } } From 79c3153abd1204e1c6f03b71e93890af1ed0e64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:30:09 +0900 Subject: [PATCH 07/13] =?UTF-8?q?[fix]=20#120=20ContentList=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=95=20=EC=9E=AC=EC=A1=B0=ED=9A=8C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ContentList/ContentListFeature.swift | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift index 44ebb6cc..c87226a6 100644 --- a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift +++ b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift @@ -95,6 +95,7 @@ public struct ContentListFeature { case 컨텐츠_삭제(id: Int) case pagenation_네트워크 case 컨텐츠_목록_갱신 + case 페이징_재조회 } public enum ScopeAction: Equatable { @@ -270,7 +271,7 @@ private extension ContentListFeature { let _ = try await contentClient.컨텐츠_삭제("\(id)") await send(.inner(.컨텐츠_삭제_반영(id: id)), animation: .pokitSpring) } - + case .pagenation_네트워크: state.domain.pageable.page += 1 return .run { [type = state.contentType] send in @@ -317,6 +318,45 @@ private extension ContentListFeature { } } + case .페이징_재조회: + return .run { [ + pageable = state.domain.pageable, + contentType = state.contentType + ] send in + let stream = AsyncThrowingStream { continuation in + Task { + for page in 0...pageable.page { + let paeagableRequest = BasePageableRequest( + page: page, + size: pageable.size, + sort: pageable.sort + ) + switch contentType { + case .favorite: + let contentList = try await remindClient.즐겨찾기_링크모음_조회( + paeagableRequest + ).toDomain() + continuation.yield(contentList) + case .unread: + let contentList = try await remindClient.읽지않음_컨텐츠_조회( + paeagableRequest + ).toDomain() + continuation.yield(contentList) + } + } + continuation.finish() + } + } + var contentItems: BaseContentListInquiry? = nil + for try await contentList in stream { + let items = contentItems?.data ?? [] + let newItems = contentList.data ?? [] + contentItems = contentList + contentItems?.data = items + newItems + } + guard let contentItems else { return } + await send(.inner(.컨텐츠_목록_갱신(contentItems)), animation: .pokitSpring) + } } } @@ -344,12 +384,7 @@ private extension ContentListFeature { func handleDelegateAction(_ action: Action.DelegateAction, state: inout State) -> Effect { switch action { case .컨텐츠_목록_조회: - switch state.contentType { - case .favorite: - return .send(.async(.즐겨찾기_링크모음_조회)) - case .unread: - return .send(.async(.읽지않음_컨텐츠_조회)) - } + return .send(.async(.페이징_재조회)) default: return .none } From 9dee26b7527e95b23825ee9d1d67ee9a2c60919b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 16 Sep 2024 18:19:36 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[fix]=20#120=20PokitRootFeature=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=95=20=EC=9E=AC=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/PokitRootFeature.swift | 91 ++++++++++++++++--- 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift b/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift index b71e45a2..8c6eb351 100644 --- a/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift +++ b/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift @@ -118,6 +118,8 @@ public struct PokitRootFeature { case 카테고리_페이징_조회 case 미분류_카테고리_컨텐츠_조회 case 카테고리_조회 + case 미분류_카테고리_컨텐츠_페이징_재조회 + case 카테고리_페이징_재조회 } public enum ScopeAction: Equatable { @@ -228,12 +230,12 @@ private extension PokitRootFeature { guard let _ = state.domain.categoryList.data?.count else { return .send(.inner(.페이지네이션_초기화)) } - return .send(.async(.카테고리_조회), animation: .pokitSpring) + return .send(.async(.카테고리_페이징_재조회), animation: .pokitSpring) case .folder(.미분류): guard let _ = state.domain.unclassifiedContentList.data?.count else { return .send(.inner(.페이지네이션_초기화)) } - return .send(.async(.미분류_카테고리_컨텐츠_조회), animation: .pokitSpring) + return .send(.async(.미분류_카테고리_컨텐츠_페이징_재조회), animation: .pokitSpring) default: return .none } case .다음페이지_로딩_presented: @@ -311,14 +313,14 @@ private extension PokitRootFeature { state.isPokitDeleteSheetPresented = false return .none case .페이지네이션_초기화: - state.domain.pageable.page = -1 + state.domain.pageable.page = 0 state.domain.categoryList.data = nil state.domain.unclassifiedContentList.data = nil switch state.folderType { case .folder(.포킷): - return .send(.async(.카테고리_페이징_조회), animation: .pokitDissolve) + return .send(.async(.카테고리_조회), animation: .pokitDissolve) case .folder(.미분류): - return .send(.async(.미분류_카테고리_컨텐츠_페이징_조회), animation: .pokitDissolve) + return .send(.async(.미분류_카테고리_컨텐츠_조회), animation: .pokitDissolve) default: return .none } } @@ -343,7 +345,7 @@ private extension PokitRootFeature { sort: pageable.sort ) ).toDomain() - await send(.inner(.미분류_페이지네이션_결과(contentList: contentList)), animation: .pokitDissolve) + await send(.inner(.미분류_페이지네이션_결과(contentList: contentList))) } case .카테고리_페이징_조회: state.domain.pageable.page += 1 @@ -358,7 +360,7 @@ private extension PokitRootFeature { ), true ).toDomain() - await send(.inner(.카테고리_페이지네이션_결과(contentList: classified)), animation: .pokitDissolve) + await send(.inner(.카테고리_페이지네이션_결과(contentList: classified))) } case .미분류_카테고리_컨텐츠_조회: state.domain.pageable.page = 0 @@ -389,6 +391,71 @@ private extension PokitRootFeature { ).toDomain() await send(.inner(.카테고리_갱신(categoryList: classified)), animation: .pokitSpring) } + case .미분류_카테고리_컨텐츠_페이징_재조회: + return .run { [ + pageable = state.domain.pageable + ] send in + let stream = AsyncThrowingStream { continuation in + Task { + for page in 0...pageable.page { + let contentList = try await contentClient.미분류_카테고리_컨텐츠_조회( + BasePageableRequest( + page: page, + size: pageable.size, + sort: pageable.sort + ) + ).toDomain() + continuation.yield(contentList) + } + continuation.finish() + } + } + var contentItems: BaseContentListInquiry? = nil + for try await contentList in stream { + let items = contentItems?.data ?? [] + let newItems = contentList.data ?? [] + contentItems = contentList + contentItems?.data = items + newItems + } + guard let contentItems else { return } + await send( + .inner(.미분류_카테고리_컨텐츠_갱신(contentList: contentItems)), + animation: .pokitSpring + ) + } + case .카테고리_페이징_재조회: + return .run { [ + pageable = state.domain.pageable + ] send in + let stream = AsyncThrowingStream { continuation in + Task { + for page in 0...pageable.page { + let categoryList = try await categoryClient.카테고리_목록_조회( + BasePageableRequest( + page: page, + size: pageable.size, + sort: pageable.sort + ), + true + ).toDomain() + continuation.yield(categoryList) + } + continuation.finish() + } + } + var categoryItems: BaseCategoryListInquiry? = nil + for try await categoryList in stream { + let items = categoryItems?.data ?? [] + let newItems = categoryList.data ?? [] + categoryItems = categoryList + categoryItems?.data = items + newItems + } + guard let categoryItems else { return } + await send( + .inner(.카테고리_갱신(categoryList: categoryItems)), + animation: .pokitSpring + ) + } } } @@ -490,15 +557,9 @@ private extension PokitRootFeature { case .미분류_카테고리_컨텐츠_조회: switch state.folderType { case .folder(.포킷): - guard let _ = state.domain.categoryList.data?.count else { - return .send(.inner(.페이지네이션_초기화)) - } - return .send(.async(.카테고리_조회), animation: .pokitSpring) + return .send(.async(.카테고리_페이징_재조회), animation: .pokitSpring) case .folder(.미분류): - guard let _ = state.domain.unclassifiedContentList.data?.count else { - return .send(.inner(.페이지네이션_초기화)) - } - return .send(.async(.미분류_카테고리_컨텐츠_조회), animation: .pokitSpring) + return .send(.async(.미분류_카테고리_컨텐츠_페이징_재조회), animation: .pokitSpring) default: return .none } default: From bc54fdf604016435096bb6eaf62108cf6cf351bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 16 Sep 2024 18:28:26 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[fix]=20#120=20=EB=AF=B8=EB=B6=84?= =?UTF-8?q?=EB=A5=98=20=EC=BB=A8=ED=85=90=EC=B8=A0=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=BB=A8=ED=85=90=EC=B8=A0=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=95=88=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeaturePokit/Sources/PokitRootFeature.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift b/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift index 8c6eb351..429a9b83 100644 --- a/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift +++ b/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift @@ -120,6 +120,7 @@ public struct PokitRootFeature { case 카테고리_조회 case 미분류_카테고리_컨텐츠_페이징_재조회 case 카테고리_페이징_재조회 + case 미분류_카테고리_컨텐츠_삭제(contentId: Int) } public enum ScopeAction: Equatable { @@ -456,6 +457,12 @@ private extension PokitRootFeature { animation: .pokitSpring ) } + case let .미분류_카테고리_컨텐츠_삭제(contentId): + return .run { send in + let _ = try await contentClient.컨텐츠_삭제("\(contentId)") + await send(.inner(.컨텐츠_삭제(contentId: contentId)), animation: .pokitSpring) + } + } } @@ -531,7 +538,10 @@ private extension PokitRootFeature { return .none } - return .send(.inner(.컨텐츠_삭제(contentId: selectedItem.id)), animation: .pokitSpring) + return .send( + .async(.미분류_카테고리_컨텐츠_삭제(contentId: selectedItem.id)), + animation: .pokitSpring + ) case .folder(.포킷): guard let selectedItem = state.selectedKebobItem else { From 5d3bf5168608ff8e9935cd0af5aa091aa436c09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 16 Sep 2024 20:54:05 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[fix]=20#120=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=95=98=EB=8B=A8=20=ED=8C=A8=EB=94=A9=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeatureCategoryDetail/Sources/CategoryDetailView.swift | 1 + .../FeatureSetting/Sources/Search/PokitSearchView.swift | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift index 655b1ff7..1cbc0fb5 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift @@ -168,6 +168,7 @@ private extension CategoryDetailView { Spacer() } + .padding(.bottom, 36) } } } else { diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift index 72080288..8a1cfc22 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift @@ -311,12 +311,13 @@ private extension PokitSearchView { } } .padding(.horizontal, 20) + .padding(.bottom, 36) } } else { PokitLoading() } } - .padding(.vertical, 24) + .padding(.top, 24) } } //MARK: - Preview From 6abd8d9eda8ad1069b277004b2be8c237d0d1556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:01:51 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[fix]=20#120=20ContentDetail=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AD=EC=A0=9C=EC=8B=9C=20=EB=B6=80=EB=AA=A8=20?= =?UTF-8?q?=EB=B7=B0=EC=97=90=20=EB=B0=98=EC=98=81=20=EC=95=88=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ContentDetail/ContentDetailFeature.swift | 4 +++- .../FeatureSetting/Sources/Search/PokitSearchFeature.swift | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift index 55b59c6e..26763554 100644 --- a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift +++ b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift @@ -96,6 +96,7 @@ public struct ContentDetailFeature { case editButtonTapped(contentId: Int) case 즐겨찾기_갱신_완료 case 컨텐츠_조회_완료 + case 컨텐츠_삭제_완료 } } @@ -250,8 +251,9 @@ private extension ContentDetailFeature { await send(.inner(.즐겨찾기_갱신(false))) } case .컨텐츠_삭제(id: let id): - return .run { _ in + return .run { send in try await contentClient.컨텐츠_삭제("\(id)") + await send(.delegate(.컨텐츠_삭제_완료)) await dismiss() } } diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift index 9f001bd8..c66315be 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift @@ -482,7 +482,7 @@ private extension PokitSearchFeature { contentItems?.data = items + newItems } guard let contentItems else { return } - await send(.inner(.컨텐츠_목록_갱신(contentItems))) + await send(.inner(.컨텐츠_목록_갱신(contentItems)), animation: .pokitSpring) } case .최근검색어_갱신: guard state.isAutoSaveSearch else { return .none } @@ -581,7 +581,7 @@ private extension PokitSearchFeature { guard let contentList = state.domain.contentList.data, !contentList.isEmpty else { return .none } - return .send(.async(.컨텐츠_검색)) + return .send(.async(.컨텐츠_검색), animation: .pokitSpring) default: return .none } } From 57572def18e47e8b8ee73dd138bfef9799ed053a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:34:25 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[fix]=20#120=20CategoryDetail=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=95=20=EC=9E=AC=EC=A1=B0=ED=9A=8C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Sources/MainTab/MainTabPath.swift | 3 +- .../Sources/CategoryDetailFeature.swift | 42 ++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/Projects/App/Sources/MainTab/MainTabPath.swift b/Projects/App/Sources/MainTab/MainTabPath.swift index 92a1a47d..20252b04 100644 --- a/Projects/App/Sources/MainTab/MainTabPath.swift +++ b/Projects/App/Sources/MainTab/MainTabPath.swift @@ -137,7 +137,8 @@ public extension MainTabFeature { /// - 컨텐츠 상세보기 내부 액션 실행 case .contentDetail(.presented(.delegate(.즐겨찾기_갱신_완료))), - .contentDetail(.presented(.delegate(.컨텐츠_조회_완료))): + .contentDetail(.presented(.delegate(.컨텐츠_조회_완료))), + .contentDetail(.presented(.delegate(.컨텐츠_삭제_완료))): guard let stackElementId = state.path.ids.last, let lastPath = state.path.last else { switch state.selectedTab { diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift index 4dca091e..66a5bb7b 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift @@ -118,6 +118,7 @@ public struct CategoryDetailFeature { case 카테고리_내_컨텐츠_목록_조회 case 컨텐츠_삭제(id: Int) case pagenation_네트워크 + case 페이징_재조회 } public enum ScopeAction: Equatable { @@ -307,6 +308,45 @@ private extension CategoryDetailFeature { case .pagenation_네트워크: state.domain.pageable.page += 1 return .send(.async(.카테고리_내_컨텐츠_목록_조회)) + case .페이징_재조회: + return .run { [ + pageable = state.domain.pageable, + categoryId = state.domain.category.id, + condition = state.domain.condition + ] send in + let stream = AsyncThrowingStream { continuation in + Task { + for page in 0...pageable.page { + let paeagableRequest = BasePageableRequest( + page: page, + size: pageable.size, + sort: pageable.sort + ) + let conditionRequest = BaseConditionRequest( + categoryIds: condition.categoryIds, + isRead: condition.isUnreadFlitered, + favorites: condition.isFavoriteFlitered + ) + let contentList = try await contentClient.카테고리_내_컨텐츠_목록_조회( + "\(categoryId)", + paeagableRequest, + conditionRequest + ).toDomain() + continuation.yield(contentList) + } + continuation.finish() + } + } + var contentItems: BaseContentListInquiry? = nil + for try await contentList in stream { + let items = contentItems?.data ?? [] + let newItems = contentList.data ?? [] + contentItems = contentList + contentItems?.data = items + newItems + } + guard let contentItems else { return } + await send(.inner(.카테고리_내_컨텐츠_목록_갱신(contentItems)), animation: .pokitSpring) + } } } @@ -413,7 +453,7 @@ private extension CategoryDetailFeature { func handleDelegateAction(_ action: Action.DelegateAction, state: inout State) -> Effect { switch action { case .카테고리_내_컨텐츠_목록_조회: - return .send(.async(.카테고리_내_컨텐츠_목록_조회)) + return .send(.async(.페이징_재조회)) default: return .none } From ad08b7439bd10657cfcdcaea83af345f5eef5d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 16 Sep 2024 22:16:13 +0900 Subject: [PATCH 13/13] =?UTF-8?q?[remove]=20#120=20develop.yml=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/develop.yml | 41 ----------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 .github/workflows/develop.yml diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml deleted file mode 100644 index 7e5f76c5..00000000 --- a/.github/workflows/develop.yml +++ /dev/null @@ -1,41 +0,0 @@ -# This workflow will build a Swift project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift - -name: deveplop workflow - -on: - pull_request: - branches: [ "develop" ] - -jobs: - build: - - runs-on: macos-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set up Xcode - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '15.0' - - - name: generate project - # - run: expect <