Skip to content

[UI/UX] Improve error handling on BE and FE #1363

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

Merged
merged 35 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3473865
wip: show error message if no games found
flavioislima May 23, 2022
0e9eb68
feat: add Error components
flavioislima May 23, 2022
165c78f
i18n: updated keys
flavioislima May 23, 2022
d5fb22a
chore: typo
flavioislima May 23, 2022
864085d
fix: check
flavioislima May 23, 2022
790c0fc
feat: show error message on gamepage
flavioislima May 23, 2022
8d1903c
chore: added more checks
flavioislima May 23, 2022
21fd5cc
feat: added more messages and error handlers
flavioislima May 24, 2022
9082ec0
chore: change invalid path message
flavioislima May 24, 2022
e843a90
feat: add context menu to Settings
flavioislima May 24, 2022
6983640
fix: steam runtime setting not updating
flavioislima May 24, 2022
e433727
fix: type conversion on logger
flavioislima May 25, 2022
293dae9
feat: add showdialog param to logger
flavioislima May 25, 2022
a4829d3
fix: wrong type coertions
flavioislima May 25, 2022
5119fcb
chore: log level adjustments
flavioislima May 25, 2022
f5ee3e3
fix: epic webview login not working
flavioislima May 25, 2022
67be666
fix: logged in status not changing
flavioislima May 25, 2022
68c8ef0
feat: handle other login errors
flavioislima May 25, 2022
0a9e030
chore: refactor login FE a bit
flavioislima May 25, 2022
2f0f0cb
feat: deal with memory cache failure on install
flavioislima May 25, 2022
e57d098
fix: error handler error
flavioislima May 25, 2022
cc47b81
chore: pr comments
flavioislima May 25, 2022
07e5f7e
chore: pr comments 2
flavioislima May 25, 2022
f06232a
chore: function naming
flavioislima May 25, 2022
035bb5c
Merge branch 'main' of github.com:Heroic-Games-Launcher/HeroicGamesLa…
flavioislima May 25, 2022
afe6f64
feat: refactor and deal with deleted folders
flavioislima May 26, 2022
30f3e6b
feat: gog forceUninstall
flavioislima May 26, 2022
ab7d49e
fix: use legendary uninstall command
flavioislima May 26, 2022
b1a1e54
chore: pr comments
flavioislima May 26, 2022
04600d1
fix: imports and tests
flavioislima May 26, 2022
4e03fe4
feat: check if gog game folder exists
flavioislima May 26, 2022
705bf5d
i18n: updated keys
flavioislima May 26, 2022
feca68f
feat: get runtimes from steam libraries
flavioislima May 26, 2022
cfd8c43
Merge branch 'main' of github.com:Heroic-Games-Launcher/HeroicGamesLa…
flavioislima May 27, 2022
bf5adfe
i18n: updated keys
flavioislima May 27, 2022
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
33 changes: 26 additions & 7 deletions electron/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,15 @@ abstract class GlobalConfig {
// Config file exists, detect its version.
else {
// Check version field in the config.
version = JSON.parse(readFileSync(heroicConfigPath, 'utf-8'))['version']
try {
version = JSON.parse(readFileSync(heroicConfigPath, 'utf-8'))['version']
} catch (error) {
logError(
`Config file is corrupted, please check ${heroicConfigPath}`,
LogPrefix.Backend
)
version = 'v0'
}
// Legacy config file without a version field, it's a v0 config.
if (!version) {
version = 'v0'
Expand Down Expand Up @@ -420,12 +428,23 @@ class GlobalConfigV0 extends GlobalConfig {
return this.getFactoryDefaults()
}

let settings = JSON.parse(readFileSync(heroicConfigPath, 'utf-8'))
settings = {
...(await this.getFactoryDefaults()),
...settings.defaultSettings
} as AppSettings
return settings
try {
let settings = JSON.parse(readFileSync(heroicConfigPath, 'utf-8'))
settings = {
...(await this.getFactoryDefaults()),
...settings.defaultSettings
} as AppSettings
return settings
} catch (error) {
logError(
`Config file is corrupted, please check ${heroicConfigPath}`,
LogPrefix.Backend
)
const settings = {
...(await this.getFactoryDefaults())
}
return settings
}
}

public async getCustomWinePaths(): Promise<Set<WineInstallation>> {
Expand Down
34 changes: 26 additions & 8 deletions electron/game_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,15 @@ abstract class GameConfig {
// Config file exists, detect its version.
else {
// Check version field in the config.
version = JSON.parse(readFileSync(path, 'utf-8'))['version']
try {
version = JSON.parse(readFileSync(path, 'utf-8'))['version']
} catch (error) {
logError(
`Config file is corrupted, please check ${path}`,
LogPrefix.Backend
)
version = 'v0'
}
// Legacy config file without a version field, it's a v0 config.
if (!version) {
version = 'v0'
Expand Down Expand Up @@ -189,13 +197,23 @@ class GameConfigV0 extends GameConfig {
if (!existsSync(this.path)) {
return { ...GlobalConfig.get().config } as GameSettings
}
const settings = JSON.parse(readFileSync(this.path, 'utf-8'))
// Take defaults, then overwrite if explicitly set values exist.
// The settings defined work as overrides.
return {
...GlobalConfig.get().config,
...settings[this.appName]
} as GameSettings
try {
const settings = JSON.parse(readFileSync(this.path, 'utf-8'))
// Take defaults, then overwrite if explicitly set values exist.
// The settings defined work as overrides.
return {
...GlobalConfig.get().config,
...settings[this.appName]
} as GameSettings
} catch (error) {
logError(
`Config file is corrupted, please check ${this.path}`,
LogPrefix.Backend
)
return {
...GlobalConfig.get().config
}
}
}

public async resetToDefaults() {
Expand Down
9 changes: 7 additions & 2 deletions electron/gog/electronStores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ const configStore = new Store({

const apiInfoCache = new Store({
cwd: 'gog_store',
name: 'api_info_cache'
name: 'api_info_cache',
clearInvalidConfig: true
})
const libraryStore = new Store({
cwd: 'gog_store',
name: 'library',
clearInvalidConfig: true
})
const libraryStore = new Store({ cwd: 'gog_store', name: 'library' })

export { configStore, installedGamesStore, apiInfoCache, libraryStore }
20 changes: 13 additions & 7 deletions electron/gog/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import {
InstallArgs,
LaunchResult,
GOGLoginData,
InstalledInfo,
InstallProgress
InstalledInfo
} from 'types'
import { existsSync, rmSync } from 'graceful-fs'
import {
Expand Down Expand Up @@ -109,8 +108,15 @@ class GOGGame extends Game {
LogPrefix.Gog
)
}
await GOGLibrary.get().importGame(JSON.parse(res.stdout), path)
return res
try {
await GOGLibrary.get().importGame(JSON.parse(res.stdout), path)
return res
} catch (error) {
logError(
['Failed to import', `${this.appName}:`, res.error],
LogPrefix.Gog
)
}
}

public onInstallOrUpdateOutput(
Expand Down Expand Up @@ -224,7 +230,7 @@ class GOGGame extends Game {
buildId: isLinuxNative ? '' : installInfo.game.buildId
}
const array: Array<InstalledInfo> =
(installedGamesStore.get('installed') as Array<InstalledInfo>) || []
(installedGamesStore.get('installed', []) as Array<InstalledInfo>) || []
array.push(installedData)
installedGamesStore.set('installed', array)
GOGLibrary.get().refreshInstalled()
Expand Down Expand Up @@ -513,7 +519,7 @@ class GOGGame extends Game {
res.stderr = stderr
})
.catch((error) => {
res.error = error
res.error = `${error}`
})
} else {
rmSync(object.install_path, { recursive: true })
Expand Down Expand Up @@ -611,7 +617,7 @@ class GOGGame extends Game {
if (GOGUser.isTokenExpired()) {
await GOGUser.refreshToken()
}
const credentials = configStore.get('credentials') as GOGLoginData
const credentials = configStore.get('credentials', {}) as GOGLoginData

const installPlatform = gameData.install.platform
const logPath = join(heroicGamesConfigPath, this.appName + '.log')
Expand Down
31 changes: 19 additions & 12 deletions electron/gog/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class GOGLibrary {
}

const gamesObjects: GameInfo[] = []
const gamesArray = libraryStore.get('games') as GameInfo[]
const gamesArray = libraryStore.get('games', []) as GameInfo[]
for (const game of gameApiArray as GOGGameInfo[]) {
let unifiedObject = gamesArray
? gamesArray.find((value) => value.app_name === String(game.id))
Expand Down Expand Up @@ -182,7 +182,7 @@ export class GOGLibrary {
}

const gogInfo = JSON.parse(res.stdout)
const libraryArray = libraryStore.get('games') as GameInfo[]
const libraryArray = libraryStore.get('games', []) as GameInfo[]
const gameObjectIndex = libraryArray.findIndex(
(value) => value.app_name === appName
)
Expand Down Expand Up @@ -219,7 +219,7 @@ export class GOGLibrary {
*/
public refreshInstalled() {
const installedArray =
(installedGamesStore.get('installed') as Array<InstalledInfo>) || []
(installedGamesStore.get('installed', []) as Array<InstalledInfo>) || []
this.installedGames.clear()
installedArray.forEach((value) => {
this.installedGames.set(value.appName, value)
Expand All @@ -230,7 +230,7 @@ export class GOGLibrary {
const cachedGameData = this.library.get(appName)

const installedArray =
(installedGamesStore.get('installed') as Array<InstalledInfo>) || []
(installedGamesStore.get('installed', []) as Array<InstalledInfo>) || []

const gameIndex = installedArray.findIndex(
(value) => value.appName === appName
Expand Down Expand Up @@ -488,18 +488,25 @@ export class GOGLibrary {
logInfo(`Loading playTask data from ${infoFilePath}`, LogPrefix.Backend)
const fileData = readFileSync(infoFilePath, { encoding: 'utf-8' })

const jsonData = JSON.parse(fileData)
const playTasks = jsonData.playTasks
try {
const jsonData = JSON.parse(fileData)
const playTasks = jsonData.playTasks

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const primary = playTasks.find((value: any) => value?.isPrimary)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const primary = playTasks.find((value: any) => value?.isPrimary)

const workingDir = primary?.workingDir
const workingDir = primary?.workingDir

if (workingDir) {
return join(workingDir, primary.path)
if (workingDir) {
return join(workingDir, primary.path)
}
return primary.path
} catch (error) {
logError(
`Error reading ${fileData}, could not complete operation`,
LogPrefix.Gog
)
}
return primary.path
}

return ''
Expand Down
17 changes: 14 additions & 3 deletions electron/gog/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import axios from 'axios'
import { logError, logInfo, LogPrefix, logWarning } from '../logger/logger'
import { GOGLoginData } from '../types'
import { configStore, libraryStore } from '../gog/electronStores'
import { errorHandler } from '../utils'

const gogAuthenticateUrl =
'https://auth.gog.com/token?client_id=46899977096215655&client_secret=9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fembed.gog.com%2Fon_login_success%3Forigin%3Dclient&code='
Expand Down Expand Up @@ -72,14 +73,17 @@ export class GOGUser {
return this.refreshToken()
}

return configStore.get('credentials') as GOGLoginData
return configStore.get('credentials', {}) as GOGLoginData
}

/**
* Refreshes token and returns new credentials
*/
public static async refreshToken(): Promise<GOGLoginData | null> {
const user: GOGLoginData = configStore.get('credentials') as GOGLoginData
const user: GOGLoginData = configStore.get(
'credentials',
{}
) as GOGLoginData
logInfo('Refreshing access_token', LogPrefix.Gog)
if (user) {
const response = await axios
Expand All @@ -103,12 +107,19 @@ export class GOGUser {
return data
} else {
logError('No credentials, auth required', LogPrefix.Gog)
errorHandler({
error: { stderr: 'No credentials', stdout: '' },
runner: 'GOG'
})
return null
}
}

public static isTokenExpired() {
const user: GOGLoginData = configStore.get('credentials') as GOGLoginData
const user: GOGLoginData = configStore.get(
'credentials',
null
) as GOGLoginData
if (!user) {
return true
}
Expand Down
39 changes: 32 additions & 7 deletions electron/launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ export async function verifyWinePrefix(
return { res: result, updated: wasUpdated }
})
.catch((error) => {
logError(['Unable to create Wineprefix: ', error], LogPrefix.Backend)
return { res: { stderr: error, stdout: '' }, updated: false }
logError(['Unable to create Wineprefix: ', `${error}`], LogPrefix.Backend)
return { res: { stderr: `${error}`, stdout: '' }, updated: false }
})
}

Expand Down Expand Up @@ -397,8 +397,9 @@ async function runWineCommand(
return response
})
.catch((error) => {
logError(['Error running Wine command:', error], LogPrefix.Backend)
return { stderr: error, stdout: '' }
// error might not always be a string
logError(['Error running Wine command:', `${error}`], LogPrefix.Backend)
return { stderr: `${error}`, stdout: '' }
})
}

Expand Down Expand Up @@ -484,11 +485,22 @@ async function runLegendaryOrGogdlCommand(
child.on('close', (code, signal) => {
errorHandler({
error: { stderr: stderr.join(), stdout: stdout.join() },
logPath: options?.logFile
logPath: options?.logFile,
runner: runner.name
})

if (stderr.join().includes('ERROR')) {
rej(stderr.join())
}

if (stderr.join().includes('MemoryError:')) {
rej('MemoryError:')
}

if (signal) {
rej('Process terminated with signal ' + signal)
}

res({
stdout: stdout.join('\n'),
stderr: stderr.join('\n')
Expand All @@ -502,10 +514,23 @@ async function runLegendaryOrGogdlCommand(
return { stdout, stderr, fullCommand: safeCommand }
})
.catch((error) => {
errorHandler({ error, logPath: options?.logFile })
if (error === 'MemoryError:') {
logWarning(
'Install failed, trying again with higher limit',
LogPrefix.Legendary
)
return { stdout: '', stderr: '', fullCommand: safeCommand, error }
}
errorHandler({
error: { stderr: `${error}`, stdout: `${error}` },
logPath: options?.logFile,
runner: runner.name
})
const showDialog = !`${error}`.includes('signal')
logError(
['Error running', runner.name, 'command', `"${safeCommand}": ${error}`],
runner.logPrefix
runner.logPrefix,
showDialog
)
return { stdout: '', stderr: '', fullCommand: safeCommand, error }
})
Expand Down
9 changes: 6 additions & 3 deletions electron/legendary/electronStores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import Store from 'electron-store'

const installStore = new Store({
cwd: 'lib-cache',
name: 'installInfo'
name: 'installInfo',
clearInvalidConfig: true
})
const libraryStore = new Store({
cwd: 'lib-cache',
name: 'library'
name: 'library',
clearInvalidConfig: true
})

const gameInfoStore = new Store({
cwd: 'lib-cache',
name: 'gameinfo'
name: 'gameinfo',
clearInvalidConfig: true
})

export { gameInfoStore, installStore, libraryStore }
Loading