From 5bd2fdcdd513fd683a6cbc79dbd79098c19db8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=A1vio=20F=20Lima?= Date: Mon, 30 May 2022 13:39:37 +0200 Subject: [PATCH] [Fix] Library not showing and Corrupted config dialog (#1411) * [Fix] Library going to Unreal Market on Login * fix: error dialogs * other: version * fix: login check * chore: skip auto-update on macOS * fix: wineprefix error on windows * chore: deal with old categorry epic * fix: library logic and login event --- electron/config.ts | 45 ++++++-------- electron/game_config.ts | 34 ++++------ electron/launcher.ts | 11 ++-- electron/legendary/library.ts | 2 +- electron/logger/logger.ts | 2 +- electron/main.ts | 7 ++- electron/utils.ts | 8 +-- package.json | 2 +- src/components/UI/Header/index.tsx | 4 +- .../Sidebar/components/SidebarLinks/index.tsx | 10 +-- src/screens/Library/constants.ts | 2 +- src/screens/Library/index.tsx | 16 +++-- src/screens/WebView/index.tsx | 62 ++++++++----------- src/state/ContextProvider.tsx | 2 +- src/state/GlobalState.tsx | 21 ++++--- src/types.ts | 6 +- 16 files changed, 113 insertions(+), 121 deletions(-) diff --git a/electron/config.ts b/electron/config.ts index 79b79bc86f..b9d0d70947 100644 --- a/electron/config.ts +++ b/electron/config.ts @@ -62,7 +62,8 @@ abstract class GlobalConfig { } catch (error) { logError( `Config file is corrupted, please check ${heroicConfigPath}`, - LogPrefix.Backend + LogPrefix.Backend, + false ) version = 'v0' } @@ -94,7 +95,8 @@ abstract class GlobalConfig { default: logError( `Invalid config version '${version}' requested.`, - LogPrefix.GlobalConfig + LogPrefix.GlobalConfig, + false ) break } @@ -110,7 +112,8 @@ abstract class GlobalConfig { // Upgrade failed. logError( `Failed to upgrade outdated ${version} config.`, - LogPrefix.GlobalConfig + LogPrefix.GlobalConfig, + false ) } } @@ -340,7 +343,7 @@ abstract class GlobalConfig { * Writes to file after that. * DO NOT call `flush()` afterward. * - * @returns true if upgrade successful, false if upgrade fails or no upgrade needed. + * @returns true if upgrade successful if upgrade fails or no upgrade needed. */ public abstract upgrade(): boolean @@ -419,29 +422,21 @@ class GlobalConfigV0 extends GlobalConfig { return this.getFactoryDefaults() } - try { - let settings = JSON.parse(readFileSync(heroicConfigPath, 'utf-8')) - const defaultSettings = settings.defaultSettings as AppSettings + let settings = JSON.parse(readFileSync(heroicConfigPath, 'utf-8')) + const defaultSettings = settings.defaultSettings as AppSettings - // fix relative paths - const winePrefix = defaultSettings.winePrefix.replace('~', userHome) + // fix relative paths + const winePrefix = !isWindows + ? defaultSettings?.winePrefix?.replace('~', userHome) + : '' - settings = { - ...(await this.getFactoryDefaults()), - ...settings.defaultSettings, - winePrefix - } as AppSettings - return settings - } catch (error) { - logError( - `Config file is corrupted, please check ${heroicConfigPath}`, - LogPrefix.Backend - ) - const settings = { - ...(await this.getFactoryDefaults()) - } - return settings - } + settings = { + ...(await this.getFactoryDefaults()), + ...settings.defaultSettings, + winePrefix + } as AppSettings + + return settings } public async getCustomWinePaths(): Promise> { diff --git a/electron/game_config.ts b/electron/game_config.ts index f609854f39..3c635bd382 100644 --- a/electron/game_config.ts +++ b/electron/game_config.ts @@ -127,7 +127,7 @@ abstract class GameConfig { * Writes to file after that. * DO NOT call `flush()` afterward. * - * @returns true if upgrade successful, false if upgrade fails or no upgrade needed. + * @returns true if upgrade successful if upgrade fails or no upgrade needed. */ public abstract upgrade(): boolean @@ -198,28 +198,20 @@ class GameConfigV0 extends GameConfig { if (!existsSync(this.path)) { return { ...GlobalConfig.get().config } 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. + const settings = JSON.parse(readFileSync(this.path, 'utf-8')) + // Take defaults, then overwrite if explicitly set values exist. + // The settings defined work as overrides. - // fix relative paths - const { winePrefix } = settings[this.appName] as GameSettings + // fix relative paths + const { winePrefix } = (settings[this.appName] as GameSettings) || {} - return { - ...GlobalConfig.get().config, - ...settings[this.appName], - winePrefix: winePrefix.replace('~', userHome) - } as GameSettings - } catch (error) { - logError( - `Config file is corrupted, please check ${this.path}`, - LogPrefix.Backend - ) - return { - ...GlobalConfig.get().config - } - } + return { + ...GlobalConfig.get().config, + ...settings[this.appName], + winePrefix: winePrefix + ? winePrefix.replace('~', userHome) + : `${userHome}/.wine` + } as GameSettings } public async resetToDefaults() { diff --git a/electron/launcher.ts b/electron/launcher.ts index 89d6f58e81..aa651a9839 100644 --- a/electron/launcher.ts +++ b/electron/launcher.ts @@ -550,7 +550,8 @@ async function runLegendaryOrGogdlCommand( errorHandler({ error: `${stdout.join().concat(stderr.join())}`, logPath: options?.logFile, - runner: runner.name + runner: runner.name, + appName }) if (signal) { @@ -577,14 +578,14 @@ async function runLegendaryOrGogdlCommand( appName }) - const dontShowDialog = - `${error}`.includes('signal') && - `${error}`.includes('appears to be deleted') + const showDialog = + !`${error}`.includes('signal') && + !`${error}`.includes('appears to be deleted') logError( ['Error running', runner.name, 'command', `"${safeCommand}": ${error}`], runner.logPrefix, - dontShowDialog + showDialog ) return { stdout: '', stderr: `${error}`, fullCommand: safeCommand, error } }) diff --git a/electron/legendary/library.ts b/electron/legendary/library.ts index 815151786d..660a32dca0 100644 --- a/electron/legendary/library.ts +++ b/electron/legendary/library.ts @@ -532,7 +532,7 @@ export class LegendaryLibrary { this.library.set(app_name, null) return app_name } catch (error) { - logError(`Metadata for ${fileName} corrupted`, LogPrefix.Legendary, false) + logError(`Metadata for ${fileName} corrupted`, LogPrefix.Legendary) return 'error' } } diff --git a/electron/logger/logger.ts b/electron/logger/logger.ts index b04a042c76..66d158084c 100644 --- a/electron/logger/logger.ts +++ b/electron/logger/logger.ts @@ -103,7 +103,7 @@ export function logDebug( export function logError( text: string[] | string, prefix: LogPrefix = LogPrefix.General, - showDialog = true, + showDialog = false, skipLogToFile = false ) { const extendText = `${getTimeStamp()} ERROR: ${getPrefixString( diff --git a/electron/main.ts b/electron/main.ts index 22bfc22349..b826f88bec 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -83,7 +83,8 @@ import { weblateUrl, wikiLink, fontsStore, - heroicConfigPath + heroicConfigPath, + isMac } from './constants' import { handleProtocol } from './protocol' import { logError, logInfo, LogPrefix, logWarning } from './logger/logger' @@ -192,7 +193,9 @@ async function createWindow(): Promise { } else { Menu.setApplicationMenu(null) mainWindow.loadURL(`file://${path.join(__dirname, '../build/index.html')}`) - autoUpdater.checkForUpdates() + if (!isMac) { + autoUpdater.checkForUpdates() + } return mainWindow } } diff --git a/electron/utils.ts b/electron/utils.ts index 51598ea455..51c6c77987 100644 --- a/electron/utils.ts +++ b/electron/utils.ts @@ -298,11 +298,7 @@ async function errorHandler( const noSpaceMsg = 'Not enough available disk space' const plat = r === 'legendary' ? 'Legendary (Epic Games)' : r const deletedFolderMsg = 'appears to be deleted' - const otherErrorMessages = [ - 'No saved credentials', - 'in get_user_entitlements', - 'No credentials' - ] + const otherErrorMessages = ['No saved credentials', 'No credentials'] if (!window) { window = BrowserWindow.getFocusedWindow() @@ -312,7 +308,7 @@ async function errorHandler( execAsync(`tail "${logPath}" | grep 'disk space'`) .then(({ stdout }) => { if (stdout.includes(noSpaceMsg)) { - logError(noSpaceMsg, LogPrefix.Backend, false) + logError(noSpaceMsg, LogPrefix.Backend) return showErrorBoxModal( window, i18next.t('box.error.diskspace.title', 'No Space'), diff --git a/package.json b/package.json index cfbcc8fe7e..b1e2fe859c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "heroic", - "version": "2.3.4", + "version": "2.3.5", "private": true, "main": "public/main.js", "homepage": "./", diff --git a/src/components/UI/Header/index.tsx b/src/components/UI/Header/index.tsx index 32f990cacf..cb42471f2a 100644 --- a/src/components/UI/Header/index.tsx +++ b/src/components/UI/Header/index.tsx @@ -41,7 +41,9 @@ export default function Header({ list }: ComponentProps) { const numberOfGames = useMemo(() => { const dlcCount = - category === 'epic' ? list.filter((lib) => lib.install.is_dlc).length : 0 + category === 'legendary' + ? list.filter((lib) => lib.install.is_dlc).length + : 0 return list.length - dlcCount }, [list, category]) diff --git a/src/components/UI/Sidebar/components/SidebarLinks/index.tsx b/src/components/UI/Sidebar/components/SidebarLinks/index.tsx index bba1d6ccec..5f9217a48d 100644 --- a/src/components/UI/Sidebar/components/SidebarLinks/index.tsx +++ b/src/components/UI/Sidebar/components/SidebarLinks/index.tsx @@ -14,7 +14,7 @@ import { useTranslation } from 'react-i18next' import { NavLink, useLocation } from 'react-router-dom' import { getAppSettings } from 'src/helpers' import ContextProvider from 'src/state/ContextProvider' -import { Runner } from 'src/types' +import { Category, Runner } from 'src/types' import './index.css' interface LocationState { @@ -60,7 +60,7 @@ export default function SidebarLinks() { const loggedIn = epic.username || gog.username const toggleCategory = useCallback( - (newCategory: string) => { + (newCategory: Category) => { if (category !== newCategory) { handleCategory(newCategory) handleFilter(newCategory === 'unreal' ? 'unreal' : 'all') @@ -112,9 +112,9 @@ export default function SidebarLinks() { {epic.username && ( toggleCategory('epic')} + onClick={() => toggleCategory('legendary')} className={cx('Sidebar__item SidebarLinks__subItem', { - ['active']: category === 'epic' + ['active']: category === 'legendary' })} > {t('Epic Games', 'Epic Games')} @@ -218,7 +218,7 @@ export default function SidebarLinks() { to={`/settings/${appName}/wine`} state={{ ...state, runner: state?.runner }} className={cx('Sidebar__item SidebarLinks__subItem', { - ['active']: category === 'wine' + ['active']: type === 'wine' })} > Wine diff --git a/src/screens/Library/constants.ts b/src/screens/Library/constants.ts index 02d11a6e0a..e2b4183c33 100644 --- a/src/screens/Library/constants.ts +++ b/src/screens/Library/constants.ts @@ -5,7 +5,7 @@ export function getLibraryTitle( filter: string, t: TFunction<'translation'> ) { - if (category === 'epic' || category === 'gog') { + if (category === 'legendary' || category === 'gog') { return t('title.allGames', 'All Games') } diff --git a/src/screens/Library/index.tsx b/src/screens/Library/index.tsx index 880ef2dcf9..fadea188a3 100644 --- a/src/screens/Library/index.tsx +++ b/src/screens/Library/index.tsx @@ -106,7 +106,7 @@ export default function Library(): JSX.Element { sortDescending={sortDescending} toggleSortDescending={() => handleSortDescending()} sortInstalled={sortInstalled} - library={category === 'epic' ? 'legendary' : 'gog'} + library={category === 'legendary' ? 'legendary' : 'gog'} toggleSortinstalled={() => handleSortInstalled()} /> @@ -160,7 +160,7 @@ export default function Library(): JSX.Element { return [] } - if (category === 'epic' && platform === 'linux') { + if (category === 'legendary' && platform === 'linux') { return library.filter((game) => game?.is_game) } @@ -195,14 +195,20 @@ export default function Library(): JSX.Element { // select library const libraryToShow = useMemo(() => { let library: GameInfo[] = [] - if (epic.username && category === 'epic') { + const isEpic = + epic.username && (category === 'legendary' || category === 'unreal') + const isGog = gog.username && category === 'gog' + + if (isEpic) { library = epic.library - } else if (gog.username && category === 'gog') { + } else if (isGog) { library = gog.library - } else if (!epic.username && category === 'epic') { + } else if (!isEpic) { if (gog.username) { library = gog.library } + } else { + library = epic.library } // filter diff --git a/src/screens/WebView/index.tsx b/src/screens/WebView/index.tsx index 71be96e68f..06d01926c6 100644 --- a/src/screens/WebView/index.tsx +++ b/src/screens/WebView/index.tsx @@ -95,41 +95,33 @@ export default function WebView() { } } else { webview.addEventListener('did-navigate', () => { - if ( - webview.getURL() === 'https://www.epicgames.com/id/api/redirect' - ) { - webview.addEventListener( - 'found-in-page', - async (res) => { - const data = res as Event & { result: { matches: number } } - - if (!data.result.matches) { - return - } - webview.focus() - webview.selectAll() - webview.copy() - - if (!clipboard.readText().match('sid')) { - return - } - const { sid }: SID = JSON.parse(clipboard.readText()) - try { - setLoading({ - refresh: true, - message: t('status.logging', 'Logging In...') - }) - await epic.login(sid) - handleSuccessfulLogin() - } catch (error) { - console.error(error) - ipcRenderer.send('logError', error) - } - }, - { once: true } - ) - webview.findInPage('sid') - } + webview.findInPage('sid') + webview.addEventListener('found-in-page', async (res) => { + const data = res as Event & { result: { matches: number } } + + if (!data.result.matches) { + return + } + webview.focus() + webview.selectAll() + webview.copy() + + if (!clipboard.readText().match('sid')) { + return + } + const { sid }: SID = JSON.parse(clipboard.readText()) + try { + setLoading({ + refresh: true, + message: t('status.logging', 'Logging In...') + }) + await epic.login(sid) + handleSuccessfulLogin() + } catch (error) { + console.error(error) + ipcRenderer.send('logError', error) + } + }) }) } } diff --git a/src/state/ContextProvider.tsx b/src/state/ContextProvider.tsx index 8093b85f7c..4d64e7cab4 100644 --- a/src/state/ContextProvider.tsx +++ b/src/state/ContextProvider.tsx @@ -3,7 +3,7 @@ import React from 'react' import { ContextType } from 'src/types' const initialContext: ContextType = { - category: 'epic', + category: 'legendary', epic: { library: [], username: null, diff --git a/src/state/GlobalState.tsx b/src/state/GlobalState.tsx index e169f88b59..c3aa415254 100644 --- a/src/state/GlobalState.tsx +++ b/src/state/GlobalState.tsx @@ -1,6 +1,7 @@ import React, { PureComponent } from 'react' import { + Category, FavouriteGame, GameInfo, GameStatus, @@ -47,7 +48,7 @@ interface Props { } interface StateProps { - category: string + category: Category epic: { library: GameInfo[] username: string | null @@ -100,7 +101,7 @@ export class GlobalState extends PureComponent { return games } state: StateProps = { - category: storage.getItem('category') || 'epic', + category: (storage.getItem('category') as Category) || 'legendary', epic: { library: libraryStore.has('library') ? (libraryStore.get('library', []) as GameInfo[]) @@ -141,8 +142,7 @@ export class GlobalState extends PureComponent { (configStore.get('contentFontFamily') as string) || "'Cabin', sans-serif", actionsFontFamily: (configStore.get('actionsFontFamily') as string) || "'Rubik', sans-serif", - allTilesInColor: - (configStore.get('allTilesInColor', false) as boolean) || false + allTilesInColor: (configStore.get('allTilesInColor') as boolean) || false } setLanguage = (newLanguage: string) => { @@ -260,7 +260,7 @@ export class GlobalState extends PureComponent { } }) - this.handleSuccessfulLogin('legendery' as Runner) + this.handleSuccessfulLogin('legendary') } return response.status @@ -396,7 +396,7 @@ export class GlobalState extends PureComponent { if (fetch) { // try to restore the saved information await ipcRenderer - .invoke('refreshWineVersionInfo', false) + .invoke('refreshWineVersionInfo') .then((releases) => { this.setState({ wineVersions: releases @@ -423,7 +423,7 @@ export class GlobalState extends PureComponent { handlePlatformFilter = (filterPlatform: string) => this.setState({ filterPlatform }) handleLayout = (layout: string) => this.setState({ layout }) - handleCategory = (category: string) => this.setState({ category }) + handleCategory = (category: Category) => this.setState({ category }) handleGameStatus = async ({ appName, @@ -485,8 +485,11 @@ export class GlobalState extends PureComponent { async componentDidMount() { const { t } = this.props - const { epic, gog, gameUpdates = [], libraryStatus } = this.state - + const { epic, gog, gameUpdates = [], libraryStatus, category } = this.state + const oldCategory: string = category + if (oldCategory === 'epic') { + this.handleCategory('legendary') + } // Deals launching from protocol. Also checks if the game is already running ipcRenderer.on('launchGame', async (e, appName, runner) => { const currentApp = libraryStatus.filter( diff --git a/src/types.ts b/src/types.ts index a20f33d87b..520aae9dbb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -47,8 +47,10 @@ export interface AppSettings { useSteamRuntime: boolean } +export type Category = 'all' | 'legendary' | 'gog' | 'unreal' | 'heroic' + export interface ContextType { - category: string + category: Category wineVersions: WineVersionInfo[] recentGames: GameInfo[] error: boolean @@ -59,7 +61,7 @@ export interface ContextType { isRTL: boolean language: string setLanguage: (newLanguage: string) => void - handleCategory: (value: string) => void + handleCategory: (value: Category) => void handleFilter: (value: string) => void handlePlatformFilter: (value: string) => void handleGameStatus: (game: GameStatus) => Promise