Skip to content

Commit fe9c8d5

Browse files
feat: add NSFW filter and NSFW replace cover #6
* 准备添加NSFW过滤器 * Add selection game transition animation * Add nsis multi-language * add NSFW filter and NSFW cover blur * del debug console * update language * del debug console * Set persistent storage in nsfw and change the cover blur to replace the cover * del debug console and default off nsfwCoverReplace --------- Co-authored-by: huoshen80 <huoshen80@hotmail.com>
1 parent a4ccbca commit fe9c8d5

13 files changed

Lines changed: 776 additions & 618 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ The `Reina` in the name is the character <a href="https://vndb.org/c64303"><b>
5151
- [ ] Sync games status with Bangumi
5252
- [ ] Bulk import games
5353
- [ ] Tool: migrate whitecloud data into reinaManager
54+
- [ ] Add hide NSFW filter
5455

5556
## Demo
5657
##### Frontend Demo
@@ -76,3 +77,4 @@ This project is licensed under the [AGPL-3.0 license](https://github.com/huoshen
7677
[![Star History Chart](https://api.star-history.com/svg?repos=huoshen80/ReinaManager&type=Date)](https://star-history.com/#huoshen80/ReinaManager&Date)
7778

7879

80+

public/images/NR18.png

267 KB
Loading

src-tauri/tauri.conf.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,14 @@
4141
"publisher": "huoshen80",
4242
"windows": {
4343
"nsis": {
44-
"installerIcon": "icons/icon.ico"
44+
"installerIcon": "icons/icon.ico",
45+
"languages": [
46+
"English",
47+
"SimpChinese",
48+
"TradChinese",
49+
"Japanese"
50+
],
51+
"displayLanguageSelector": true
4552
}
4653
}
4754
},
@@ -52,4 +59,4 @@
5259
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDI0MzZDRDk1QzdDMTU3OTgKUldTWVY4SEhsYzAySktqTVVKdVRjWFBJbnRubGx6cWROeHloc3dpZ252MGVtSUI0REVIaEUzYnQK"
5360
}
5461
}
55-
}
62+
}

src/components/Cards/index.tsx

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
* - @/store
1515
*/
1616

17-
import { useState, memo } from 'react';
17+
import {memo, useState} from 'react';
1818
import Card from '@mui/material/Card';
1919
import CardMedia from '@mui/material/CardMedia';
2020
import CardActionArea from '@mui/material/CardActionArea';
2121
import RightMenu from '@/components/RightMenu';
22-
import { useStore } from '@/store';
23-
import type { GameData } from '@/types';
22+
import {useStore} from '@/store';
23+
import type {GameData} from '@/types';
2424
import KeepAlive from 'react-activation';
25-
import { useTranslation } from 'react-i18next';
26-
import { getGameDisplayName } from '@/utils';
25+
import {useTranslation} from 'react-i18next';
26+
import {getGameDisplayName, isNsfwGame} from '@/utils';
2727

