diff --git a/src/back/responses.ts b/src/back/responses.ts index 31a58129d..f1a027ab8 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -1138,10 +1138,9 @@ export function registerRequestCallbacks(state: BackState, init: () => Promise { + state.socketServer.register(BackIn.GET_TAGS, async (event, tagFilters) => { const flatFilters: string[] = tagFilters ? tagFilters.reduce((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; }); diff --git a/src/renderer/components/SearchBar.tsx b/src/renderer/components/SearchBar.tsx index ce1a0845c..0cee8631c 100644 --- a/src/renderer/components/SearchBar.tsx +++ b/src/renderer/components/SearchBar.tsx @@ -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([]); + + 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) => { dispatch(setSearchText({ @@ -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, @@ -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); @@ -147,8 +164,27 @@ export function SearchBar() { ); }; + const tagLabelRenderer = (item: TagSelectItem) => { + const category = tagCategories.find(t => t.name === item.tag.category); + + return ( +
+
+ +
+
+ {item.tag.name} +
+
+ ); + }; + return ( -
+
@@ -184,47 +220,50 @@ export function SearchBar() { {/* value={expanded ? 'Hide Filters' : 'Show Filters'} */} {/* onClick={() => setExpanded(!expanded)}/> */}
- { expanded && - ( -
- - { view.selectedPlaylist && ( - - )} - { window.Shared.preferences.data.useCustomViews && ( - { - return strings.libraries[item] || item; - }}/> - )} - - -
- ) - } +
+ + { view.selectedPlaylist && ( + + )} + { window.Shared.preferences.data.useCustomViews && ( + { + return strings.libraries[item] || item; + }}/> + )} + + + +
); } @@ -285,6 +324,10 @@ type SearchableSelectItem = { orderVal: string; } +type TagSelectItem = { + tag: Tag; +} & SearchableSelectItem; + function SearchableSelect(props: SearchableSelectProps) { const { title, items, selected, onToggle, onClear, mapName, labelRenderer } = props; const [expanded, setExpanded] = React.useState(false); diff --git a/src/renderer/components/pages/TagsPage.tsx b/src/renderer/components/pages/TagsPage.tsx index 56a87b7e4..5478f68a8 100644 --- a/src/renderer/components/pages/TagsPage.tsx +++ b/src/renderer/components/pages/TagsPage.tsx @@ -44,7 +44,7 @@ export class TagsPage extends React.Component { } 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); } }); @@ -113,8 +113,8 @@ export class TagsPage extends React.Component { onEditTag = (tag: Partial) => { 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 }); } }; diff --git a/src/renderer/store/search/slice.ts b/src/renderer/store/search/slice.ts index cb982ec97..7577cc3e0 100644 --- a/src/renderer/store/search/slice.ts +++ b/src/renderer/store/search/slice.ts @@ -479,7 +479,6 @@ const searchSlice = createSlice({ }, addData(state: SearchState, { payload }: PayloadAction) { 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}`); diff --git a/src/shared/back/types.ts b/src/shared/back/types.ts index 244b1d422..ee2b79455 100644 --- a/src/shared/back/types.ts +++ b/src/shared/back/types.ts @@ -348,7 +348,7 @@ export type BackInTemplate = SocketTemplate 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; diff --git a/src/shared/search/util.ts b/src/shared/search/util.ts index 0f66b85ca..0eb14da16 100644 --- a/src/shared/search/util.ts +++ b/src/shared/search/util.ts @@ -25,6 +25,7 @@ export function getDefaultAdvancedFilter(library?: string): AdvancedFilter { library: library ? [library] : [], playMode: [], platform: [], + tags: [], }; } @@ -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 ); } @@ -89,6 +91,7 @@ export function parseAdvancedFilter(advFilter: AdvancedFilter): GameFilter { exactFunc('library', 'library'); exactFunc('platform', 'platforms'); nonExactFunc('playMode', 'playMode'); + exactFunc('tags', 'tags'); return filter; } diff --git a/typings/flashpoint-launcher.d.ts b/typings/flashpoint-launcher.d.ts index bf662e28e..a4c412ea1 100644 --- a/typings/flashpoint-launcher.d.ts +++ b/typings/flashpoint-launcher.d.ts @@ -1090,6 +1090,7 @@ declare module 'flashpoint-launcher' { library: string[]; playMode: string[]; platform: string[]; + tags: string[]; } enum ScreenshotPreviewMode {