Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add custom user-defined game categories #1428 #3115

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
82ef59c
feat: created basic POC of this feature
lgcavalheiro Oct 4, 2023
bda5e12
chore: merge with main
lgcavalheiro Oct 4, 2023
b4d82d0
feat: added dropdown selector for custom categories
lgcavalheiro Oct 5, 2023
b6eb420
feat: added translations
lgcavalheiro Oct 5, 2023
dfa1582
feat: added translations
lgcavalheiro Oct 5, 2023
a1e0c05
fix: fixed issues raised during review + added uncategorized preset
lgcavalheiro Oct 7, 2023
1459ce7
chore: pushing en translation files
lgcavalheiro Oct 7, 2023
33d30fa
chore: rebase against upstream/main + conflict resolution
lgcavalheiro Nov 9, 2023
a35d9ea
fix: runner is now relevant + added validation for new categories
lgcavalheiro Nov 10, 2023
eeb61bb
fix: changed category settings layout to favor checkboxes
lgcavalheiro Nov 12, 2023
9a97fec
Group library filters in a dropdown. Add only installed filter
arielj Nov 25, 2023
ef463b5
Favorites filter composable. Refresh libraries in background
arielj Nov 25, 2023
398c2f8
Re-add translation strings
arielj Nov 25, 2023
cb452a8
Fix checked status for store filters
arielj Nov 25, 2023
3eda2ee
Remove console.log
arielj Nov 25, 2023
ba930a2
fix: category filter on small screens
lgcavalheiro Nov 26, 2023
86d1dd4
Fix missing windows games when no platform is selected
arielj Nov 28, 2023
1b0f265
fix: merge with upstream/main + added a gap between catagories select…
lgcavalheiro Nov 29, 2023
cbf185a
fix: i18n errors on push
lgcavalheiro Nov 29, 2023
637ce96
chore: more merges with upstream/main
lgcavalheiro Nov 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/locales/en/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@
"disableEosOverlay": "Disable EOS Overlay",
"enableEosOverlay": "Enable EOS Overlay",
"logs": "Logs",
"manageCategories": "Manage Categories",
"move": "Move Game",
"protondb": "Check Compatibility",
"removeFromSteam": "Remove from Steam",
Expand Down
9 changes: 9 additions & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@
"go_to_library": "Go to Library",
"login": "Log in"
},
"Categories": "Categories",
"category-settings": {
"add-new-category": "Add New Category",
"assigned-categories": "Assigned Categories",
"available-categories": "Available Categories",
"new-category": "New Category",
"no-assigned-categories": "No categories have been assigned to this game"
},
"controller": {
"hints": {
"back": "Back",
Expand Down Expand Up @@ -242,6 +250,7 @@
"GOG": "GOG",
"gog-store": "GOG Store",
"header": {
"all_categories": "All Categories",
"hide_non_available_games": "Hide non-available games",
"ignore_hidden": "Ignore Hidden",
"platform": "Platform",
Expand Down
3 changes: 2 additions & 1 deletion public/locales/es/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@
"store": "Página de la tienda",
"verify": "Verificar y reparar",
"extraInfo": "Información extra",
"logs": "Registros"
"logs": "Registros",
"manageCategories": "Administrar Categorías"
arielj marked this conversation as resolved.
Show resolved Hide resolved
},
"help": {
"cloud_save_unsupported": "Este juego no admite guardados en la nube. Esta información es proporcionada por los desarrolladores del juego. Algunos juegos implementan su propio sistema de guardado en la nube"
Expand Down
14 changes: 12 additions & 2 deletions public/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@
"show_available_games": "Mostrar juegos no disponibles",
"show_favourites_only": "Mostrar sólo favoritos",
"show_hidden": "Mostrar Ocultos",
"store": "Filtrar tienda"
"store": "Filtrar tienda",
"all_categories": "Todas las categorias"
},
"help": {
"amdfsr": "El FSR de AMD ayuda a aumentar la velocidad de los fotogramas mediante el escalado de las resoluciones más bajas en el modo de pantalla completa. La calidad de la imagen aumenta de 5 a 1 a costa de un ligero impacto en el rendimiento. Su activación puede mejorar el rendimiento.",
Expand Down Expand Up @@ -740,5 +741,14 @@
},
"prime-gaming": "Prime Gaming",
"amazon": "Amazon",
"Amazon Games": "Juegos de Amazon"
"Amazon Games": "Juegos de Amazon",
"Categories": "Categorias",
"category-settings": {
"assigned-categories": "Categorías Asignadas",
"available-categories": "Categorías Disponibles",
"new-category": "Nueva Categoría",
"add-new-category": "Añadir Nueva Categoría",
"no-assigned-categories": "No se han asignado categorías a este juego"

}
}
3 changes: 2 additions & 1 deletion public/locales/pt/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@
"store": "Página da Loja",
"verify": "Verificar e Reparar",
"extraInfo": "Informação extra",
"logs": "Histórico"
"logs": "Histórico",
"manageCategories": "Gerenciar Categorias"
},
"help": {
"cloud_save_unsupported": "Este jogo não suporta salvamentos na nuvem. Esta informação é fornecida pelos desenvolvedores do jogo. Alguns jogos implementam seu próprio sistema de salvamento na nuvem"
Expand Down
13 changes: 11 additions & 2 deletions public/locales/pt/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@
"show_available_games": "Show non-Available games",
"show_favourites_only": "Show Favourites only",
"show_hidden": "Show Hidden",
"store": "Filter Store"
"store": "Filter Store",
"all_categories": "Todas as categorias"
},
"help": {
"amdfsr": "AMD's FSR helps boost framerate by upscaling lower resolutions in Fullscreen Mode. Image quality increases from 5 to 1 at the cost of a slight performance hit. Enabling may improve performance.",
Expand Down Expand Up @@ -703,5 +704,13 @@
},
"amazon": "Amazonas",
"Amazon Games": "Amazon Games",
"prime-gaming": "Prime Gaming"
"prime-gaming": "Prime Gaming",
"Categories": "Categorias",
"category-settings": {
"assigned-categories": "Categorias Atribuídas",
"available-categories": "Categorias Disponíveis",
"new-category": "Nova Categoria",
"add-new-category": "Adicionar Nova Categoria",
"no-assigned-categories": "Nenhuma categoria atribuída a este jogo"
}
}
3 changes: 2 additions & 1 deletion public/locales/pt_BR/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@
"store": "Página na Loja",
"verify": "Verificar e Reparar",
"extraInfo": "Informação extra",
"logs": "Histórico"
"logs": "Histórico",
"manageCategories": "Gerenciar Categorias"
},
"help": {
"cloud_save_unsupported": "Este jogo não suporta salvamentos na nuvem. Esta informação é fornecida pelos desenvolvedores do jogo. Alguns jogos implementam seu próprio sistema de salvamento na nuvem"
Expand Down
13 changes: 11 additions & 2 deletions public/locales/pt_BR/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@
"show_available_games": "Mostrar jogos indisponíveis",
"show_favourites_only": "Mostrar apenas favoritos",
"show_hidden": "Mostrar escondidos",
"store": "Filtrar loja"
"store": "Filtrar loja",
"all_categories": "Todas as categorias"
},
"help": {
"amdfsr": "O FSR da AMD ajuda a aumentar a taxa de quadros aumentando as resoluções mais baixas no modo de tela cheia. A qualidade da imagem aumenta de 5 para 1 ao custo de um pequeno impacto no desempenho. A ativação pode melhorar o desempenho.",
Expand Down Expand Up @@ -740,5 +741,13 @@
},
"amazon": "Amazonas",
"Amazon Games": "Amazon Games",
"prime-gaming": "Prime Gaming ignore-same"
"prime-gaming": "Prime Gaming ignore-same",
"Categories": "Categorias",
"category-settings": {
"assigned-categories": "Categorias Atribuídas",
"available-categories": "Categorias Disponíveis",
"new-category": "Nova Categoria",
"add-new-category": "Adicionar Nova Categoria",
"no-assigned-categories": "Nenhuma categoria atribuída a este jogo"
}
}
1 change: 1 addition & 0 deletions src/common/types/electron_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface StoreStructure {
recent: RecentGame[]
hidden: HiddenGame[]
favourites: FavouriteGame[]
customCategories: Record<string, string[]>
}
theme: string
zoomPercent: number
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/components/UI/StoreFilter/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#custom-category-selector {
width: 256px;
}
30 changes: 28 additions & 2 deletions src/frontend/components/UI/StoreFilter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@ import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import FormControl from 'frontend/components/UI/FormControl'
import ContextProvider from 'frontend/state/ContextProvider'
import SelectField from '../SelectField'
import './index.css'