2828
/**
2929
* Cards 组件用于展示游戏卡片列表。
@@ -33,45 +33,55 @@ import { getGameDisplayName } from '@/utils';
3333
* @returns {JSX.Element} 游戏卡片列表
3434
*/
3535
// 单个卡片项,使用memo避免无关渲染
36-
const CardItem = memo(({ card, isActive, onContextMenu, onClick, displayName }: {
36+
const CardItem = memo(({card, isActive, onContextMenu, onClick, displayName}: {
3737
card: GameData;
3838
isActive: boolean;
3939
onContextMenu: (e: React.MouseEvent) => void;
4040
onClick: () => void;
4141
displayName: string;
42-
}) => (
43-
<Card
44-
key={card.id}
45-
className={`min-w-24 max-w-full ${isActive ? 'scale-y-105' : ''}`}
46-
onContextMenu={onContextMenu}
47-
>
48-
<CardActionArea
49-
onClick={onClick}
50-
className={`
51-
duration-100
52-
hover:shadow-lg hover:scale-105
53-
active:shadow-sm active:scale-95
54-
`}
42+
}) => {
43+
const {nsfwCoverReplace} = useStore();
44+
45+
// 确保 tags 统一为数组
46+
const tags = typeof card.tags === "string" ? JSON.parse(card.tags) : card.tags;
47+
const isNsfw = isNsfwGame(tags);
48+
49+
return (
50+
<Card
51+
key={card.id}
52+
className={`min-w-24 max-w-full !transition-all ${isActive ? 'scale-y-105' : 'scale-y-100'}`}
53+
onContextMenu={onContextMenu}
5554
>
56-
<CardMedia
57-
component="img"
58-
className="h-auto aspect-[3/4]"
59-
image={card.image}
60-
alt="Card Image"
61-
draggable="false"
62-
loading="lazy"
63-
/>
64-
<div className={`p-1 h-8 text-base truncate ${isActive ? '!font-bold text-blue-500' : ''}`}>{displayName}</div>
65-
</CardActionArea>
66-
</Card>
67-
));
55+
<CardActionArea
56+
onClick={onClick}
57+
className={`
58+
duration-100
59+
hover:shadow-lg hover:scale-105
60+
active:shadow-sm active:scale-95
61+
`}
62+
>
63+
<CardMedia
64+
component="img"
65+
className="h-auto aspect-[3/4]"
66+
image={nsfwCoverReplace && isNsfw ? "/images/NR18.png" : card.image}
67+
alt="Card Image"
68+
draggable="false"
69+
loading="lazy"
70+
/>
71+
<div className={`p-1 h-8 text-base truncate ${isActive ? '!font-bold text-blue-500' : ''}`}>
72+
{displayName}
73+
</div>
74+
</CardActionArea>
75+
</Card>
76+
);
77+
});
6878

6979
const Cards = () => {
7080
// 只订阅需要的状态,减少重渲染
7181
const selectedGameId = useStore(s => s.selectedGameId);
7282
const setSelectedGameId = useStore(s => s.setSelectedGameId);
7383
const games = useStore(s => s.games);
74-
const { i18n } = useTranslation();
84+
const {i18n} = useTranslation();
7585
const [menuPosition, setMenuPosition] = useState<{
7686
mouseX: number;
7787
mouseY: number;
@@ -92,17 +102,18 @@ const Cards = () => {
92102
};
93103
return (
94104
<KeepAlive>
95-
<div className="flex-1 text-center grid grid-cols-5 lg:grid-cols-6 xl:grid-cols-7 2xl:grid-cols-8 gap-4 p-4">
105+
<div
106+
className="flex-1 text-center grid grid-cols-5 lg:grid-cols-6 xl:grid-cols-7 2xl:grid-cols-8 gap-4 p-4">
96107
{/* 右键菜单组件 */}
97108
<RightMenu id={menuPosition?.cardId} isopen={Boolean(menuPosition)}
98-
anchorPosition={
99-
menuPosition
100-
? { top: menuPosition.mouseY, left: menuPosition.mouseX }
101-
: undefined
102-
}
103-
setAnchorEl={(value) => {
104-
if (!value) setMenuPosition(null);
105-
}} /> {/* 游戏卡片渲染 */}
109+
anchorPosition={
110+
menuPosition
111+
? {top: menuPosition.mouseY, left: menuPosition.mouseX}
112+
: undefined
113+
}
114+
setAnchorEl={(value) => {
115+
if (!value) setMenuPosition(null);
116+
}}/> {/* 游戏卡片渲染 */}
106117
{games.map((card) => {
107118
const displayName = getGameDisplayName(card, i18n.language);
108119

@@ -122,4 +133,4 @@ const Cards = () => {
122133
);
123134
};
124135

125-
export default Cards;
136+
export default Cards;

src/locales/en_US.json

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
"saveError": "Failed to save BGM Token",
4848
"clearToken": "Clear token"
4949
},
50+
"nsfw": {
51+
"title": "NSFW Settings",
52+
"filter": "Filter NSFW content",
53+
"coverReplace": "NSFW cover replace"
54+
},
5055
"savePath": {
5156
"title": "Game Save Backup Path",
5257
"pathLabel": "Backup Root Directory Path",
@@ -154,7 +159,7 @@
154159
"inputGameName": "Enter game name",
155160
"clearSearch": "Clear search"
156161
},
157-
"Toolbar": {
162+
"Toolbar": {
158163
"gameLibrary": "Game Library",
159164
"openGameFolder": "Open Game Folder",
160165
"deleteGame": "Delete Game",
@@ -170,7 +175,7 @@
170175
"gamePathNotFound": "Game path not found",
171176
"launchFailed": "Failed to launch game:",
172177
"playing": "Playing..."
173-
},
178+
},
174179
"SortModal": {
175180
"sort": "Sort",
176181
"sortMethod": "Sort Method:",
@@ -207,14 +212,14 @@
207212
"warning": "Warning",
208213
"gameNotFound": "Game data not found",
209214
"deleteBackupTitle": "Delete Backup?"
210-
},
215+
},
211216
"AddModal": {
212217
"addGame": "Add Game",
213218
"noExecutableSelected": "No executable selected",
214219
"gameExists": "Game already exists",
215220
"selectLauncher": "Select Launcher",
216221
"selectExecutable": "Please select an executable file",
217-
"enableCustomMode": "Enable Custom Mode",
222+
"enableCustomMode": "Enable Custom Mode",
218223
"enableVNDB": "Use VNDB API (BGM API by default)",
219224
"gameName": "Game Name",
220225
"gameIDTips": "Game ID (Enter BGM ID or VNDB ID)",
@@ -259,7 +264,7 @@
259264
"added": "Added {{title}}",
260265
"played": "Played {{title}}",
261266
"addedAt": "Added at {{time}}",
262-
"playedAtTime": "Played at {{time}}",
267+
"playedAtTime": "Played at {{time}}",
263268
"duration": "Play duration: {{duration}}"
264269
},
265270
"lastPlayed": "Last played: {{time}}",
@@ -285,4 +290,4 @@
285290
"allFiles": "All Files"
286291
}
287292
}
288-
}
293+
}

