Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions WSSiOS/Network/Search/SearchService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,59 @@ protocol SearchService {
keywordIds: [Int],
page: Int,
size: Int) -> Single<DetailSearchNovels>
func getRecentSearches() -> Single<[RecentSearch]>
func deleteRecentSearch(id: Int) -> Single<Void>
func deleteAllRecentSearches() -> Single<Void>
}

final class DefaultSearchService: NSObject, Networking { }

extension DefaultSearchService: SearchService {
func getRecentSearches() -> Single<[RecentSearch]> {
do {
let request = try makeHTTPRequest(method: .get,
path: URLs.Search.recentSearch,
headers: APIConstants.accessTokenHeader,
body: nil)
NetworkLogger.log(request: request)
return tokenCheckURLSession.rx.data(request: request)
.map { try self.decode(data: $0, to: RecentSearches.self).recentSearches }
.asSingle()
} catch {
return Single.error(error)
}
}

func deleteRecentSearch(id: Int) -> Single<Void> {
do {
let request = try makeHTTPRequest(method: .delete,
path: URLs.Search.deleteRecentSearchKeyword(id: id),
headers: APIConstants.accessTokenHeader,
body: nil)
NetworkLogger.log(request: request)
return tokenCheckURLSession.rx.data(request: request)
.map { _ in }
.asSingle()
} catch {
return Single.error(error)
}
}

func deleteAllRecentSearches() -> Single<Void> {
do {
let request = try makeHTTPRequest(method: .delete,
path: URLs.Search.deleteAllRecentSearchKeywords,
headers: APIConstants.accessTokenHeader,
body: nil)
NetworkLogger.log(request: request)
return tokenCheckURLSession.rx.data(request: request)
.map { _ in }
.asSingle()
} catch {
return Single.error(error)
}
}

func getSosopicks() -> Single<SosoPickNovels> {
do {
let request = try makeHTTPRequest(method: .get,
Expand Down
5 changes: 5 additions & 0 deletions WSSiOS/Resource/Constants/Strings/StringLiterals+Search.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ extension StringLiterals {
enum Search {
static let title = "탐색하기"
static let searchbar = "작품 제목, 작가를 검색하세요"

static let recentSearchTitle = "최근 검색어"
static let deleteAll = "전체삭제"
static let genreSearchTitle = "장르별 검색"

static let induceTitle = "뭐 읽을지 고민될 땐?"
static let induceDescription = "장르, 연재상태, 별점, 키워드로 작품 찾기"
Expand Down Expand Up @@ -53,5 +57,6 @@ extension StringLiterals {
static let empty = "해당하는 작품이 없어요\n검색의 범위를 더 넓혀보세요"

static let applyOption = "장르, 연재상태, 별점, 키워드 적용"
static let applyGenre = "장르 적용"
}
}
5 changes: 5 additions & 0 deletions WSSiOS/Resource/Constants/URLs/URLs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ enum URLs {
static let sosoPick = "/soso-picks"
static let normalSearch = "/novels"
static let detailSearch = "/novels/filtered"
static let recentSearch = "/novels/recent-searches"
static func deleteRecentSearchKeyword(id: Int) -> String {
return "/novels/recent-searches/\(id)"
}
static let deleteAllRecentSearchKeywords = "/novels/recent-searches"
}

enum Keyword {
Expand Down
1 change: 1 addition & 0 deletions WSSiOS/Source/Data/Base/NovelGenre.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,5 @@ extension NovelGenre {
static let feedFilterGenres: [NovelGenre] = [.fantasy, .modernFantasy, .romance, .romanceFantasy, .wuxia, .mystery, .drama, .lightNovel, .bl, .etc]
static let detailSearchGenres: [NovelGenre] = [.fantasy, .modernFantasy, .romance, .romanceFantasy, .wuxia, .mystery, .drama, .lightNovel, .bl]
static let myPageEditGenres: [NovelGenre] = [.romance, .romanceFantasy, .fantasy, .modernFantasy, .wuxia, .bl, .lightNovel, .mystery, .drama]
static let normalSearchGenres: [NovelGenre] = [.modernFantasy, .romanceFantasy, .romance, .fantasy, .wuxia, .bl, .lightNovel, .drama, .mystery]
}
12 changes: 11 additions & 1 deletion WSSiOS/Source/Data/DTO/SearchResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,20 @@ struct SearchNovel: Codable {
var interestCount: Int
var novelRating: Float
var novelRatingCount: Int

enum CodingKeys: String, CodingKey {
case novelId, novelImage, interestCount, novelRating, novelRatingCount
case novelTitle = "title"
case novelAuthor = "author"
}
}

/// 최근 검색어 조회 API
struct RecentSearches: Codable {
let recentSearches: [RecentSearch]
}

struct RecentSearch: Codable {
let id: Int
let keyword: String
}
15 changes: 15 additions & 0 deletions WSSiOS/Source/Data/Repository/SearchRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ protocol SearchRepository {
upperNovelRating: Float,
keywordIds: [Int],
page: Int) -> Observable<DetailSearchNovels>
func getRecentSearches() -> Observable<[RecentSearch]>
func deleteRecentSearch(id: Int) -> Observable<Void>
func deleteAllRecentSearches() -> Observable<Void>
}

struct DefaultSearchRepository: SearchRepository {
Expand Down Expand Up @@ -52,4 +55,16 @@ struct DefaultSearchRepository: SearchRepository {
page: page,
size: searchSize).asObservable()
}

func getRecentSearches() -> Observable<[RecentSearch]> {
return searchService.getRecentSearches().asObservable()
}

func deleteRecentSearch(id: Int) -> Observable<Void> {
return searchService.deleteRecentSearch(id: id).asObservable()
}

func deleteAllRecentSearches() -> Observable<Void> {
return searchService.deleteAllRecentSearches().asObservable()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ final class DetailSearchResultHeaderView: UIView {
$0.layer.cornerRadius = 14

headerLabel.do {
$0.applyWSSFont(.body4, with: StringLiterals.DetailSearch.applyOption)
$0.textColor = .wssGray200
}

Expand Down Expand Up @@ -84,4 +83,8 @@ final class DetailSearchResultHeaderView: UIView {
}
}
}

func bindPlaceholder(_ text: String) {
headerLabel.applyWSSFont(.body4, with: text)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ final class DetailSearchResultViewController: UIViewController, UIScrollViewDele
registerCell()
setDelegate()

rootView.headerView.bindPlaceholder(viewModel.entryType.placeholder)

bindViewModel()
bindAction()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ final class DetailSearchViewController: UIViewController, UIScrollViewDelegate {
.subscribe(with: self, onNext: { owner, filterQuery in
let viewModel = DetailSearchResultViewModel(
searchRepository: DefaultSearchRepository(searchService: DefaultSearchService()),
option: filterQuery
option: filterQuery,
entryType: .fullOption
)
let viewController = DetailSearchResultViewController(viewModel: viewModel)
viewController.hidesBottomBarWhenPushed = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import RxSwift
import RxCocoa

final class DetailSearchResultViewModel: ViewModelType {

//MARK: - Properties

private let searchRepository: SearchRepository
// 검색 필터 옵션

// 검색 필터 옵션
var option: SearchFilterQuery

let entryType: EntryType

// 무한 스크롤
private var currentPage: Int = 0
private var isLoadable: Bool = false
Expand Down Expand Up @@ -54,9 +55,11 @@ final class DetailSearchResultViewModel: ViewModelType {
}

init(searchRepository: SearchRepository,
option: SearchFilterQuery) {
option: SearchFilterQuery,
entryType: EntryType) {
self.searchRepository = searchRepository
self.option = option
self.entryType = entryType
}

func transform(from input: Input, disposeBag: DisposeBag) -> Output {
Expand Down Expand Up @@ -171,3 +174,17 @@ final class DetailSearchResultViewModel: ViewModelType {
page: page)
}
}

extension DetailSearchResultViewModel {
enum EntryType {
case fullOption
case genreOnly

var placeholder: String {
switch self {
case .fullOption: return StringLiterals.DetailSearch.applyOption
case .genreOnly: return StringLiterals.DetailSearch.applyGenre
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//
// NormalSearchGenreView.swift
// WSSiOS
//
// Created by onesunny2 on 5/8/25.
//

import UIKit

import SnapKit
import Then

final class NormalSearchGenreView: UIView {

//MARK: - Components

private let titleLabel = UILabel()
private let chevronImageView = UIImageView()
let headerButton = UIButton()
let genreCollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout())

//MARK: - Life Cycle

override init(frame: CGRect) {
super.init(frame: frame)

setUI()
setHierarchy()
setLayout()
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

//MARK: - UI

private func setUI() {
titleLabel.do {
$0.applyWSSFont(.title2, with: StringLiterals.Search.genreSearchTitle)
$0.textColor = .wssBlack
}

chevronImageView.do {
$0.image = .icChevronRightMini
$0.contentMode = .scaleAspectFit
}

genreCollectionView.do {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 12
layout.sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)

$0.collectionViewLayout = layout
$0.isScrollEnabled = true
$0.showsHorizontalScrollIndicator = false
$0.backgroundColor = .clear
}
}

private func setHierarchy() {
self.addSubviews(titleLabel, chevronImageView, headerButton, genreCollectionView)
}

private func setLayout() {
titleLabel.snp.makeConstraints {
$0.top.equalToSuperview()
$0.leading.equalToSuperview().inset(20)
}

chevronImageView.snp.makeConstraints {
$0.centerY.equalTo(titleLabel)
$0.leading.equalTo(titleLabel.snp.trailing).offset(3)
$0.size.equalTo(16)
}

headerButton.snp.makeConstraints {
$0.top.leading.bottom.equalTo(titleLabel)
$0.trailing.equalTo(chevronImageView)
}

genreCollectionView.snp.makeConstraints {
$0.top.equalTo(titleLabel.snp.bottom).offset(12)
$0.leading.trailing.equalToSuperview()
$0.height.equalTo(69)
$0.bottom.equalToSuperview()
}
}
}
Loading