Skip to content

Commit

Permalink
A lot of rendering refactors related to libraries (and a few extras) (#…
Browse files Browse the repository at this point in the history
…1267)

* A lot of rendering refactors related to libraries

* Fixes for hidden games

* Fix games count when ignoring hidden games

* Remove unneeded variables destructuring
  • Loading branch information
arielj authored May 17, 2022
1 parent 1150f33 commit b75e515
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 213 deletions.
1 change: 0 additions & 1 deletion electron/legendary/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ export class LegendaryLibrary {

if (fullRefresh) {
try {
logInfo('Refreshing Epic Games...', LogPrefix.Legendary)
await this.refresh()
} catch (error) {
logError(`${error}`, LogPrefix.Legendary)
Expand Down
24 changes: 16 additions & 8 deletions src/components/UI/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { faApple, faLinux, faWindows } from '@fortawesome/free-brands-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import React, { useContext, useMemo } from 'react'
import cx from 'classnames'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { SearchBar } from 'src/components/UI'
import { UE_VERSIONS } from './constants'
import { GameInfo } from 'src/types'
import ContextProvider from 'src/state/ContextProvider'
import { SearchBar } from 'src/components/UI'
import FormControl from '../FormControl'
import { UE_VERSIONS } from './constants'
import { faApple, faLinux, faWindows } from '@fortawesome/free-brands-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { Visibility, VisibilityOff } from '@mui/icons-material'
import './index.css'

export interface HeaderProps {
numberOfGames: number
interface ComponentProps {
list: GameInfo[]
}

export default function Header({ numberOfGames }: HeaderProps) {
export default function Header({ list }: ComponentProps) {
const { t } = useTranslation()
const {
category,
Expand All @@ -38,6 +39,13 @@ export default function Header({ numberOfGames }: HeaderProps) {
? t('header.ignore_hidden', 'Ignore Hidden')
: t('header.show_hidden', 'Show Hidden')

const numberOfGames = useMemo(() => {
const dlcCount =
category === 'epic' ? list.filter((lib) => lib.install.is_dlc).length : 0

return list.length - dlcCount
}, [list, category])

return (
<div className="Header">
{category !== 'unreal' && (
Expand Down
4 changes: 3 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const Backend = new HttpApi(null, {
initGamepad()
initShortcuts()

const storage: Storage = window.localStorage

i18next
// load translation using http -> see /public/locales
// learn more: https://github.com/i18next/i18next-http-backend
Expand All @@ -34,7 +36,7 @@ i18next
interpolation: {
escapeValue: false
},
lng: 'en',
lng: storage.getItem('language') || 'en',
react: {
useSuspense: true
},
Expand Down
17 changes: 5 additions & 12 deletions src/screens/Library/components/GameCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './index.css'

import React, { useContext, useEffect, useState, CSSProperties } from 'react'
import React, { useContext, CSSProperties, useMemo } from 'react'

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faRepeat } from '@fortawesome/free-solid-svg-icons'
Expand Down Expand Up @@ -191,23 +191,16 @@ const GameCard = ({
return null
}

const [isHiddenGame, setIsHiddenGame] = useState(false)
const [isFavouriteGame, setIsFavouriteGame] = useState(false)

useEffect(() => {
const found = !!hiddenGames.list.find(
const isHiddenGame = useMemo(() => {
return !!hiddenGames.list.find(
(hiddenGame) => hiddenGame.appName === appName
)

setIsHiddenGame(found)
}, [hiddenGames, appName])

useEffect(() => {
const found = !!favouriteGames.list.find(
const isFavouriteGame = useMemo(() => {
return !!favouriteGames.list.find(
(favouriteGame) => favouriteGame.appName === appName
)

setIsFavouriteGame(found)
}, [favouriteGames, appName])

const isMac = ['osx', 'Mac']
Expand Down
190 changes: 150 additions & 40 deletions src/screens/Library/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import './index.css'

import React, { lazy, useContext, useEffect, useRef, useState } from 'react'
import React, {
lazy,
useContext,
useEffect,
useMemo,
useRef,
useState
} from 'react'

import ContextProvider from 'src/state/ContextProvider'

Expand All @@ -23,13 +30,19 @@ export default function Library(): JSX.Element {
layout,
libraryStatus,
refreshing,
refreshingInTheBackground,
category,
filter,
epic,
gog,
recentGames,
favouriteGames,
libraryTopSection
libraryTopSection,
filterText,
platform,
filterPlatform,
hiddenGames,
showHidden
} = useContext(ContextProvider)

const [showModal, setShowModal] = useState({
Expand Down Expand Up @@ -109,26 +122,127 @@ export default function Library(): JSX.Element {
setInstalling(newInstalling)
}, [libraryStatus])

// select library and sort
let libraryToShow = category === 'epic' ? epic.library : gog.library
libraryToShow = libraryToShow.sort(
(a: { title: string }, b: { title: string }) => {
const filterLibrary = (library: GameInfo[], filter: string) => {
if (!library) {
return []
}

if (filter.includes('UE_')) {
return library.filter((game) => {
if (!game.compatible_apps) {
return false
}
return game.compatible_apps.includes(filter)
})
} else {
switch (filter) {
case 'unreal':
return library.filter(
(game) =>
game.is_ue_project || game.is_ue_asset || game.is_ue_plugin
)
case 'asset':
return library.filter((game) => game.is_ue_asset)
case 'plugin':
return library.filter((game) => game.is_ue_plugin)
case 'project':
return library.filter((game) => game.is_ue_project)
default:
return library.filter((game) => game.is_game)
}
}
}

const filterByPlatform = (library: GameInfo[], filter: string) => {
if (category === 'epic' && platform === 'linux') {
return library.filter((game) => game.is_game)
}

const isMac = ['osx', 'Mac']

switch (filter) {
case 'win':
return library.filter((game) => {
return game.is_installed
? game.install.platform === 'windows'
: process.platform === 'darwin'
? !game.is_mac_native
: !game.is_linux_native
})
case 'mac':
return library.filter((game) => {
return game.is_installed
? isMac.includes(game.install.platform ?? '')
: game.is_mac_native
})
case 'linux':
return library.filter((game) => {
return game.is_installed
? game.install.platform === 'linux'
: game.is_linux_native
})
default:
return library.filter((game) => game.is_game)
}
}

// select library
const libraryToShow = useMemo(() => {
let library = category === 'epic' ? epic.library : gog.library

// filter
try {
const filterRegex = new RegExp(filterText, 'i')
const textFilter = ({ title, app_name }: GameInfo) =>
filterRegex.test(title) || filterRegex.test(app_name)
library = filterByPlatform(
filterLibrary(library, filter).filter(textFilter),
filterPlatform
)
} catch (error) {
console.log(error)
}

// hide hidden
const hiddenGamesAppNames = hiddenGames.list.map((hidden) => hidden.appName)

if (!showHidden) {
library = library.filter(
(game) => !hiddenGamesAppNames.includes(game.app_name)
)
}

// sort
library = library.sort((a: { title: string }, b: { title: string }) => {
const gameA = a.title.toUpperCase().replace('THE ', '')
const gameB = b.title.toUpperCase().replace('THE ', '')
return sortDescending ? (gameA > gameB ? -1 : 1) : gameA < gameB ? -1 : 1
}
)
const installed = libraryToShow.filter((g) => g.is_installed)
const notInstalled = libraryToShow.filter(
(g) => !g.is_installed && !installing.includes(g.app_name)
)
const installingGames = libraryToShow.filter((g) =>
installing.includes(g.app_name)
)
libraryToShow = sortInstalled
? [...installed, ...installingGames, ...notInstalled]
: libraryToShow
})
const installed = library.filter((g) => g.is_installed)
const notInstalled = library.filter(
(g) => !g.is_installed && !installing.includes(g.app_name)
)
const installingGames = library.filter((g) =>
installing.includes(g.app_name)
)
library = sortInstalled
? [...installed, ...installingGames, ...notInstalled]
: library

return library
}, [
category,
epic,
gog,
filter,
filterText,
filterPlatform,
sortDescending,
sortInstalled,
showHidden
])

// top section
const showRecentGames =
libraryTopSection === 'recently_played' &&
!!recentGames.length &&
Expand All @@ -139,29 +253,25 @@ export default function Library(): JSX.Element {
!!favouriteGames.list.length &&
category !== 'unreal'

const favourites: GameInfo[] = []

if (showFavourites) {
const favouriteAppNames = favouriteGames.list.map(
(favourite) => favourite.appName
)
epic.library.forEach((game) => {
if (favouriteAppNames.includes(game.app_name)) favourites.push(game)
})
gog.library.forEach((game) => {
if (favouriteAppNames.includes(game.app_name)) favourites.push(game)
})
}

const dlcCount = epic.library.filter((lib) => lib.install.is_dlc)
const numberOfGames =
category === 'epic'
? epic.library.length - dlcCount.length
: gog.library.length
const favourites: GameInfo[] = useMemo(() => {
const tempArray: GameInfo[] = []
if (showFavourites) {
const favouriteAppNames = favouriteGames.list.map(
(favourite) => favourite.appName
)
epic.library.forEach((game) => {
if (favouriteAppNames.includes(game.app_name)) tempArray.push(game)
})
gog.library.forEach((game) => {
if (favouriteAppNames.includes(game.app_name)) tempArray.push(game)
})
}
return tempArray
}, [showFavourites, favouriteGames, epic, gog])

return (
<>
<Header numberOfGames={numberOfGames} />
<Header list={libraryToShow} />

<div className="listing">
<span id="top" />
Expand All @@ -184,9 +294,9 @@ export default function Library(): JSX.Element {

<h3 className="libraryHeader">{titleWithIcons()}</h3>

{refreshing && <UpdateComponent inline />}
{refreshing && !refreshingInTheBackground && <UpdateComponent inline />}

{!refreshing && (
{(!refreshing || refreshingInTheBackground) && (
<GamesList
library={libraryToShow}
layout={layout}
Expand Down
1 change: 1 addition & 0 deletions src/state/ContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const initialContext: ContextType = {
refreshLibrary: async () => Promise.resolve(),
refreshWineVersionInfo: async () => Promise.resolve(),
refreshing: false,
refreshingInTheBackground: true,
isRTL: false,
hiddenGames: {
list: [],
Expand Down
Loading

0 comments on commit b75e515

Please sign in to comment.