src/locales/ja_JP.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
"saveError": "BGM Token の保存に失敗しました",
4848
"clearToken": "トークンをクリア"
4949
},
50+
"nsfw": {
51+
"title": "NSFWセット",
52+
"filter": "NSFWコンテンツのフィルタリング",
53+
"coverReplace": "NSFW表紙の置き換え"
54+
},
5055
"savePath": {
5156
"title": "ゲームセーブバックアップパス",
5257
"pathLabel": "バックアップルートディレクトリパス",
@@ -154,7 +159,7 @@
154159
"inputGameName": "ゲーム名を入力",
155160
"clearSearch": "検索をクリア"
156161
},
157-
"Toolbar": {
162+
"Toolbar": {
158163
"gameLibrary": "ゲームライブラリ",
159164
"openGameFolder": "ゲームフォルダを開く",
160165
"deleteGame": "ゲームを削除",
@@ -170,7 +175,7 @@
170175
"gamePathNotFound": "ゲームパスが見つかりません",
171176
"launchFailed": "ゲーム起動に失敗しました:",
172177
"playing": "プレイ中..."
173-
},
178+
},
174179
"SortModal": {
175180
"sort": "並べ替え",
176181
"sortMethod": "並べ替え方法:",
@@ -207,14 +212,14 @@
207212
"warning": "警告",
208213
"gameNotFound": "ゲームデータが見つかりません",
209214
"deleteBackupTitle": "バックアップを削除"
210-
},
215+
},
211216
"AddModal": {
212217
"addGame": "ゲームを追加",
213218
"noExecutableSelected": "実行ファイルが選択されていません",
214219
"gameExists": "ゲームはすでに存在します",
215220
"selectLauncher": "ランチャーを選択",
216221
"selectExecutable": "実行ファイルを選択してください",
217-
"enableCustomMode": "カスタムモードを有効化",
222+
"enableCustomMode": "カスタムモードを有効化",
218223
"enableVNDB": "VNDB APIを使用する(デフォルトではBGM API)",
219224
"gameName": "ゲーム名",
220225
"gameIDTips": "ゲームID(BGM IDまたはVNDB IDを入力)",
@@ -259,7 +264,7 @@
259264
"added": "{{title}}を追加しました",
260265
"played": "{{title}}をプレイしました",
261266
"addedAt": "{{time}}に追加",
262-
"playedAtTime": "{{time}}にプレイ",
267+
"playedAtTime": "{{time}}にプレイ",
263268
"duration": "プレイ時間: {{duration}}"
264269
},
265270
"lastPlayed": "最終プレイ: {{time}}",