export default React.memo(function StoreFilter() {
const { category, handleCategory, gog, epic, amazon } =
useContext(ContextProvider)
const {
category,
handleCategory,
gog,
epic,
amazon,
customCategories,
currentCustomCategory,
setCurrentCustomCategory
} = useContext(ContextProvider)
const { t } = useTranslation()

const isGOGLoggedin = gog.username
Expand All @@ -16,6 +26,22 @@ export default React.memo(function StoreFilter() {
return (
<div className="storeFilter">
<FormControl segmented small>
<SelectField
htmlId="custom-category-selector"
value={currentCustomCategory || ''}
onChange={(e) => {
setCurrentCustomCategory(e.target.value)
}}
>
<option value="">
{t('header.all_categories', 'All Categories')}
</option>
{customCategories.listCategories().map((category) => (
<option value={category} key={category}>
{category}
</option>
))}
</SelectField>
<button
onClick={() => handleCategory('all')}
className={classNames('FormControl__button', {
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/screens/Game/GamePage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ export default React.memo(function GamePage(): JSX.Element | null {
platform,
showDialogModal,
isSettingsModalOpen,
experimentalFeatures,
connectivity
connectivity,
experimentalFeatures
} = useContext(ContextProvider)

const [gameInfo, setGameInfo] = useState(locationGameInfo)
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/screens/Library/components/GameCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,11 @@ const GameCard = ({
onclick: () => favouriteGames.add(appName, title),
show: !isFavouriteGame
},
{
label: t('submenu.manageCategories', 'Manage Categories'),
arielj marked this conversation as resolved.
Show resolved Hide resolved
onclick: () => setIsSettingsModalOpen(true, 'category', gameInfo),
show: true
},
{
label: t('button.remove_from_favourites', 'Remove From Favourites'),
onclick: () => favouriteGames.remove(appName),
Expand Down
41 changes: 23 additions & 18 deletions src/frontend/screens/Library/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ export default React.memo(function Library(): JSX.Element {
showHidden,
handleCategory,
showFavourites: showFavouritesLibrary,
showNonAvailable
showNonAvailable,
currentCustomCategory,
customCategories
} = useContext(ContextProvider)

const [showModal, setShowModal] = useState<ModalState>({
Expand Down Expand Up @@ -234,31 +236,34 @@ export default React.memo(function Library(): JSX.Element {
return tempArray
}, [showFavourites, favouriteGames, epic, gog])

const makeLibrary = () => {
const isEpic = epic.username && epicCategories.includes(category)
const isGog = gog.username && gogCategories.includes(category)
const isAmazon = amazon.user_id && amazonCategories.includes(category)
const epicLibrary = isEpic ? epic.library : []
const gogLibrary = isGog ? gog.library : []
const sideloadedApps = sideloadedCategories.includes(category)
? sideloadedLibrary
: []
const amazonLibrary = isAmazon ? amazon.library : []

return [...sideloadedApps, ...epicLibrary, ...gogLibrary, ...amazonLibrary]
}

// select library
const libraryToShow = useMemo(() => {
let library: Array<GameInfo> = []
if (showFavouritesLibrary) {
library = [...favourites].filter((game) =>
category === 'all' ? game : game?.runner === category
)
} else if (currentCustomCategory && currentCustomCategory.length > 0) {
const gamesInCustomCategory = customCategories.list[currentCustomCategory]
library = makeLibrary().filter((game) =>
gamesInCustomCategory.includes(game.app_name)
)
} else {
const isEpic = epic.username && epicCategories.includes(category)
const isGog = gog.username && gogCategories.includes(category)
const isAmazon = amazon.user_id && amazonCategories.includes(category)
const epicLibrary = isEpic ? epic.library : []
const gogLibrary = isGog ? gog.library : []
const sideloadedApps = sideloadedCategories.includes(category)
? sideloadedLibrary
: []
const amazonLibrary = isAmazon ? amazon.library : []

library = [
...sideloadedApps,
...epicLibrary,
...gogLibrary,
...amazonLibrary
]

library = makeLibrary()
if (!showNonAvailable) {
const nonAvailbleGames = storage.getItem('nonAvailableGames') || '[]'
const nonAvailbleGamesArray = JSON.parse(nonAvailbleGames)
Expand Down
25 changes: 17 additions & 8 deletions src/frontend/screens/Settings/components/SettingsModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext } from 'react'
import React, { useContext, useMemo } from 'react'
import { GameInfo } from 'common/types'
import {
Dialog,
Expand All @@ -13,10 +13,11 @@ import LogSettings from '../../sections/LogSettings'
import './index.scss'
import { useTranslation } from 'react-i18next'
import { SettingsContextType } from 'frontend/types'
import CategorySettings from '../../sections/CategorySettings'

type Props = {
gameInfo: GameInfo
type: 'settings' | 'log'
type: 'settings' | 'log' | 'category'
}

function SettingsModal({ gameInfo, type }: Props) {
Expand All @@ -32,6 +33,16 @@ function SettingsModal({ gameInfo, type }: Props) {
runner
})

const titleType = useMemo(() => {
const titleTypeLiterals = {
settings: t('Settings', 'Settings'),
log: t('settings.navbar.log', 'Log'),
category: t('Categories', 'Categories')
}

return titleTypeLiterals[type]
}, [type])

if (!contextValues) {
return null
}
Expand All @@ -43,15 +54,13 @@ function SettingsModal({ gameInfo, type }: Props) {
className={'InstallModal__dialog'}
>
<DialogHeader onClose={() => setIsSettingsModalOpen(false)}>
{`${title} (${
type === 'settings'
? t('Settings', 'Settings')
: t('settings.navbar.log', 'Log')
})`}
{`${title} (${titleType})`}
</DialogHeader>
<DialogContent className="settingsDialogContent">
<SettingsContext.Provider value={contextValues}>
{type === 'settings' ? <GamesSettings useDetails /> : <LogSettings />}
{type === 'settings' && <GamesSettings useDetails />}
{type === 'log' && <LogSettings />}
{type === 'category' && <CategorySettings />}
</SettingsContext.Provider>
</DialogContent>
</Dialog>
Expand Down
Loading