Skip to content

Commit 0c92abb

Browse files
authored
Merge pull request #177 from Eroge-Abyss/time-categories
feat: add search bar and total time options
2 parents 9deb467 + 7f88250 commit 0c92abb

15 files changed

Lines changed: 308 additions & 115 deletions

File tree

src-tauri/src/commands/storage.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::services::{
66
stores::{
77
categories::{Categories, CategoriesStore},
88
games::{Game, Games, GamesStore},
9-
settings::{PlaytimeMode, SortOrder, ThemeSettings},
9+
settings::{PlaytimeDisplayMode, PlaytimeMode, SortOrder, ThemeSettings},
1010
},
1111
};
1212
use anyhow::Context;
@@ -377,6 +377,26 @@ pub fn set_playtime_mode(app_handle: AppHandle, to: PlaytimeMode) -> CmdResult<(
377377
Ok(())
378378
}
379379

380+
/// Gets playtime display mode
381+
#[tauri::command]
382+
pub fn get_playtime_display_mode(app_handle: AppHandle) -> CmdResult<PlaytimeDisplayMode> {
383+
Ok(app_handle
384+
.state::<ManagedState>()
385+
.lock()?
386+
.settings
387+
.playtime_display_mode)
388+
}
389+
390+
/// Saves new playtime display mode to disk
391+
#[tauri::command]
392+
pub fn set_playtime_display_mode(app_handle: AppHandle, to: PlaytimeDisplayMode) -> CmdResult<()> {
393+
let state = app_handle.state::<ManagedState>();
394+
let mut lock = state.lock()?;
395+
lock.update_settings(&app_handle, |s| s.playtime_display_mode = to)
396+
.context("Failed to update playtime display mode")?;
397+
Ok(())
398+
}
399+
380400
#[tauri::command]
381401
pub fn get_use_jp_for_title_time(app_handle: AppHandle) -> CmdResult<bool> {
382402
Ok(app_handle

src-tauri/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ pub fn run() {
7979
commands::storage::set_discord_presence_mode,
8080
commands::storage::get_playtime_mode,
8181
commands::storage::set_playtime_mode,
82+
commands::storage::get_playtime_display_mode,
83+
commands::storage::set_playtime_display_mode,
8284
commands::storage::get_sort_order,
8385
commands::storage::set_sort_order,
8486
commands::storage::set_characters,

src-tauri/src/services/stores/settings.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,19 @@ pub enum PlaytimeMode {
3838
ExStatic,
3939
}
4040

41+
#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug)]
42+
#[serde(rename_all = "snake_case")]
43+
pub enum PlaytimeDisplayMode {
44+
#[default]
45+
All,
46+
Filtered,
47+
}
48+
4149
#[derive(Serialize, Deserialize, Debug, Clone)]
4250
pub struct Settings {
4351
pub disable_presence_on_nsfw: bool,
4452
pub playtime_mode: PlaytimeMode,
53+
pub playtime_display_mode: PlaytimeDisplayMode,
4554
pub use_jp_for_title_time: bool,
4655
pub theme_settings: ThemeSettings,
4756
pub sort_order: SortOrder,
@@ -57,6 +66,7 @@ impl Default for Settings {
5766
Self {
5867
disable_presence_on_nsfw: true,
5968
playtime_mode: PlaytimeMode::default(),
69+
playtime_display_mode: PlaytimeDisplayMode::default(),
6070
use_jp_for_title_time: false,
6171
theme_settings: ThemeSettings::default(),
6272
sort_order: SortOrder::default(),

src/lib/components/home/AddManualGame.svelte

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@
7272
});
7373
7474
toast.success('Game saved successfully!');
75+
76+
// Reset state
77+
exe_path = null;
78+
manualTitle = '';
79+
manualAltTitle = '';
80+
manualDescription = '';
81+
manualImagePath = '';
82+
manualIsNsfw = false;
83+
7584
closeModal();
7685
} catch (error) {
7786
console.error('Error saving manual game:', error);

src/lib/components/home/AddVndbGame.svelte

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@
6363
});
6464
6565
toast.success('Game saved successfully!');
66+
67+
// Reset state
68+
exe_path = null;
69+
charactersDownload = false;
70+
vndb.reset();
71+
6672
closeModal();
6773
} catch (error) {
6874
console.error('Error saving game:', error);

src/lib/components/home/FilterAndSort.svelte

Lines changed: 143 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { settingsStore } from '$lib/stores/settings.svelte';
55
import type { SortOrder } from '$lib/types';
66
import { withMenuClose } from '$lib/util';
7+
import { gamesStore } from '$lib/stores/games.svelte';
78
89
let menuToggleRef: HTMLButtonElement;
910
// svelte-ignore non_reactive_update
@@ -84,106 +85,155 @@
8485

8586
<svelte:window onclick={handleClickOutside} />
8687

87-
<div class="filter-sort-container">
88-
<button
89-
onclick={() => (activeMenu = !activeMenu)}
90-
class="menu-toggle"
91-
class:active={activeMenu}
92-
bind:this={menuToggleRef}
93-
>
94-
<i class="fa-solid fa-filter"></i>
95-
Filter & Sort
96-
</button>
97-
98-
{#if activeMenu}
99-
<div
100-
class="secondary-menu"
101-
in:fly={{ y: -10, duration: 200 }}
102-
bind:this={filterSortMenuRef}
88+
<div class="controls-container">
89+
<div class="search-wrapper">
90+
<i class="fa-solid fa-search"></i>
91+
<input
92+
id="game-search-input"
93+
type="text"
94+
placeholder="Search..."
95+
bind:value={gamesStore.searchQuery}
96+
/>
97+
</div>
98+
<div class="filter-sort-container">
99+
<button
100+
onclick={() => (activeMenu = !activeMenu)}
101+
class="menu-toggle"
102+
class:active={activeMenu}
103+
bind:this={menuToggleRef}
103104
>
104-
<h3>Sort By</h3>
105-
<div class="menu-item-with-submenu">
106-
<button
107-
onclick={() => (showSortMenu = !showSortMenu)}
108-
class="menu-item sort-menu-item-toggle"
109-
>
110-
<i class="fa-solid fa-arrow-down-short-wide"></i>
111-
{currentSortOption.charAt(0).toUpperCase() +
112-
currentSortOption.slice(1).replace('_', ' ')}
113-
<i class="fa-solid fa-chevron-right chevron"></i>
114-
</button>
115-
116-
{#if showSortMenu}
117-
<div
118-
class="sort-submenu secondary-menu"
119-
in:fly={{ x: 10, duration: 200 }}
105+
<i class="fa-solid fa-filter"></i>
106+
Filter & Sort
107+
</button>
108+
109+
{#if activeMenu}
110+
<div
111+
class="secondary-menu"
112+
in:fly={{ y: -10, duration: 200 }}
113+
bind:this={filterSortMenuRef}
114+
>
115+
<h3>Sort By</h3>
116+
<div class="menu-item-with-submenu">
117+
<button
118+
onclick={() => (showSortMenu = !showSortMenu)}
119+
class="menu-item sort-menu-item-toggle"
120120
>
121-
<button
122-
onclick={withMenuClose(
123-
() => setSortOrder('title'),
124-
() => (showSortMenu = false),
125-
)}
126-
class="menu-item"
127-
class:active={currentSortOption === 'title'}
128-
>
129-
Title
130-
</button>
131-
<button
132-
onclick={withMenuClose(
133-
() => setSortOrder('last_played'),
134-
() => (showSortMenu = false),
135-
)}
136-
class="menu-item"
137-
class:active={currentSortOption === 'last_played'}
138-
>
139-
Last Played
140-
</button>
141-
<button
142-
onclick={withMenuClose(
143-
() => setSortOrder('playtime'),
144-
() => (showSortMenu = false),
145-
)}
146-
class="menu-item"
147-
class:active={currentSortOption === 'playtime'}
121+
<i class="fa-solid fa-arrow-down-short-wide"></i>
122+
{currentSortOption.charAt(0).toUpperCase() +
123+
currentSortOption.slice(1).replace('_', ' ')}
124+
<i class="fa-solid fa-chevron-right chevron"></i>
125+
</button>
126+
127+
{#if showSortMenu}
128+
<div
129+
class="sort-submenu secondary-menu"
130+
in:fly={{ x: 10, duration: 200 }}
148131
>
149-
Playtime
150-
</button>
151-
</div>
152-
{/if}
153-
</div>
132+
<button
133+
onclick={withMenuClose(
134+
() => setSortOrder('title'),
135+
() => (showSortMenu = false),
136+
)}
137+
class="menu-item"
138+
class:active={currentSortOption === 'title'}
139+
>
140+
Title
141+
</button>
142+
<button
143+
onclick={withMenuClose(
144+
() => setSortOrder('last_played'),
145+
() => (showSortMenu = false),
146+
)}
147+
class="menu-item"
148+
class:active={currentSortOption === 'last_played'}
149+
>
150+
Last Played
151+
</button>
152+
<button
153+
onclick={withMenuClose(
154+
() => setSortOrder('playtime'),
155+
() => (showSortMenu = false),
156+
)}
157+
class="menu-item"
158+
class:active={currentSortOption === 'playtime'}
159+
>
160+
Playtime
161+
</button>
162+
</div>
163+
{/if}
164+
</div>
165+
166+
<div class="menu-divider"></div>
154167

155-
<div class="menu-divider"></div>
156-
157-
<h3>Filter By</h3>
158-
<div class="menu-item-with-submenu">
159-
<button
160-
onclick={() => (showStatusMenu = !showStatusMenu)}
161-
class="menu-item status-menu-item-toggle"
162-
>
163-
<i class="fa-solid fa-tags"></i>
164-
Status
165-
<i class="fa-solid fa-chevron-right chevron"></i>
166-
</button>
167-
168-
{#if showStatusMenu}
169-
<div
170-
class="status-submenu secondary-menu"
171-
in:fly={{ x: 10, duration: 200 }}
168+
<h3>Filter By</h3>
169+
<div class="menu-item-with-submenu">
170+
<button
171+
onclick={() => (showStatusMenu = !showStatusMenu)}
172+
class="menu-item status-menu-item-toggle"
172173
>
173-
<StatusSelector
174-
categories={settingsStore.selectedCategories}
175-
{toggleStatus}
176-
{clearStatuses}
177-
showUncategorized
178-
/>
179-
</div>
180-
{/if}
174+
<i class="fa-solid fa-tags"></i>
175+
Status
176+
<i class="fa-solid fa-chevron-right chevron"></i>
177+
</button>
178+
179+
{#if showStatusMenu}
180+
<div
181+
class="status-submenu secondary-menu"
182+
in:fly={{ x: 10, duration: 200 }}
183+
>
184+
<StatusSelector
185+
categories={settingsStore.selectedCategories}
186+
{toggleStatus}
187+
{clearStatuses}
188+
showUncategorized
189+
/>
190+
</div>
191+
{/if}
192+
</div>
181193
</div>
182-
</div>
183-
{/if}
194+
{/if}
195+
</div>
184196
</div>
185197

186198
<style>
199+
.controls-container {
200+
display: flex;
201+
align-items: center;
202+
gap: 0.5rem;
203+
}
204+
205+
.search-wrapper {
206+
position: relative;
207+
}
208+
209+
.search-wrapper i {
210+
position: absolute;
211+
left: 0.75rem;
212+
top: 50%;
213+
transform: translateY(-50%);
214+
color: var(--main-text);
215+
opacity: 0.6;
216+
pointer-events: none;
217+
}
218+
219+
.search-wrapper input {
220+
background: var(--accent);
221+
border: none;
222+
border-radius: var(--small-radius);
223+
padding: 0.5rem 0.75rem 0.5rem 2.25rem;
224+
font-size: 16px;
225+
color: var(--main-text);
226+
transition: all 0.3s ease;
227+
width: 180px;
228+
height: 40px;
229+
box-sizing: border-box;
230+
}
231+
232+
.search-wrapper input:focus {
233+
outline: 1px solid var(--secondary);
234+
background: color-mix(in srgb, var(--accent), var(--main-text) 5%);
235+
}
236+
187237
.filter-sort-container {
188238
position: relative;
189239
display: inline-block;
@@ -202,6 +252,8 @@
202252
align-items: center;
203253
gap: 0.5rem;
204254
white-space: nowrap;
255+
height: 40px;
256+
box-sizing: border-box;
205257
}
206258
207259
.menu-toggle:hover {

0 commit comments

Comments
 (0)