From 40de94e1abde4cee72b72b1fb88db30a4df596fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=9F=E3=81=A4=E3=81=9E=E3=81=86?= Date: Sat, 9 Jan 2021 00:59:16 +0800 Subject: [PATCH] =?UTF-8?q?=E3=83=95=E3=82=A3=E3=83=AB=E3=82=BF=E3=83=BC?= =?UTF-8?q?=E6=A9=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- EhPanda.xcodeproj/project.pbxproj | 16 +- .../Common.swift" | 11 +- .../Defaults.swift" | 138 ++++++++++++--- .../en.lproj/Localizable.strings" | 21 +++ .../zh-Hans.lproj/Localizable.strings" | 23 ++- .../AppAction.swift" | 4 +- .../AppCommand.swift" | 3 +- .../AppState.swift" | 5 +- .../Store.swift" | 8 +- .../Request.swift" | 6 +- .../DetailView.swift" | 2 +- .../FilterView.swift" | 166 ++++++++++++++++++ .../HomeView.swift" | 21 ++- .../SettingView.swift" | 9 + .../BlurView.swift" | 41 ----- .../OverlaySheet.swift" | 57 ------ .../Filter.swift" | 64 +++++++ .../Manga.swift" | 48 ++++- 18 files changed, 489 insertions(+), 154 deletions(-) create mode 100644 "EhPanda/\343\203\223\343\203\245\343\203\274/FilterView.swift" delete mode 100644 "EhPanda/\343\203\223\343\203\245\343\203\274/\343\203\204\343\203\274\343\203\253/BlurView.swift" delete mode 100644 "EhPanda/\343\203\223\343\203\245\343\203\274/\343\203\204\343\203\274\343\203\253/OverlaySheet.swift" create mode 100644 "EhPanda/\343\203\242\343\203\207\343\203\253/Filter.swift" diff --git a/EhPanda.xcodeproj/project.pbxproj b/EhPanda.xcodeproj/project.pbxproj index 816cabe4..db55a104 100644 --- a/EhPanda.xcodeproj/project.pbxproj +++ b/EhPanda.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - AB2C029325A7F2E100F8F740 /* BlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB2C029125A7F2E100F8F740 /* BlurView.swift */; }; - AB2C029425A7F2E100F8F740 /* OverlaySheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB2C029225A7F2E100F8F740 /* OverlaySheet.swift */; }; AB40CFDC25983EC200D1DC9A /* FileStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB40CFDB25983EC200D1DC9A /* FileStorage.swift */; }; AB40CFDF25983EDF00D1DC9A /* FileHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB40CFDE25983EDF00D1DC9A /* FileHelper.swift */; }; AB718B2525A0C0AC00D32962 /* CommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB718B2425A0C0AC00D32962 /* CommentView.swift */; }; @@ -16,6 +14,8 @@ AB994DC32598B3E000E9A367 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB994DC22598B3E000E9A367 /* WebView.swift */; }; AB994DCB2598D96200E9A367 /* AlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB994DCA2598D96200E9A367 /* AlertView.swift */; }; ABA732D925A8018A00B3D9AB /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA732D825A8018A00B3D9AB /* Extensions.swift */; }; + ABA732DF25A852D800B3D9AB /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA732DE25A852D800B3D9AB /* Filter.swift */; }; + ABA732E225A8590900B3D9AB /* FilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA732E125A8590900B3D9AB /* FilterView.swift */; }; ABC3C7852593699B00E0C11B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ABC3C7692593699A00E0C11B /* Assets.xcassets */; }; ABC3C7862593699B00E0C11B /* Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC3C76A2593699A00E0C11B /* Common.swift */; }; ABC3C7872593699B00E0C11B /* EhPandaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC3C76B2593699A00E0C11B /* EhPandaApp.swift */; }; @@ -41,8 +41,6 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - AB2C029125A7F2E100F8F740 /* BlurView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurView.swift; sourceTree = ""; }; - AB2C029225A7F2E100F8F740 /* OverlaySheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverlaySheet.swift; sourceTree = ""; }; AB40CFDB25983EC200D1DC9A /* FileStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileStorage.swift; sourceTree = ""; }; AB40CFDE25983EDF00D1DC9A /* FileHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileHelper.swift; sourceTree = ""; }; AB718B2425A0C0AC00D32962 /* CommentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentView.swift; sourceTree = ""; }; @@ -51,6 +49,8 @@ AB994DC22598B3E000E9A367 /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = ""; }; AB994DCA2598D96200E9A367 /* AlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertView.swift; sourceTree = ""; }; ABA732D825A8018A00B3D9AB /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; + ABA732DE25A852D800B3D9AB /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; + ABA732E125A8590900B3D9AB /* FilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterView.swift; sourceTree = ""; }; ABC3C7542593696C00E0C11B /* EhPanda.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EhPanda.app; sourceTree = BUILT_PRODUCTS_DIR; }; ABC3C7692593699A00E0C11B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; ABC3C76A2593699A00E0C11B /* Common.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = ""; }; @@ -149,6 +149,7 @@ children = ( ABC3C7732593699A00E0C11B /* HomeView.swift */, AB994DBF2598B17500E9A367 /* SettingView.swift */, + ABA732E125A8590900B3D9AB /* FilterView.swift */, ABC3C7722593699A00E0C11B /* DetailView.swift */, AB718B2425A0C0AC00D32962 /* CommentView.swift */, ABC3C7742593699A00E0C11B /* ContentView.swift */, @@ -166,8 +167,6 @@ ABC3C7772593699A00E0C11B /* RatingView.swift */, AB994DCA2598D96200E9A367 /* AlertView.swift */, ABF75F4225A1C11100544D29 /* Comment.swift */, - AB2C029125A7F2E100F8F740 /* BlurView.swift */, - AB2C029225A7F2E100F8F740 /* OverlaySheet.swift */, ); path = "ツール"; sourceTree = ""; @@ -184,6 +183,7 @@ isa = PBXGroup; children = ( ABF75F3E25A19CD200544D29 /* User.swift */, + ABA732DE25A852D800B3D9AB /* Filter.swift */, ABC3C7802593699A00E0C11B /* Manga.swift */, ); path = "モデル"; @@ -285,6 +285,7 @@ ABA732D925A8018A00B3D9AB /* Extensions.swift in Sources */, AB40CFDC25983EC200D1DC9A /* FileStorage.swift in Sources */, ABCD2F0A259763FC008E5A20 /* Request.swift in Sources */, + ABA732E225A8590900B3D9AB /* FilterView.swift in Sources */, ABCD2F07259760D4008E5A20 /* AppError.swift in Sources */, ABC3C7892593699B00E0C11B /* Defaults.swift in Sources */, AB40CFDF25983EDF00D1DC9A /* FileHelper.swift in Sources */, @@ -294,8 +295,6 @@ ABC3C78F2593699B00E0C11B /* ViewModifiers.swift in Sources */, ABCD2EF82597602D008E5A20 /* Store.swift in Sources */, ABC3C7862593699B00E0C11B /* Common.swift in Sources */, - AB2C029425A7F2E100F8F740 /* OverlaySheet.swift in Sources */, - AB2C029325A7F2E100F8F740 /* BlurView.swift in Sources */, ABCD2F0E25976B95008E5A20 /* Parser.swift in Sources */, AB994DCB2598D96200E9A367 /* AlertView.swift in Sources */, ABC3C78D2593699B00E0C11B /* HomeView.swift in Sources */, @@ -306,6 +305,7 @@ ABC3C7872593699B00E0C11B /* EhPandaApp.swift in Sources */, AB718B2525A0C0AC00D32962 /* CommentView.swift in Sources */, ABF75F4325A1C11100544D29 /* Comment.swift in Sources */, + ABA732DF25A852D800B3D9AB /* Filter.swift in Sources */, AB994DC02598B17500E9A367 /* SettingView.swift in Sources */, ABF75F3F25A19CD200544D29 /* User.swift in Sources */, ); diff --git "a/EhPanda/\343\202\242\343\203\227\343\203\252/Common.swift" "b/EhPanda/\343\202\242\343\203\227\343\203\252/Common.swift" index 8d2a6aac..0ba25888 100644 --- "a/EhPanda/\343\202\242\343\203\227\343\203\252/Common.swift" +++ "b/EhPanda/\343\202\242\343\203\227\343\203\252/Common.swift" @@ -55,6 +55,10 @@ public var exx: Bool { UserDefaults.standard.string(forKey: "entry") == "Rra3MKpjKBJLgraHqt9t" } +public var isPad: Bool { + UIDevice.current.userInterfaceIdiom == .pad +} + public var galleryType: GalleryType { let rawValue = UserDefaults .standard @@ -71,7 +75,12 @@ public func readableUnit(bytes: Int64) -> String { public func diskImageCaches() -> String { let bytes = SDImageCache.shared.totalDiskSize() - return readableUnit(bytes: Int64(bytes)) + + if bytes == 0 { + return "0 KB" + } else { + return readableUnit(bytes: Int64(bytes)) + } } public func browsingCaches() -> String { diff --git "a/EhPanda/\343\202\242\343\203\227\343\203\252/Defaults.swift" "b/EhPanda/\343\202\242\343\203\227\343\203\252/Defaults.swift" index 27e1576c..45ce1fa1 100644 --- "a/EhPanda/\343\202\242\343\203\227\343\203\252/Defaults.swift" +++ "b/EhPanda/\343\202\242\343\203\227\343\203\252/Defaults.swift" @@ -7,6 +7,7 @@ class Defaults { class URL { + // いわゆるホストドメイン static var host: String { if exx { return galleryType == .eh ? ehentai : exhentai @@ -14,12 +15,12 @@ class Defaults { return merge([ehentai, listCompact, nonh, f_search + "parody:durarara$"]) } } - static let ehentai = "https://e-hentai.org/" static let exhentai = "https://exhentai.org/" static let forum = "https://forums.e-hentai.org/" static let login = merge([forum + index, login_act]) + // 各機能ページ static let api = "api.php" static let index = "index.php" static let favorites = "favorites.php" @@ -38,36 +39,123 @@ class Defaults { static let listCompact = "inline_set=dm_l" static let detailLarge = "inline_set=ts_l" + // フィルター + static let f_cats = "f_cats=" + static let advSearch = "advsearch=1" + static let f_sname_on = "f_sname=on" + static let f_stags_on = "f_stags=on" + static let f_sdesc_on = "f_sdesc=on" + static let f_storr_on = "f_storr=on" + static let f_sto_on = "f_sto=on" + static let f_sdt1_on = "f_sdt1=on" + static let f_sdt2_on = "f_sdt2=on" + static let f_sh_on = "f_sh=on" + static let f_sr_on = "f_sr=on" + static let f_srdd = "f_srdd=" + static let f_sp_on = "f_sp=on" + static let f_spf = "f_spf=" + static let f_spt = "f_spt=" + static let f_sfl_on = "f_sfl=on" + static let f_sfu_on = "f_sfu=on" + static let f_sft_on = "f_sft=on" + } +} + +// MARK: リクエスト +extension Defaults.URL { + static func search(keyword: String, filter: Filter) -> String { + let params: [String] + = [host, listCompact, f_search + keyword.URLString()] + + applyFilters(filter) + ePrint(merge(params)) + return merge(params) + } + static func popularList() -> String { + merge([host, listCompact]) + } + static func favoritesList() -> String { + merge([host + favorites, listCompact]) + } + + static func mangaDetail(url: String) -> String { + merge([url, ignoreOffensive, showComments, detailLarge]) + } + static func contentPage(url: String, page: Int) -> String { + merge([url, p + "\(page)"]) + } + + static func addFavorite(id: String, token: String) -> String { + merge([host + gallerypopups, gid + id, t + token, addfav_act]) + } +} + +// MARK: フィルター +extension Defaults.URL { + static func applyFilters(_ filter: Filter) -> [String] { + var filters = [String]() + + var category = 0 + category += filter.doujinshi.isFiltered ? Category.Doujinshi.value : 0 + category += filter.manga.isFiltered ? Category.Manga.value : 0 + category += filter.artist_CG.isFiltered ? Category.Artist_CG.value : 0 + category += filter.game_CG.isFiltered ? Category.Game_CG.value : 0 + category += filter.western.isFiltered ? Category.Western.value : 0 + category += filter.non_h.isFiltered ? Category.Non_H.value : 0 + category += filter.image_set.isFiltered ? Category.Image_Set.value : 0 + category += filter.cosplay.isFiltered ? Category.Cosplay.value : 0 + category += filter.asian_porn.isFiltered ? Category.Asian_Porn.value : 0 + category += filter.misc.isFiltered ? Category.Misc.value : 0 - static func search(keyword: String) -> String { - merge([host, listCompact, f_search, keyword.URLString()]) - } - static func popularList() -> String { - merge([host, listCompact]) - } - static func favoritesList() -> String { - merge([host + favorites, listCompact]) + if ![0, 1023].contains(category) { + filters.append(f_cats + "\(category)") } - static func mangaDetail(url: String) -> String { - merge([url, ignoreOffensive, showComments, detailLarge]) - } - static func contentPage(url: String, page: Int) -> String { - merge([url, p + "\(page)"]) - } + if !filter.advanced { return filters } + filters.append(advSearch) + + if filter.galleryName { filters.append(f_sname_on) } + if filter.galleryTags { filters.append(f_stags_on) } + if filter.galleryDesc { filters.append(f_sdesc_on) } + if filter.torrentFilenames { filters.append(f_storr_on) } + if filter.onlyWithTorrents { filters.append(f_sto_on) } + if filter.lowPowerTags { filters.append(f_sdt1_on) } + if filter.downvotedTags { filters.append(f_sdt2_on) } + if filter.expungedGalleries { filters.append(f_sh_on) } - static func addFavorite(id: String, token: String) -> String { - merge([host + gallerypopups, gid + id, t + token, addfav_act]) + if filter.minRatingActivated, + [2, 3, 4, 5].contains(filter.minRating) + { + filters.append(f_sr_on) + filters.append(f_srdd + "\(filter.minRating)") } - static func merge(_ urls: [String]) -> String { - let firstTwo = urls.prefix(2) - let remainder = urls.suffix(from: 2) - - var joinedArray = [String]() - joinedArray.append(firstTwo.joined(separator: "?")) - joinedArray.append(remainder.joined(separator: "&")) - return joinedArray.joined(separator: "&") + if filter.pageRangeActivated, + let minPages = Int(filter.pageLowerBound), + let maxPages = Int(filter.pageUpperBound), + minPages > 0 && maxPages > 0 && minPages <= maxPages + { + filters.append(f_sp_on) + filters.append(f_spf + "\(minPages)") + filters.append(f_spt + "\(maxPages)") } + + if filter.disableLanguage { filters.append(f_sfl_on) } + if filter.disableUploader { filters.append(f_sfu_on) } + if filter.disableTags { filters.append(f_sft_on) } + + return filters + } +} + +// MARK: ツール +extension Defaults.URL { + static func merge(_ urls: [String]) -> String { + let firstTwo = urls.prefix(2) + let remainder = urls.suffix(from: 2) + + var joinedArray = [String]() + joinedArray.append(firstTwo.joined(separator: "?")) + joinedArray.append(remainder.joined(separator: "&")) + return joinedArray.joined(separator: "&") } } diff --git "a/EhPanda/\343\202\242\343\203\227\343\203\252/en.lproj/Localizable.strings" "b/EhPanda/\343\202\242\343\203\227\343\203\252/en.lproj/Localizable.strings" index cbe7a25d..cf5e408f 100644 --- "a/EhPanda/\343\202\242\343\203\227\343\203\252/en.lproj/Localizable.strings" +++ "b/EhPanda/\343\202\242\343\203\227\343\203\252/en.lproj/Localizable.strings" @@ -61,6 +61,27 @@ "警告" = "WARNING"; "デバッグ専用機能です" = "It's for debug only"; +// MARK: FilterView +"フィルター" = "Filters"; +"基本" = "BASIC"; +"高度な設定" = "Advanced Settings"; +"高度" = "ADVANCED"; +"ギャラリー名を検索" = "Search Gallery Name"; +"ギャラリータグを検索" = "Search Gallery Tags"; +"ギャラリー説明を検索" = "Search Gallery Description"; +"トレントファイル名を検索" = "Search Torrent Filenames"; +"トレントを含むもののみを表示" = "Only Show Galleries With Torrents"; +"低希望タグを検索" = "Search Low-Power Tags"; +"低評価タグを検索" = "Search Downvoted Tags"; +"削除済みのギャラリーを表示" = "Show Expunged Galleries"; +"評価の下限を指定" = "Set Minimum Rating"; +"ページ数範囲を指定" = "Set Pages Range"; +"範囲" = "Range"; +"既定フィルター" = "DEFAULT FILTER"; +"言語フィルターを無効化" = "Disable Language Filter"; +"アップローダフィルターを無効化" = "Disable Uploader Filter"; +"タグフィルターを無効化" = "Disable Tags Filter"; + // MARK: Category "同人誌" = "Doujinshi"; "漫画" = "Manga"; diff --git "a/EhPanda/\343\202\242\343\203\227\343\203\252/zh-Hans.lproj/Localizable.strings" "b/EhPanda/\343\202\242\343\203\227\343\203\252/zh-Hans.lproj/Localizable.strings" index 686908bb..b7ba7b72 100644 --- "a/EhPanda/\343\202\242\343\203\227\343\203\252/zh-Hans.lproj/Localizable.strings" +++ "b/EhPanda/\343\202\242\343\203\227\343\203\252/zh-Hans.lproj/Localizable.strings" @@ -49,7 +49,7 @@ "アカウント" = "账户"; "ギャラリー" = "画廊"; "ログイン済み" = "已登陆"; -"ログイン" = "前往E-Hentai登陆"; +"ログイン" = "登陆"; "ログアウト" = "退出登陆"; "本当にログアウトしますか?" = "确定要退出登陆吗?"; "キャッシュ" = "缓存"; @@ -60,6 +60,27 @@ "警告" = "警告"; "デバッグ専用機能です" = "这是仅为问题排查设计的功能"; +// MARK: FilterView +"フィルター" = "筛选"; +"基本" = "基础"; +"高度な設定" = "高级选项"; +"高度" = "高级"; +"ギャラリー名を検索" = "搜索画廊名称"; +"ギャラリータグを検索" = "搜索画廊标签"; +"ギャラリー説明を検索" = "搜索画廊描述"; +"トレントファイル名を検索" = "搜索种子文件名"; +"トレントを含むもののみを表示" = "只显示带有种子的画廊"; +"低希望タグを検索" = "搜索低期望标签"; +"低評価タグを検索" = "搜索低评价标签"; +"削除済みのギャラリーを表示" = "显示已被删除的画廊"; +"評価の下限を指定" = "设定评分下限"; +"ページ数範囲を指定" = "设定页数范围"; +"範囲" = "范围"; +"既定フィルター" = "默认筛选"; +"言語フィルターを無効化" = "禁用语言筛选"; +"アップローダフィルターを無効化" = "禁用上传者筛选"; +"タグフィルターを無効化" = "禁用标签筛选"; + // MARK: Category "同人誌" = "同人志"; "漫画" = "漫画"; diff --git "a/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppAction.swift" "b/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppAction.swift" index 98c28625..3a2ec9ab 100644 --- "a/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppAction.swift" +++ "b/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppAction.swift" @@ -11,11 +11,14 @@ import Foundation enum AppAction { case updateUser(user: User?) case eraseCachedList + case initiateFilter case toggleNavBarHidden(isHidden: Bool) + case toggleSettingPresented case toggleWebViewPresented case toggleLogoutPresented + case toggleFilterViewPresented case toggleEraseImageCachesPresented case toggleEraseCachedListPresented case toggleDraftCommentViewPresented_Button @@ -24,7 +27,6 @@ enum AppAction { case cleanCommentContent_Button case cleanCommentContent_BarItem - case toggleSettingPresented case toggleHomeListType(type: HomeListType) case fetchSearchItems(keyword: String) diff --git "a/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppCommand.swift" "b/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppCommand.swift" index 05696db8..d295c302 100644 --- "a/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppCommand.swift" +++ "b/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppCommand.swift" @@ -15,10 +15,11 @@ protocol AppCommand { struct FetchSearchItemsCommand: AppCommand { let keyword: String + let filter: Filter func execute(in store: Store) { let token = SubscriptionToken() - SearchItemsRequest(keyword: keyword) + SearchItemsRequest(keyword: keyword, filter: filter) .publisher .receive(on: DispatchQueue.main) .sink { completion in diff --git "a/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppState.swift" "b/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppState.swift" index d4447467..ecafb4f1 100644 --- "a/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppState.swift" +++ "b/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/AppState.swift" @@ -19,16 +19,19 @@ struct AppState { extension AppState { struct Environment { var navBarHidden = false - var isWebViewPresented = false var isSettingPresented = false + var isWebViewPresented = false var isLogoutPresented = false var isEraseImageCachesPresented = false var isEraseCachedListPresented = false + var isFilterViewPresented = false } struct Settings { @FileStorage(directory: .cachesDirectory, fileName: "user.json") var user: User? + @FileStorage(directory: .cachesDirectory, fileName: "filter.json") + var filter: Filter? var galleryType: GalleryType { get { let rawValue = UserDefaults diff --git "a/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/Store.swift" "b/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/Store.swift" index be88d630..bb817978 100644 --- "a/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/Store.swift" +++ "b/EhPanda/\343\203\207\343\203\274\343\202\277\343\203\225\343\203\255\343\203\274/Store.swift" @@ -29,6 +29,8 @@ class Store: ObservableObject { appState.settings.user = user case .eraseCachedList: appState.cachedList.items = nil + case .initiateFilter: + appState.settings.filter = Filter() case .toggleNavBarHidden(let isHidden): appState.environment.navBarHidden = isHidden @@ -40,6 +42,8 @@ class Store: ObservableObject { appState.environment.isEraseImageCachesPresented.toggle() case .toggleEraseCachedListPresented: appState.environment.isEraseCachedListPresented.toggle() + case .toggleFilterViewPresented: + appState.environment.isFilterViewPresented.toggle() case .toggleDraftCommentViewPresented_Button: appState.detailInfo.isDraftCommentViewPresented_Button.toggle() case .toggleDraftCommentViewPresented_BarItem: @@ -62,7 +66,9 @@ class Store: ObservableObject { if appState.homeList.searchLoading { break } appState.homeList.searchLoading = true - appCommand = FetchSearchItemsCommand(keyword: keyword) + + let filter = appState.settings.filter ?? Filter() + appCommand = FetchSearchItemsCommand(keyword: keyword, filter: filter) case .fetchSearchItemsDone(let result): appState.homeList.searchLoading = false diff --git "a/EhPanda/\343\203\215\343\203\203\343\203\210\343\203\257\343\203\274\343\202\257/Request.swift" "b/EhPanda/\343\203\215\343\203\203\343\203\210\343\203\257\343\203\274\343\202\257/Request.swift" index cf6d2be5..82ae2606 100644 --- "a/EhPanda/\343\203\215\343\203\203\343\203\210\343\203\257\343\203\274\343\202\257/Request.swift" +++ "b/EhPanda/\343\203\215\343\203\203\343\203\210\343\203\257\343\203\274\343\202\257/Request.swift" @@ -11,13 +11,17 @@ import Foundation struct SearchItemsRequest { let keyword: String + let filter: Filter let parser = Parser() var publisher: AnyPublisher<[Manga], AppError> { let word = keyword.replacingOccurrences(of: " ", with: "+") return URLSession.shared .dataTaskPublisher( - for: URL(string: Defaults.URL.search(keyword: word))! + for: URL(string: Defaults.URL.search( + keyword: word, + filter: filter + ))! ) .tryMap { try Kanna.HTML(html: $0.data, encoding: .utf8) } .map(parser.parseListItems) diff --git "a/EhPanda/\343\203\223\343\203\245\343\203\274/DetailView.swift" "b/EhPanda/\343\203\223\343\203\245\343\203\274/DetailView.swift" index fdc62d94..fc95368e 100644 --- "a/EhPanda/\343\203\223\343\203\245\343\203\274/DetailView.swift" +++ "b/EhPanda/\343\203\223\343\203\245\343\203\274/DetailView.swift" @@ -127,7 +127,7 @@ private struct HeaderView: View { Spacer() HStack { if exx { - Text(manga.translatedCategory.lString()) + Text(manga.jpnCategories.lString()) .fontWeight(.bold) .lineLimit(1) .font(.headline) diff --git "a/EhPanda/\343\203\223\343\203\245\343\203\274/FilterView.swift" "b/EhPanda/\343\203\223\343\203\245\343\203\274/FilterView.swift" new file mode 100644 index 00000000..1ca60f14 --- /dev/null +++ "b/EhPanda/\343\203\223\343\203\245\343\203\274/FilterView.swift" @@ -0,0 +1,166 @@ +// +// FilterView.swift +// EhPanda +// +// Created by 荒木辰造 on R 3/01/08. +// + +import SwiftUI + +struct FilterView: View { + @EnvironmentObject var store: Store + @State var horizontalPadding: CGFloat = 0 + + var settings: AppState.Settings { + store.appState.settings + } + var settingsBinding: Binding { + $store.appState.settings + } + + var body: some View { + NavigationView { + if let filter = settings.filter, + let filterBinding = Binding(settingsBinding.filter) { + Form { + Section(header: Text("基本")) { + CategoryView() + Toggle("高度な設定", isOn: filterBinding.advanced) + } + if filter.advanced { + Section(header: Text("高度")) { + Toggle("ギャラリー名を検索", isOn: filterBinding.galleryName) + Toggle("ギャラリータグを検索", isOn: filterBinding.galleryTags) + Toggle("ギャラリー説明を検索", isOn: filterBinding.galleryDesc) + Toggle("トレントファイル名を検索", isOn: filterBinding.torrentFilenames) + Toggle("トレントを含むもののみを表示", isOn: filterBinding.onlyWithTorrents) + Toggle("低希望タグを検索", isOn: filterBinding.lowPowerTags) + Toggle("低評価タグを検索", isOn: filterBinding.downvotedTags) + Toggle("削除済みのギャラリーを表示", isOn: filterBinding.expungedGalleries) + } + Section { + Toggle("評価の下限を指定", isOn: filterBinding.minRatingActivated) + if filter.minRatingActivated { + Picker(selection: filterBinding.minRating, label: Text("評価の下限"), content: { + Text("2").tag(2) + Text("3").tag(3) + Text("4").tag(4) + Text("5").tag(5) + }) + .pickerStyle(SegmentedPickerStyle()) + } + Toggle("ページ数範囲を指定", isOn: filterBinding.pageRangeActivated) + if filter.pageRangeActivated { + HStack { + Text("範囲") + Spacer() + TextField("", text: filterBinding.pageLowerBound) + .multilineTextAlignment(.center) + .background(Color(.systemGray4)) + .frame(width: 50) + .cornerRadius(5) + Text("-") + TextField("", text: filterBinding.pageUpperBound) + .multilineTextAlignment(.center) + .background(Color(.systemGray4)) + .frame(width: 50) + .cornerRadius(5) + } + } + } + Section(header: Text("既定フィルター")) { + Toggle("言語フィルターを無効化", isOn: filterBinding.disableLanguage) + Toggle("アップローダフィルターを無効化", isOn: filterBinding.disableUploader) + Toggle("タグフィルターを無効化", isOn: filterBinding.disableTags) + } + } + } + .padding(.horizontal, horizontalPadding) + .navigationBarTitle("フィルター") + } + } + .onAppear(perform: onAppear) + } + func onAppear() { + if isPad { + horizontalPadding = 20 + } + if settings.filter == nil { + store.dispatch(.initiateFilter) + } + } +} + +private struct CategoryView: View { + @EnvironmentObject var store: Store + + var settings: AppState.Settings { + store.appState.settings + } + var settingsBinding: Binding { + $store.appState.settings + } + + let gridItems = [ + GridItem(.adaptive(minimum: 80, maximum: 100)) + ] + + var body: some View { + if let filter = settings.filter, + let filterBinding = Binding(settingsBinding.filter) { + LazyVGrid(columns: gridItems) { + ForEach(tuples(filter, filterBinding)) { tuple in + CategoryCell(isFiltered: tuple.isFiltered, category: tuple.category) + } + } + .padding(.vertical) + } + } + + private func tuples(_ filter: Filter, _ filterBinding: Binding) -> [TupleCategory] { + [TupleCategory(isFiltered: filterBinding.doujinshi.isFiltered, category: filter.doujinshi.category), + TupleCategory(isFiltered: filterBinding.manga.isFiltered, category: filter.manga.category), + TupleCategory(isFiltered: filterBinding.artist_CG.isFiltered, category: filter.artist_CG.category), + TupleCategory(isFiltered: filterBinding.game_CG.isFiltered, category: filter.game_CG.category), + TupleCategory(isFiltered: filterBinding.western.isFiltered, category: filter.western.category), + TupleCategory(isFiltered: filterBinding.non_h.isFiltered, category: filter.non_h.category), + TupleCategory(isFiltered: filterBinding.image_set.isFiltered, category: filter.image_set.category), + TupleCategory(isFiltered: filterBinding.cosplay.isFiltered, category: filter.cosplay.category), + TupleCategory(isFiltered: filterBinding.asian_porn.isFiltered, category: filter.asian_porn.category), + TupleCategory(isFiltered: filterBinding.misc.isFiltered, category: filter.misc.category)] + + } + + private struct TupleCategory: Identifiable { + var id = UUID() + + let isFiltered: Binding + let category: Category + } +} + +private struct CategoryCell: View { + @Binding var isFiltered: Bool + let category: Category + + var body: some View { + ZStack { + Rectangle() + .foregroundColor( + isFiltered + ? category.color.opacity(0.3) + : category.color + ) + Text(category.jpn.lString()) + .fontWeight(.bold) + .foregroundColor(.white) + .padding(.vertical, 5) + } + .onTapGesture(perform: onTapGesture) + .cornerRadius(5) + } + + func onTapGesture() { + isFiltered.toggle() + } +} diff --git "a/EhPanda/\343\203\223\343\203\245\343\203\274/HomeView.swift" "b/EhPanda/\343\203\223\343\203\245\343\203\274/HomeView.swift" index 4d82973b..33df8415 100644 --- "a/EhPanda/\343\203\223\343\203\245\343\203\274/HomeView.swift" +++ "b/EhPanda/\343\203\223\343\203\245\343\203\274/HomeView.swift" @@ -89,6 +89,13 @@ struct HomeView: View { commitAction: searchBarCommit, filterAction: searchBarFilter ) + .sheet( + isPresented: environmentBinding.isFilterViewPresented, + content: { + FilterView() + .environmentObject(store) + } + ) } conditionalList } @@ -141,7 +148,7 @@ struct HomeView: View { fetchSearchItems() } func searchBarFilter() { - + store.dispatch(.toggleFilterViewPresented) } func fetchSearchItems() { @@ -239,13 +246,11 @@ private struct SearchBar: View { if !keyword.isEmpty { Image(systemName: "xmark.circle.fill") .foregroundColor(.gray) - .onTapGesture { - keyword = "" - } + .onTapGesture(perform: onClearButtonTap) } Image(systemName: "slider.horizontal.3") .foregroundColor(.gray) - .onTapGesture {} + .onTapGesture(perform: filterAction) } } @@ -256,6 +261,10 @@ private struct SearchBar: View { .cornerRadius(8) .padding(.bottom, 10) } + + func onClearButtonTap() { + keyword = "" + } } // MARK: 概要列 @@ -293,7 +302,7 @@ private struct MangaSummaryRow: View { } HStack(alignment: .bottom) { if exx { - Text(manga.translatedCategory.lString()) + Text(manga.jpnCategories.lString()) .fontWeight(.bold) .lineLimit(1) .font(.footnote) diff --git "a/EhPanda/\343\203\223\343\203\245\343\203\274/SettingView.swift" "b/EhPanda/\343\203\223\343\203\245\343\203\274/SettingView.swift" index f3bf1dc0..d116fe4f 100644 --- "a/EhPanda/\343\203\223\343\203\245\343\203\274/SettingView.swift" +++ "b/EhPanda/\343\203\223\343\203\245\343\203\274/SettingView.swift" @@ -10,6 +10,7 @@ import SDWebImageSwiftUI struct SettingView: View { @EnvironmentObject var store: Store + @State var horizontalPadding: CGFloat = 0 var settingsBinding: Binding { $store.appState.settings @@ -95,8 +96,16 @@ struct SettingView: View { }) } } + .padding(.horizontal, horizontalPadding) .navigationBarTitle("設定") } + .onAppear(perform: onAppear) + } + + func onAppear() { + if isPad { + horizontalPadding = 20 + } } func toggleLogout() { diff --git "a/EhPanda/\343\203\223\343\203\245\343\203\274/\343\203\204\343\203\274\343\203\253/BlurView.swift" "b/EhPanda/\343\203\223\343\203\245\343\203\274/\343\203\204\343\203\274\343\203\253/BlurView.swift" deleted file mode 100644 index 5b244ec0..00000000 --- "a/EhPanda/\343\203\223\343\203\245\343\203\274/\343\203\204\343\203\274\343\203\253/BlurView.swift" +++ /dev/null @@ -1,41 +0,0 @@ -// -// BlurView.swift -// PokeMaster -// -// Created by 王 巍 on 2019/08/09. -// Copyright © 2019 OneV's Den. All rights reserved. -// - -import SwiftUI -import UIKit - -struct BlurView : UIViewRepresentable { - let style: UIBlurEffect.Style - - func makeUIView(context: UIViewRepresentableContext) -> UIView { - let view = UIView(frame: .zero) - view.backgroundColor = .clear - let blurEffect = UIBlurEffect(style: style) - let blurView = UIVisualEffectView(effect: blurEffect) - - blurView.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(blurView) - NSLayoutConstraint.activate([ - blurView.heightAnchor.constraint(equalTo: view.heightAnchor), - blurView.widthAnchor.constraint(equalTo: view.widthAnchor) - ]) - return view - } - - func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext) { - } -} - -extension View { - func blurBackground(style: UIBlurEffect.Style) -> some View { - ZStack { - BlurView(style: style) - self - } - } -} diff --git "a/EhPanda/\343\203\223\343\203\245\343\203\274/\343\203\204\343\203\274\343\203\253/OverlaySheet.swift" "b/EhPanda/\343\203\223\343\203\245\343\203\274/\343\203\204\343\203\274\343\203\253/OverlaySheet.swift" deleted file mode 100644 index a30bce35..00000000 --- "a/EhPanda/\343\203\223\343\203\245\343\203\274/\343\203\204\343\203\274\343\203\253/OverlaySheet.swift" +++ /dev/null @@ -1,57 +0,0 @@ -// -// OverlaySheet.swift -// PokeMaster -// -// Created by 王 巍 on 2019/08/11. -// Copyright © 2019 OneV's Den. All rights reserved. -// - -import SwiftUI - -struct OverlaySheet: View { - - private let isPresented: Binding - private let makeContent: () -> Content - - @GestureState private var translation = CGPoint.zero - - init(isPresented: Binding, @ViewBuilder content: @escaping () -> Content) { - self.isPresented = isPresented - self.makeContent = content - } - - var body: some View { - VStack { - Spacer() - makeContent() - } - .offset(y: (isPresented.wrappedValue ? 0 : UIScreen.main.bounds.height) + max(0, translation.y)) - .animation(.interpolatingSpring(stiffness: 70, damping: 12)) - .edgesIgnoringSafeArea(.bottom) - .gesture(panelDraggingGesture) - } - - var panelDraggingGesture: some Gesture { - DragGesture() - .updating($translation) { current, state, _ in - state.y = current.translation.height - } - .onEnded { state in - if state.translation.height > 250 { - self.isPresented.wrappedValue = false - } - } - } -} - -extension View { - func overlaySheet( - isPresented: Binding, - @ViewBuilder content: @escaping () -> Content - ) -> some View - { - overlay( - OverlaySheet(isPresented: isPresented, content: content) - ) - } -} diff --git "a/EhPanda/\343\203\242\343\203\207\343\203\253/Filter.swift" "b/EhPanda/\343\203\242\343\203\207\343\203\253/Filter.swift" new file mode 100644 index 00000000..b63de786 --- /dev/null +++ "b/EhPanda/\343\203\242\343\203\207\343\203\253/Filter.swift" @@ -0,0 +1,64 @@ +// +// Filter.swift +// EhPanda +// +// Created by 荒木辰造 on R 3/01/08. +// + +import SwiftUI + +struct Filter: Codable { + // ブール値が真の場合、検索結果に出すことを意味する + var doujinshi = AssociatedCategory(category: .Doujinshi, isFiltered: false) + var manga = AssociatedCategory(category: .Manga, isFiltered: false) + var artist_CG = AssociatedCategory(category: .Artist_CG, isFiltered: false) + var game_CG = AssociatedCategory(category: .Game_CG, isFiltered: false) + var western = AssociatedCategory(category: .Western, isFiltered: false) + var non_h = AssociatedCategory(category: .Non_H, isFiltered: false) + var image_set = AssociatedCategory(category: .Image_Set, isFiltered: false) + var cosplay = AssociatedCategory(category: .Cosplay, isFiltered: false) + var asian_porn = AssociatedCategory(category: .Asian_Porn, isFiltered: false) + var misc = AssociatedCategory(category: .Misc, isFiltered: false) + + // オフにすると、下にあるすべての関数が無視される + var advanced = false + var galleryName = true + var galleryTags = true + var galleryDesc = false + var torrentFilenames = false + var onlyWithTorrents = false + var lowPowerTags = false { + didSet { + if lowPowerTags == true { + downvotedTags = false + } + } + } + var downvotedTags = false { + didSet { + if downvotedTags == true { + lowPowerTags = false + } + } + } + var expungedGalleries = false + + var minRatingActivated = false + var minRating: Int = -1 + + var pageRangeActivated = false + var pageLowerBound: String = "" + var pageUpperBound: String = "" + + var disableLanguage = false + var disableUploader = false + var disableTags = false +} + +struct AssociatedCategory: Codable { + let category: Category + var isFiltered: Bool + var color: Color { + category.color + } +} diff --git "a/EhPanda/\343\203\242\343\203\207\343\203\253/Manga.swift" "b/EhPanda/\343\203\242\343\203\207\343\203\253/Manga.swift" index d9ef9e84..31ea2b15 100644 --- "a/EhPanda/\343\203\242\343\203\207\343\203\253/Manga.swift" +++ "b/EhPanda/\343\203\242\343\203\207\343\203\253/Manga.swift" @@ -74,7 +74,9 @@ struct MangaContent: Identifiable, Codable { } // MARK: 列挙型 -enum Category: String, Codable { +enum Category: String, Codable, CaseIterable, Identifiable { + var id: String { rawValue } + case Doujinshi = "Doujinshi" case Manga = "Manga" case Artist_CG = "Artist CG" @@ -142,15 +144,43 @@ extension Manga { var notFilledCount: Int { 5 - filledCount - halfFilledCount } var color: Color { - let colorName = - galleryType.rawValue - + "/" + translatedCategory - - return Color(colorName) + category.color } - - var translatedCategory: String { - switch category { + var jpnCategories: String { + category.jpn + } +} + +extension Category { + var color: Color { + Color(galleryType.rawValue + "/" + jpn) + } + var value: Int { + switch self { + case .Doujinshi: + return 2 + case .Manga: + return 4 + case .Artist_CG: + return 8 + case .Game_CG: + return 16 + case .Western: + return 512 + case .Non_H: + return 256 + case .Image_Set: + return 32 + case .Cosplay: + return 64 + case .Asian_Porn: + return 128 + case .Misc: + return 1 + } + } + var jpn: String { + switch self { case .Doujinshi: return "同人誌" case .Manga: