Skip to content

Commit

Permalink
feat: Tags advanced filter
Browse files Browse the repository at this point in the history
  • Loading branch information
colin969 committed Aug 1, 2024
1 parent 1733ddc commit 053e153
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 55 deletions.
3 changes: 1 addition & 2 deletions src/back/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1138,10 +1138,9 @@ export function registerRequestCallbacks(state: BackState, init: () => Promise<v
return platform;
});

state.socketServer.register(BackIn.GET_TAGS, async (event, name, tagFilters) => {
state.socketServer.register(BackIn.GET_TAGS, async (event, tagFilters) => {
const flatFilters: string[] = tagFilters ? tagFilters.reduce<string[]>((prev, cur) => prev.concat(cur.tags), []) : [];
const tags = (await fpDatabase.findAllTags()).filter(t => !t.aliases.some(a => flatFilters.includes(a)));
state.socketServer.send(event.client, BackOut.GET_TAGS, tags);
return tags;
});

Expand Down
137 changes: 90 additions & 47 deletions src/renderer/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,29 @@ import * as React from 'react';
import { useDispatch } from 'react-redux';
import { GameOrder } from './GameOrder';
import { OpenIcon } from './OpenIcon';
import { SimpleButton } from './SimpleButton';
import { useView } from '@renderer/hooks/search';
import { forceSearch, setAdvancedFilter, setOrderBy, setOrderReverse, setSearchText } from '@renderer/store/search/slice';
import { ArrowKeyStepper, AutoSizer, List, ListRowProps } from 'react-virtualized-reactv17';
import { AdvancedFilter } from 'flashpoint-launcher';
import { useContext, useMemo } from 'react';
import { AdvancedFilter, Tag } from 'flashpoint-launcher';
import { useContext, useMemo, useState } from 'react';
import { LangContext } from '@renderer/util/lang';
import { useAppSelector } from '@renderer/hooks/useAppSelector';
import { getPlatformIconURL } from '@renderer/Util';
import { BackIn } from '@shared/back/types';

export function SearchBar() {
const view = useView();
const dispatch = useDispatch();
const [expanded, setExpanded] = React.useState(true);
const strings = useContext(LangContext);
const mainState = useAppSelector((state) => state.main);
const { main: mainState, tagCategories } = useAppSelector((state) => state);
const [tags, setTags] = useState<Tag[]>([]);

React.useEffect(() => {
window.Shared.back.request(BackIn.GET_TAGS, window.Shared.preferences.data.tagFilters.filter(tfg => tfg.enabled || (tfg.extreme && !window.Shared.preferences.data.browsePageShowExtreme)))
.then((tags) => {
setTags(tags);
});
}, [window.Shared.preferences.data.tagFilters, window.Shared.preferences.data.browsePageShowExtreme]);

const onTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
dispatch(setSearchText({
Expand Down Expand Up @@ -121,6 +128,9 @@ export function SearchBar() {
const onTogglePlatform = onToggleFactory('platform');
const onClearPlatform = onClearFactory('platform');

const onToggleTag = onToggleFactory('tags');
const onClearTags = onClearFactory('tags');

const simpleSelectItems = (values: string[]): SearchableSelectItem[] => {
return values.map(v => ({
value: v,
Expand All @@ -131,6 +141,13 @@ export function SearchBar() {
const libraryItems = useMemo(() => simpleSelectItems(mainState.libraries), [mainState.libraries]);
const playModeItems = useMemo(() => simpleSelectItems(mainState.suggestions.playMode), [mainState.suggestions.playMode]);
const platformItems = useMemo(() => simpleSelectItems(mainState.suggestions.platforms), [mainState.suggestions.platforms]);
const tagItems = useMemo((): TagSelectItem[] => {
return tags.map(tag => ({
value: tag.name,
orderVal: `${tag.category} ${tag.name} ${tag.aliases.join((' '))}`,
tag: tag,
}));
}, [tags]);

const platformLabelRenderer = (item: SearchableSelectItem) => {
const platformIcon = getPlatformIconURL(item.value, mainState.logoVersion);
Expand All @@ -147,8 +164,27 @@ export function SearchBar() {
);
};

const tagLabelRenderer = (item: TagSelectItem) => {
const category = tagCategories.find(t => t.name === item.tag.category);

return (
<div className='platform-label-row'>
<div
className="dropdown-icon dropdown-icon-image">
<OpenIcon
className='curate-tag__icon'
color={category ? category.color : '#FFFFFF'}
icon='tag'/>
</div>
<div className="searchable-select-dropdown-item-title">
{item.tag.name}
</div>
</div>
);
};

return (
<div className={`search-bar-wrapper ${expanded ? 'search-bar-wrapper--expanded-simple' : ''}`}>
<div className='search-bar-wrapper search-bar-wrapper--expanded-simple'>
<div className="search-bar">
<div className="search-bar-icon">
<OpenIcon icon='magnifying-glass'/>
Expand Down Expand Up @@ -184,47 +220,50 @@ export function SearchBar() {
{/* value={expanded ? 'Hide Filters' : 'Show Filters'} */}
{/* onClick={() => setExpanded(!expanded)}/> */}
</div>
{ expanded &&
(
<div className='search-bar-expansion search-bar-expansion-simple'>
<ThreeStateCheckbox
title={strings.browse.installed}
value={view.advancedFilter.installed}
onChange={onInstalledChange}/>
{ view.selectedPlaylist && (
<ThreeStateCheckbox
title={strings.browse.usePlaylistOrder}
value={view.advancedFilter.playlistOrder}
twoState={true}
onChange={onPlaylistOrderChange}/>
)}
{ window.Shared.preferences.data.useCustomViews && (
<SearchableSelect
title={strings.browse.library}
items={libraryItems}
selected={view.advancedFilter.library}
onToggle={onToggleLibrary}
onClear={onClearLibraries}
mapName={(item) => {
return strings.libraries[item] || item;
}}/>
)}
<SearchableSelect
title={strings.browse.playMode}
items={playModeItems}
selected={view.advancedFilter.playMode}
onToggle={onTogglePlayMode}
onClear={onClearPlayMode} />
<SearchableSelect
title={strings.browse.platform}
items={platformItems}
labelRenderer={platformLabelRenderer}
selected={view.advancedFilter.platform}
onToggle={onTogglePlatform}
onClear={onClearPlatform} />
</div>
)
}
<div className='search-bar-expansion search-bar-expansion-simple'>
<ThreeStateCheckbox
title={strings.browse.installed}
value={view.advancedFilter.installed}
onChange={onInstalledChange}/>
{ view.selectedPlaylist && (
<ThreeStateCheckbox
title={strings.browse.usePlaylistOrder}
value={view.advancedFilter.playlistOrder}
twoState={true}
onChange={onPlaylistOrderChange}/>
)}
{ window.Shared.preferences.data.useCustomViews && (
<SearchableSelect
title={strings.browse.library}
items={libraryItems}
selected={view.advancedFilter.library}
onToggle={onToggleLibrary}
onClear={onClearLibraries}
mapName={(item) => {
return strings.libraries[item] || item;
}}/>
)}
<SearchableSelect
title={strings.browse.playMode}
items={playModeItems}
selected={view.advancedFilter.playMode}
onToggle={onTogglePlayMode}
onClear={onClearPlayMode} />
<SearchableSelect
title={strings.browse.platform}
items={platformItems}
labelRenderer={platformLabelRenderer}
selected={view.advancedFilter.platform}
onToggle={onTogglePlatform}
onClear={onClearPlatform} />
<SearchableSelect
title={strings.browse.tags}
items={tagItems}
labelRenderer={tagLabelRenderer}
selected={view.advancedFilter.tags}
onToggle={onToggleTag}
onClear={onClearTags} />
</div>
</div>
);
}
Expand Down Expand Up @@ -285,6 +324,10 @@ type SearchableSelectItem = {
orderVal: string;
}

type TagSelectItem = {
tag: Tag;
} & SearchableSelectItem;

function SearchableSelect<T extends SearchableSelectItem>(props: SearchableSelectProps<T>) {
const { title, items, selected, onToggle, onClear, mapName, labelRenderer } = props;
const [expanded, setExpanded] = React.useState(false);
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/components/pages/TagsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class TagsPage extends React.Component<TagsPageProps, TagsPageState> {
}

componentDidMount() {
window.Shared.back.request(BackIn.GET_TAGS, '', this.props.preferencesData.tagFilters.filter(tfg => tfg.enabled || (tfg.extreme && !this.props.preferencesData.browsePageShowExtreme)))
window.Shared.back.request(BackIn.GET_TAGS, this.props.preferencesData.tagFilters.filter(tfg => tfg.enabled || (tfg.extreme && !this.props.preferencesData.browsePageShowExtreme)))
.then((data) => {
if (data) { this.onTagsChange(data); }
});
Expand Down Expand Up @@ -113,8 +113,8 @@ export class TagsPage extends React.Component<TagsPageProps, TagsPageState> {

onEditTag = (tag: Partial<Tag>) => {
if (this.state.currentTag) {
const newTag = {...deepCopy(this.state.currentTag), ...tag};
this.setState({currentTag: newTag});
const newTag = { ...deepCopy(this.state.currentTag), ...tag };
this.setState({ currentTag: newTag });
}
};

Expand Down
1 change: 0 additions & 1 deletion src/renderer/store/search/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,6 @@ const searchSlice = createSlice({
},
addData(state: SearchState, { payload }: PayloadAction<SearchAddDataAction>) {
const data = payload.data;
console.log(payload);
const view = state.views[payload.view];
if (view) {
log.debug('Search', `ADD DATA - Cur: ${view.data.searchId} Recv: ${payload.data.searchId}`);
Expand Down
2 changes: 1 addition & 1 deletion src/shared/back/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ export type BackInTemplate = SocketTemplate<BackIn, {
[BackIn.GET_PLATFORM_SUGGESTIONS]: (data: string) => TagSuggestion[];
[BackIn.GET_TAG_BY_ID]: (data: number) => Tag | null;
[BackIn.GET_PLATFORM_BY_ID]: (data: number) => Platform | null;
[BackIn.GET_TAGS]: (data: string, tagFilters?: TagFilterGroup[]) => Tag[];
[BackIn.GET_TAGS]: (tagFilters?: TagFilterGroup[]) => Tag[];
[BackIn.GET_TAG]: (data: string) => Tag | null;
[BackIn.SAVE_TAG]: (data: Tag) => Tag;
[BackIn.DELETE_TAG]: (name: string) => void;
Expand Down
5 changes: 4 additions & 1 deletion src/shared/search/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function getDefaultAdvancedFilter(library?: string): AdvancedFilter {
library: library ? [library] : [],
playMode: [],
platform: [],
tags: [],
};
}

Expand All @@ -48,7 +49,8 @@ export function isAdvFilterEmpty(advFilter: AdvancedFilter): boolean {
advFilter.installed === undefined &&
advFilter.library.length === 0 &&
advFilter.playMode.length === 0 &&
advFilter.platform.length === 0
advFilter.platform.length === 0 &&
advFilter.tags.length === 0
);
}

Expand Down Expand Up @@ -89,6 +91,7 @@ export function parseAdvancedFilter(advFilter: AdvancedFilter): GameFilter {
exactFunc('library', 'library');
exactFunc('platform', 'platforms');
nonExactFunc('playMode', 'playMode');
exactFunc('tags', 'tags');

return filter;
}
Expand Down
1 change: 1 addition & 0 deletions typings/flashpoint-launcher.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,7 @@ declare module 'flashpoint-launcher' {
library: string[];
playMode: string[];
platform: string[];
tags: string[];
}

enum ScreenshotPreviewMode {
Expand Down

0 comments on commit 053e153

Please sign in to comment.