src/locales/zh_CN.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
"saveError": "BGM Token 保存失败",
4848
"clearToken": "清除令牌"
4949
},
50+
"nsfw": {
51+
"title": "NSFW 设置",
52+
"filter": "过滤 NSFW 内容",
53+
"coverReplace": "NSFW 封面替换"
54+
},
5055
"savePath": {
5156
"title": "游戏存档备份路径",
5257
"pathLabel": "备份根目录路径",
@@ -154,7 +159,7 @@
154159
"inputGameName": "输入游戏名称",
155160
"clearSearch": "清除搜索"
156161
},
157-
"Toolbar": {
162+
"Toolbar": {
158163
"gameLibrary": "游戏仓库",
159164
"openGameFolder": "打开游戏目录",
160165
"deleteGame": "删除游戏",
@@ -259,7 +264,7 @@
259264
"added": "添加了 {{title}}",
260265
"played": "游玩了 {{title}}",
261266
"addedAt": "添加于 {{time}}",
262-
"playedAtTime": "游玩于 {{time}}",
267+
"playedAtTime": "游玩于 {{time}}",
263268
"duration": "游戏时长: {{duration}}"
264269
},
265270
"lastPlayed": "最后游玩: {{time}}",
@@ -285,4 +290,4 @@
285290
"allFiles": "所有文件"
286291
}
287292
}
288-
}
293+
}

src/locales/zh_TW.json

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
"saveError": "BGM Token 儲存失敗",
4848
"clearToken": "清除令牌"
4949
},
50+
"nsfw": {
51+
"title": "NSFW設定",
52+
"filter": "過濾NSFW内容",
53+
"coverReplace": "模糊NSFW替換"
54+
},
5055
"savePath": {
5156
"title": "遊戲存檔備份路徑",
5257
"pathLabel": "備份根目錄路徑",
@@ -154,7 +159,7 @@
154159
"inputGameName": "輸入遊戲名稱",
155160
"clearSearch": "清除搜尋"
156161
},
157-
"Toolbar": {
162+
"Toolbar": {
158163
"gameLibrary": "遊戲倉庫",
159164
"openGameFolder": "開啟遊戲目錄",
160165
"deleteGame": "刪除遊戲",
@@ -170,7 +175,7 @@
170175
"gamePathNotFound": "遊戲路徑未找到",
171176
"launchFailed": "遊戲啟動失敗:",
172177
"playing": "遊戲中..."
173-
},
178+
},
174179
"SortModal": {
175180
"sort": "排序",
176181
"sortMethod": "排序方法:",
@@ -207,14 +212,14 @@
207212
"warning": "警告",
208213
"gameNotFound": "未找到遊戲資料",
209214
"deleteBackupTitle": "刪除備份"
210-
},
215+
},
211216
"AddModal": {
212217
"addGame": "添加遊戲",
213218
"noExecutableSelected": "未選擇可執行程序",
214219
"gameExists": "遊戲已存在",
215220
"selectLauncher": "選擇啟動程序",
216221
"selectExecutable": "請選擇一個可執行程序",
217-
"enableCustomMode": "啟用自定義模式",
222+
"enableCustomMode": "啟用自定義模式",
218223
"enableVNDB": "使用VNDB api(默認使用BGM api)",
219224
"gameName": "遊戲名稱",
220225
"gameIDTips": "遊戲ID(輸入BGM ID或VNDB ID)",
@@ -259,7 +264,7 @@
259264
"added": "添加了 {{title}}",
260265
"played": "遊玩了 {{title}}",
261266
"addedAt": "添加於 {{time}}",
262-
"playedAtTime": "遊玩於 {{time}}",
267+
"playedAtTime": "遊玩於 {{time}}",
263268
"duration": "遊戲時長: {{duration}}"
264269
},
265270
"lastPlayed": "最後遊玩: {{time}}",
@@ -285,4 +290,4 @@
285290
"allFiles": "所有檔案"
286291
}
287292
}
288-
}
293+
}

0 commit comments

Comments
 (0)