From 9056f2efe5a10d0be6cfddc493fb773a49b1e4ad Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sat, 28 May 2022 06:20:27 -0400 Subject: [PATCH 01/62] fix: make launcher quit on invalid preferences. Previous behavior: launcher frontend process would hang without creating a window. --- src/back/index.ts | 4 ++-- src/main/Main.ts | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/back/index.ts b/src/back/index.ts index 7ea3495b9..30395e857 100644 --- a/src/back/index.ts +++ b/src/back/index.ts @@ -273,7 +273,7 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { state.preferences = await PreferencesFile.readOrCreateFile(path.join(state.config.flashpointPath, PREFERENCES_FILENAME)); } catch (e) { console.log(e); - exit(state); + send({quit: true}); return; } const [extConf] = await (Promise.all([ @@ -632,7 +632,7 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { } // Respond - send(state.socketServer.port, () => { + send({port: state.socketServer.port}, () => { state.apiEmitters.onDidInit.fire(); }); diff --git a/src/main/Main.ts b/src/main/Main.ts index 5089c06c9..e886688c7 100644 --- a/src/main/Main.ts +++ b/src/main/Main.ts @@ -139,10 +139,12 @@ export function main(init: Init): void { p = p.then(() => new Promise((resolve, reject) => { state.backProc = fork(path.join(__dirname, '../back/index.js'), undefined, { detached: true }); // Wait for process to initialize - state.backProc.once('message', (port) => { - if (port >= 0) { - state.backHost.port = port as string; + state.backProc.once('message', (message: any) => { + if (message.port) { + state.backHost.port = message.port as string; resolve(); + } else if (message.quit){ + app.quit(); } else { reject(new Error('Failed to start server in back process. Perhaps because it could not find an available port.')); } From 55090ee372649bc12383bc34a425e011d7d8bcca Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sat, 28 May 2022 06:42:29 -0400 Subject: [PATCH 02/62] feat: show an error box when the backend dies. There should be a lovely popup before the app quits. --- src/back/index.ts | 27 +++++++++++++++++++++------ src/main/Main.ts | 5 ++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/back/index.ts b/src/back/index.ts index 30395e857..c22901289 100644 --- a/src/back/index.ts +++ b/src/back/index.ts @@ -261,7 +261,15 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { log.info('Launcher', `Starting Flashpoint Launcher ${versionStr}`); // Read configs & preferences - state.config = await ConfigFile.readOrCreateFile(path.join(state.configFolder, CONFIG_FILENAME)); + // readOrCreateFile can throw errors, let's wrap it in try-catch each time. + try { + state.config = await ConfigFile.readOrCreateFile(path.join(state.configFolder, CONFIG_FILENAME)); + } catch (e) { + console.log(e); + // Fatal, quit. + send({quit: true, errorMessage: "Invalid config.json!"}); + return; + } // If we're on mac and the flashpoint path is relative, resolve it relative to the configFolder path. state.config.flashpointPath = process.platform == 'darwin' && state.config.flashpointPath[0] != '/' @@ -273,13 +281,19 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { state.preferences = await PreferencesFile.readOrCreateFile(path.join(state.config.flashpointPath, PREFERENCES_FILENAME)); } catch (e) { console.log(e); - send({quit: true}); + // Fatal, quit. + send({quit: true, errorMessage: "Invalid preferences.json! Is it 0 KB?"}); return; } - const [extConf] = await (Promise.all([ - ExtConfigFile.readOrCreateFile(path.join(state.config.flashpointPath, EXT_CONFIG_FILENAME)) - ])); - state.extConfig = extConf; + try { + const [extConf] = await (Promise.all([ + ExtConfigFile.readOrCreateFile(path.join(state.config.flashpointPath, EXT_CONFIG_FILENAME)) + ])); + state.extConfig = extConf; + } catch (e) { + console.log(e); + // Non-fatal, don't quit. + } // Create Game Data Directory and clean up temp files const fullDataPacksFolderPath = path.join(state.config.flashpointPath, state.preferences.dataPacksFolderPath); @@ -295,6 +309,7 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { }); } catch (error) { console.log('Failed to create default Data Packs folder!'); + // Non-fatal, don't quit. } diff --git a/src/main/Main.ts b/src/main/Main.ts index e886688c7..3b1b0f93d 100644 --- a/src/main/Main.ts +++ b/src/main/Main.ts @@ -143,7 +143,10 @@ export function main(init: Init): void { if (message.port) { state.backHost.port = message.port as string; resolve(); - } else if (message.quit){ + } else if (message.quit) { + if (message.errorMessage) { + dialog.showErrorBox("Flashpoint Startup Error", message.errorMessage); + } app.quit(); } else { reject(new Error('Failed to start server in back process. Perhaps because it could not find an available port.')); From 09dd1f7b0ddb3fea7cafa9d29f3c65b4b2ed29d4 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sat, 28 May 2022 06:51:14 -0400 Subject: [PATCH 03/62] style: use single-quotes. --- src/back/index.ts | 4 ++-- src/main/Main.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/back/index.ts b/src/back/index.ts index c22901289..2afa30bc6 100644 --- a/src/back/index.ts +++ b/src/back/index.ts @@ -267,7 +267,7 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { } catch (e) { console.log(e); // Fatal, quit. - send({quit: true, errorMessage: "Invalid config.json!"}); + send({quit: true, errorMessage: 'Invalid config.json!'}); return; } @@ -282,7 +282,7 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { } catch (e) { console.log(e); // Fatal, quit. - send({quit: true, errorMessage: "Invalid preferences.json! Is it 0 KB?"}); + send({quit: true, errorMessage: 'Invalid preferences.json! Is it 0 KB?'}); return; } try { diff --git a/src/main/Main.ts b/src/main/Main.ts index 3b1b0f93d..252ba77cf 100644 --- a/src/main/Main.ts +++ b/src/main/Main.ts @@ -145,7 +145,7 @@ export function main(init: Init): void { resolve(); } else if (message.quit) { if (message.errorMessage) { - dialog.showErrorBox("Flashpoint Startup Error", message.errorMessage); + dialog.showErrorBox('Flashpoint Startup Error', message.errorMessage); } app.quit(); } else { From 47847b68c73903b5c2a3acb4b7c5f59e3c2c3757 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sat, 28 May 2022 08:01:57 -0400 Subject: [PATCH 04/62] Make ConfigPage wait before displaying new logos. --- src/renderer/components/pages/ConfigPage.tsx | 6 ++--- src/shared/preferences/util.ts | 23 ++++++++++++++-- src/shared/utils/throttle.ts | 28 ++++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/renderer/components/pages/ConfigPage.tsx b/src/renderer/components/pages/ConfigPage.tsx index c6612bb8a..fee73f304 100644 --- a/src/renderer/components/pages/ConfigPage.tsx +++ b/src/renderer/components/pages/ConfigPage.tsx @@ -6,7 +6,7 @@ import { AppExtConfigData } from '@shared/config/interfaces'; import { ExtConfigurationProp, ExtensionContribution, IExtensionDescription, ILogoSet } from '@shared/extensions/interfaces'; import { autoCode, LangContainer, LangFile } from '@shared/lang'; import { memoizeOne } from '@shared/memoize'; -import { updatePreferencesData } from '@shared/preferences/util'; +import { updatePreferencesData, updatePreferencesDataAsync } from '@shared/preferences/util'; import { ITheme } from '@shared/ThemeFile'; import { deepCopy } from '@shared/Util'; import { formatString } from '@shared/utils/StringFormatter'; @@ -956,7 +956,7 @@ export class ConfigPage extends React.Component { - updatePreferencesData({ currentLogoSet: value }); + updatePreferencesDataAsync({ currentLogoSet: value }); } onCurrentThemeItemSelect = (value: string, index: number): void => { @@ -978,7 +978,7 @@ export class ConfigPage extends React.Component, send = true) { const preferences = window.Shared.preferences; // @TODO Figure out the delta change of the object tree, and only send the changes preferences.data = overwritePreferenceData(deepCopy(preferences.data), data); - if (preferences.onUpdate) { preferences.onUpdate(); } if (send) { sendPrefs(); } + if (preferences.onUpdate) { preferences.onUpdate(); } +} + +export async function updatePreferencesDataAsync(data: DeepPartial, send = true) { + const preferences = window.Shared.preferences; + // @TODO Figure out the delta change of the object tree, and only send the changes + preferences.data = overwritePreferenceData(deepCopy(preferences.data), data); + if (send) { + await sendPrefsAsync(); + } + if (preferences.onUpdate) { preferences.onUpdate(); } } const sendPrefs = delayedThrottle(() => { @@ -31,6 +41,15 @@ const sendPrefs = delayedThrottle(() => { ); }, 200); +const sendPrefsAsync = delayedThrottleAsync(async () => { + const preferences = window.Shared.preferences; + await window.Shared.back.request( + BackIn.UPDATE_PREFERENCES, + preferences.data, + false + ); +}, 200); + const { num, str } = Coerce; /** Default Preferences Data used for values that are not found in the file */ diff --git a/src/shared/utils/throttle.ts b/src/shared/utils/throttle.ts index 62fac529c..aada21021 100644 --- a/src/shared/utils/throttle.ts +++ b/src/shared/utils/throttle.ts @@ -4,6 +4,10 @@ import { AnyFunction, ArgumentTypesOf } from '../interfaces'; interface CallableCopy extends Function { (...args: ArgumentTypesOf): void; } +/** A callable object that has the same argument types as T (and Promise as the return type). */ +interface CallableCopyAsync extends Function { + (...args: ArgumentTypesOf): Promise; +} /** * Executes a callback immediately and starts a timer, only executes if timer is finished @@ -46,3 +50,27 @@ export function delayedThrottle(callback: T, time:number) }; return throttler; } + +/** + * Executes a callback after a `time` millisecond timer, only starting the timer if it doesn't exist + * @param callback Called when the timer ends + * @param time Time in milliseconds before calling + */ + export function delayedThrottleAsync(callback: T, time:number): CallableCopyAsync { + // Store timeout + let timeout: ReturnType | undefined; + // Function that receives and records the events + const throttler: CallableCopyAsync = async function(...args) { + return new Promise(resolve => { + // Check if currently throttling + if (timeout != undefined) { resolve(); } + // Release event after some time + timeout = setTimeout(async function() { + timeout = undefined; + await callback(...args); + resolve(); + }, time); + }); + }; + return throttler; +} From 8b126452ebaab00c0b416db3a485345f190d23cd Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sat, 28 May 2022 08:04:11 -0400 Subject: [PATCH 05/62] style: remove an extra space. --- src/shared/utils/throttle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/utils/throttle.ts b/src/shared/utils/throttle.ts index aada21021..207b9fc9e 100644 --- a/src/shared/utils/throttle.ts +++ b/src/shared/utils/throttle.ts @@ -56,7 +56,7 @@ export function delayedThrottle(callback: T, time:number) * @param callback Called when the timer ends * @param time Time in milliseconds before calling */ - export function delayedThrottleAsync(callback: T, time:number): CallableCopyAsync { +export function delayedThrottleAsync(callback: T, time:number): CallableCopyAsync { // Store timeout let timeout: ReturnType | undefined; // Function that receives and records the events From dbeff232de45f4e4f8c01198d83e929d5b4accd5 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sat, 28 May 2022 16:21:45 +0100 Subject: [PATCH 06/62] fix: delayedThrottleAsync properly exits test: Added throttle tests --- src/shared/utils/throttle.ts | 5 +++- tests/unit/back/throttle.test.ts | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/unit/back/throttle.test.ts diff --git a/src/shared/utils/throttle.ts b/src/shared/utils/throttle.ts index 207b9fc9e..1e574b8aa 100644 --- a/src/shared/utils/throttle.ts +++ b/src/shared/utils/throttle.ts @@ -63,7 +63,10 @@ export function delayedThrottleAsync(callback: T, time:nu const throttler: CallableCopyAsync = async function(...args) { return new Promise(resolve => { // Check if currently throttling - if (timeout != undefined) { resolve(); } + if (timeout != undefined) { + resolve(); + return; + } // Release event after some time timeout = setTimeout(async function() { timeout = undefined; diff --git a/tests/unit/back/throttle.test.ts b/tests/unit/back/throttle.test.ts new file mode 100644 index 000000000..dbd2b9c69 --- /dev/null +++ b/tests/unit/back/throttle.test.ts @@ -0,0 +1,49 @@ +import { delayedThrottle, delayedThrottleAsync, throttle } from '@shared/utils/throttle'; + +describe('Throttle Utils', () => { + beforeAll(async () => { + jest.useFakeTimers(); + jest.spyOn(global, 'setTimeout'); + }); + + it('throttle', () => { + const cb = jest.fn(); + const throttledFunc = throttle(cb, 100); + // First call + throttledFunc(); + expect(cb).toBeCalledTimes(1); + + // Second call - Too early, throttle + throttledFunc(); + expect(cb).toBeCalledTimes(1); + + // Throttle expired, will now run again + jest.runAllTimers(); + throttledFunc(); + expect(cb).toBeCalledTimes(2); + }); + + const delayedThrottleTestFactory = (delayedThrottle: (callback: () => any, time:number) => any) => { + return () => { + const cb = jest.fn(); + const delayedThrottleFunc = delayedThrottle(cb, 100); + + // First call, wait 100ms to run + delayedThrottleFunc(cb); + expect(cb).toBeCalledTimes(0); + jest.runAllTimers(); + expect(cb).toBeCalledTimes(1); + + // 2 calls at once, only latest should call, first ignored + delayedThrottleFunc(cb); + delayedThrottleFunc(cb); + expect(cb).toBeCalledTimes(1); + jest.runAllTimers(); + expect(cb).toBeCalledTimes(2); + }; + }; + + it('delayedThrottle', delayedThrottleTestFactory(delayedThrottle)); + + it('delayedThrottleAsync', delayedThrottleTestFactory(delayedThrottleAsync)); +}); From b9571badc89ae68b45bfc27444b857a5e3dac9c6 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sat, 28 May 2022 06:25:15 -0400 Subject: [PATCH 07/62] fix: make readOrCreateFile follow its description. refactor: turn a bunch of Promise functions into async's. Previously, onError was unused. --- src/shared/Util.ts | 103 +++++++--------------- src/shared/preferences/PreferencesFile.ts | 23 ++--- 2 files changed, 41 insertions(+), 85 deletions(-) diff --git a/src/shared/Util.ts b/src/shared/Util.ts index 302e4f693..9cb403023 100644 --- a/src/shared/Util.ts +++ b/src/shared/Util.ts @@ -6,7 +6,6 @@ import { DownloadDetails } from './back/types'; import { AppConfigData } from './config/interfaces'; import { parseVariableString } from './utils/VariableString'; import { throttle } from './utils/throttle'; -import ErrnoException = NodeJS.ErrnoException; const axios = axiosImport.default; @@ -22,20 +21,8 @@ type ReadFileOptions = { encoding?: BufferEncoding; flag?: string; } | BufferEnc * @param path Path of the JSON file. * @param options Options for reading the file. */ -export function readJsonFile(path: string, options?: ReadFileOptions): Promise { - return new Promise((resolve, reject) => { - fs.readFile(path, options, (error: ErrnoException | null, data: string | Buffer) => { - // Check if reading file failed - if (error) { return reject(error); } - // Try to parse json (and callback error if it fails) - try { - const jsonData = JSON.parse(data as string); - return resolve(jsonData); - } catch (error) { - return reject(error); - } - }); - }); +export async function readJsonFile(path: string, options?: ReadFileOptions): Promise { + return JSON.parse(await fs.promises.readFile(path, options) as string); } /** @@ -240,45 +227,27 @@ export async function recursiveDirectory(options: IRecursiveDirectoryOptions): P } async function innerRecursiveDirectory(shared: IRecursiveDirectorySharedObject, dirPath: string): Promise { - return new Promise(function(resolve, reject) { - // Full path to the current folder - const fullDirPath: string = path.join(shared.options.directoryPath, dirPath); - // Get the names of all files and sub-folders - fs.readdir(fullDirPath, function (err, files): void { - if (shared.abort) { return resolve(); } // (Abort exit point) - if (err) { reject(err); } - else { - // Resolve if folder is empty - if (files.length === 0) { return resolve(); } - // Get the stats of each folder/file to verify if they are a folder or file - // (And wait for every single one to complete before resolving the promise) - let filesOrFoldersLeft: number = files.length; - for (let i = files.length - 1; i >= 0; i--) { - const filename = files[i]; - fs.stat(path.join(fullDirPath, filename), async function(err, stats) { - if (shared.abort) { return resolve(); } // (Abort exit point) - if (err) { reject(err); } - else { - if (stats.isFile()) { - const p = shared.options.fileCallback({ - shared: shared, - filename: filename, - relativePath: dirPath, - }); - if (p) { await p; } - } else { - await innerRecursiveDirectory(shared, path.join(dirPath, filename)).catch(reject); - } - } - filesOrFoldersLeft -= 1; - if (filesOrFoldersLeft === 0) { - resolve(); - } - }); - } - } - }); - }); + // Full path to the current folder + const fullDirPath = path.join(shared.options.directoryPath, dirPath); + // Get the names of all files and sub-folders + const files = await fs.promises.readdir(fullDirPath); + if (shared.abort) { return; } + if (files.length === 0) { return; } + for (let i = files.length - 1; i >= 0; i--) { + // Get the stats of each folder/file to verify if they are a folder or file + const stats = await fs.promises.stat(path.join(fullDirPath, files[i])); + if (shared.abort) { return; } + if (stats.isFile()) { + const p = shared.options.fileCallback({ + shared: shared, + filename: files[i], + relativePath: dirPath, + }); + if (p) { await p; } + } else { + await innerRecursiveDirectory(shared, path.join(dirPath, files[i])); + } + } } export interface IRecursiveDirectoryOptions { @@ -406,21 +375,17 @@ export function fixSlashes(str: string): string { * Checks whether we can write and read to a folder * @param folder folder to check */ -export function canReadWrite(folder: string): Promise { - return new Promise((resolve) => { - const testPath = path.join(folder, 'test'); - fs.open(testPath, 'w', (err, fd) => { - if (err) { - resolve(false); - return; - } - // Cleanup file after testing - fs.close(fd, () => { - fs.promises.unlink(testPath); - }); - resolve(true); - }); - }); +export async function canReadWrite(folder: string): Promise { + const testPath = path.join(folder, 'test'); + try { + const fd = await fs.promises.open(testPath, 'w'); + // Cleanup file after testing + await fd.close(); + await fs.promises.unlink(testPath); + return true; + } catch { + return false; + } } // Courtesy of https://www.paulirish.com/2009/random-hex-color-code-snippets/ diff --git a/src/shared/preferences/PreferencesFile.ts b/src/shared/preferences/PreferencesFile.ts index 159f67d17..31ff31300 100644 --- a/src/shared/preferences/PreferencesFile.ts +++ b/src/shared/preferences/PreferencesFile.ts @@ -33,6 +33,7 @@ export namespace PreferencesFile { data = await readFile(filePath, onError) .catch((e) => { if (e.code !== 'ENOENT') { + if (onError) { onError(e); } throw e; } return undefined; @@ -47,23 +48,13 @@ export namespace PreferencesFile { return data; } - export function readFile(filePath: string, onError?: (error: string) => void): Promise { - return new Promise((resolve, reject) => { - readJsonFile(filePath, fileEncoding) - .then(json => resolve(overwritePreferenceData(deepCopy(defaultPreferencesData), json, onError))) - .catch(reject); - }); + export async function readFile(filePath: string, onError?: (error: string) => void): Promise { + const json = await readJsonFile(filePath, fileEncoding); + return overwritePreferenceData(deepCopy(defaultPreferencesData), json, onError); } - export function saveFile(filePath: string, data: AppPreferencesData): Promise { - return new Promise((resolve, reject) => { - // Convert preferences to json string - const json: string = stringifyJsonDataFile(data); - // Save the preferences file - fs.writeFile(filePath, json, function(error) { - if (error) { return reject(error); } - else { return resolve(); } - }); - }); + export async function saveFile(filePath: string, data: AppPreferencesData): Promise { + const json = stringifyJsonDataFile(data); + await fs.promises.writeFile(filePath, json); } } From cef916dd03fc2ace59d3d110ecc9019f362044be Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sat, 28 May 2022 16:42:00 +0100 Subject: [PATCH 08/62] chore: Bump version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 995a48dff..16b98b8eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flashpoint-launcher", - "version": "10.1.2", + "version": "10.1.3", "description": "A desktop application used to browse, manage and play games from BlueMaxima's Flashpoint", "main": "build/main/index.js", "config": { From 4286bcb337e13b90b4da3b14ae6b682923f92926 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sun, 29 May 2022 13:29:32 +0100 Subject: [PATCH 09/62] fix: Correct "No Game Selected" text per library --- lang/en.json | 5 ++++- src/renderer/components/RightBrowseSidebar.tsx | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lang/en.json b/lang/en.json index 30d6fd331..ec8c3636d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -276,7 +276,7 @@ "thumbnail": "Thumbnail", "screenshot": "Screenshot", "dropImageHere": "Drop an image here to add it", - "noGameSelected": "No Game Selected", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Click on a game to select it.", "deleteAdditionalApplication": "Delete Additional Application", "deleteGameAndAdditionalApps": "Delete Game (and Additional Applications)", @@ -506,10 +506,13 @@ }, "libraries": { "arcade": "Games", + "arcadeSingular": "Game", "arcadePlural": "All Games", "theatre": "Animations", + "theatreSingular": "Animation", "theatrePlural": "All Animations", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "All NG Animations" }, "upgrades": { diff --git a/src/renderer/components/RightBrowseSidebar.tsx b/src/renderer/components/RightBrowseSidebar.tsx index 115f712b7..411e4dd45 100644 --- a/src/renderer/components/RightBrowseSidebar.tsx +++ b/src/renderer/components/RightBrowseSidebar.tsx @@ -11,6 +11,7 @@ import { ModelUtils } from '@shared/game/util'; import { GamePropSuggestions, PickType, ProcessAction } from '@shared/interfaces'; import { LangContainer } from '@shared/lang'; import { deepCopy, generateTagFilterGroup, sizeToString } from '@shared/Util'; +import { formatString } from '@shared/utils/StringFormatter'; import { clipboard, Menu, MenuItemConstructorOptions } from 'electron'; import { GameData } from 'flashpoint-launcher'; import * as fs from 'fs'; @@ -725,7 +726,7 @@ export class RightBrowseSidebar extends React.Component -

{strings.noGameSelected}

+

{formatString(strings.noGameSelected, allStrings.libraries[this.props.currentLibrary + 'Singular'])}

{strings.clickToSelectGame}

); From e29fbf682d3efb59b591ff7e8495539781b1638f Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sun, 29 May 2022 20:41:20 +0100 Subject: [PATCH 10/62] feat: Logger Window --- lang/en.json | 3 +- package.json | 1 + src/back/responses.ts | 45 ++++- src/back/types.ts | 1 + src/main/LogsWindow.ts | 209 +++++++++++++++++++++ src/main/Main.ts | 19 +- src/main/index.ts | 6 +- src/main/types.ts | 2 + src/renderer/components/pages/LogsPage.tsx | 21 ++- src/renderer/index.tsx | 2 +- src/renderer/logger.tsx | 59 ++++++ src/shared/back/types.ts | 13 +- src/shared/lang.ts | 1 + webpack.config.js | 7 +- 14 files changed, 374 insertions(+), 15 deletions(-) create mode 100644 src/main/LogsWindow.ts create mode 100644 src/renderer/logger.tsx diff --git a/lang/en.json b/lang/en.json index ec8c3636d..39e903d52 100644 --- a/lang/en.json +++ b/lang/en.json @@ -113,7 +113,8 @@ "clearLog": "Clear Log", "copy404Urls": "Copy 404 URLs", "uploadLog": "Upload Log", - "copiedToClipboard": "Copied to Clipboard" + "copiedToClipboard": "Copied to Clipboard", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Home", diff --git a/package.json b/package.json index 16b98b8eb..99229ad73 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "scripts": { "start": "electron ./build/main/index.js", + "start-logger": "electron ./build/main/index.js logger=true", "build": "gulp build", "build-api-docs": "typedoc --out docs/api typings/flashpoint-launcher.d.ts --includeDeclarations --excludeExternals --disableOutputCheck", "watch": "gulp watch", diff --git a/src/back/responses.ts b/src/back/responses.ts index 9adbc2abe..c7842dbd3 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -81,6 +81,14 @@ export function registerRequestCallbacks(state: BackState): void { }; }); + state.socketServer.register(BackIn.GET_LOGGER_INIT_DATA, (event) => { + return { + preferences: state.preferences, + config: state.config, + log: state.log + }; + }); + state.socketServer.register(BackIn.GET_RENDERER_INIT_DATA, async (event) => { state.languageContainer = createContainer( state.languages, @@ -1114,6 +1122,41 @@ export function registerRequestCallbacks(state: BackState): void { } }); + state.socketServer.register(BackIn.OPEN_LOGS_WINDOW, async (event) => { + if (!state.services.has('logger_window')) { + const env = process.env; + if ('ELECTRON_RUN_AS_NODE' in env) { + delete env['ELECTRON_RUN_AS_NODE']; // If this flag is present, it will disable electron features from the process + } + const loggerArgs = [path.join(__dirname, '../main/index.js'), 'logger=true']; + const dirname = path.dirname(process.execPath); + runService( + state, + 'logger_window', + 'Logger Window', + '', + { + detached: false, + shell: true, + cwd: process.cwd(), + execFile: true, + env: env + }, + { + path: dirname, + filename: process.execPath, + arguments: escapeArgsForShell(loggerArgs), + kill: true + } + ); + } else { + const loggerService = state.services.get('logger_window'); + if (loggerService.getState() !== ProcessState.RUNNING) { + loggerService.restart(); + } + } + }); + state.socketServer.register(BackIn.UPLOAD_LOG, async (event) => { // Upload to log server const entries = state.log.filter(e => e !== undefined); @@ -1134,7 +1177,7 @@ export function registerRequestCallbacks(state: BackState): void { state.socketServer.register(BackIn.QUIT, async (event) => { // Unload all extensions before quitting await state.extensionsService.unloadAll(); - state.socketServer.send(event.client, BackOut.QUIT); + state.socketServer.broadcast(BackOut.QUIT); exit(state); }); diff --git a/src/back/types.ts b/src/back/types.ts index 76c71494f..5b6d91111 100644 --- a/src/back/types.ts +++ b/src/back/types.ts @@ -72,6 +72,7 @@ export type BackState = { extensionsService: ExtensionService; connection: Connection | undefined; prefsQueue: EventQueue; + logsWindowProc?: ManagedChildProcess; } export type BackQueryChache = { diff --git a/src/main/LogsWindow.ts b/src/main/LogsWindow.ts new file mode 100644 index 000000000..f0c0c4ace --- /dev/null +++ b/src/main/LogsWindow.ts @@ -0,0 +1,209 @@ +import { ConfigFile } from '@back/ConfigFile'; +import { CONFIG_FILENAME, PREFERENCES_FILENAME } from '@back/constants'; +import * as remoteMain from '@electron/remote/main'; +import { SocketClient } from '@shared/back/SocketClient'; +import { BackOut } from '@shared/back/types'; +import { AppConfigData } from '@shared/config/interfaces'; +import { InitRendererChannel, InitRendererData } from '@shared/IPC'; +import { AppPreferencesData } from '@shared/preferences/interfaces'; +import { PreferencesFile } from '@shared/preferences/PreferencesFile'; +import { createErrorProxy } from '@shared/Util'; +import { randomBytes } from 'crypto'; +import { app, BrowserWindow, ipcMain, IpcMainEvent, session, shell } from 'electron'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import * as WebSocket from 'ws'; +import { Init } from './types'; +import { getMainFolderPath } from './Util'; + +const TIMEOUT_DELAY = 60_000; + +type State = { + window?: BrowserWindow; + entry: string; + _secret: string; + url: string; + backHost: URL; + socket: SocketClient; + mainFolderPath: string; + config: AppConfigData; + isQuitting: boolean; + prefs: AppPreferencesData; +} + +export async function startLogger(init: Init): Promise { + const state: State = { + window: undefined, + url: path.join(__dirname, '../window/images/icon.png'), + entry: init.rest, + backHost: init.args['connect-remote'] ? new URL('ws://'+init.args['connect-remote']) : new URL('ws://localhost'), + socket: new SocketClient(WebSocket), + _secret: '', + mainFolderPath: createErrorProxy('mainFolderPath'), + config: createErrorProxy('config'), + prefs: createErrorProxy('prefs'), + isQuitting: false + }; + + function onInit(event: IpcMainEvent) { + const data: InitRendererData = { + isBackRemote: true, + installed: false, + version: 0, + host: state.backHost.href, + secret: state._secret, + }; + event.returnValue = data; + } + + await startup(); + + // -- Functions -- + + async function startup() { + app.once('ready', onAppReady); + app.once('window-all-closed', onAppWindowAllClosed); + app.once('web-contents-created', onAppWebContentsCreated); + app.on('activate', onAppActivate); + + state.backHost.port = '12001'; + + const installed = fs.existsSync('./.installed'); + state.mainFolderPath = getMainFolderPath(installed); + state.config = ConfigFile.readOrCreateFileSync(path.join(state.mainFolderPath, CONFIG_FILENAME)); + state.prefs = PreferencesFile.readOrCreateFileSync(path.join(state.config.flashpointPath, PREFERENCES_FILENAME)); + + // Get secret to connect to backend + const secretFilePath = path.join(state.mainFolderPath, 'secret.txt'); + try { + state._secret = await fs.readFile(secretFilePath, { encoding: 'utf8' }); + } catch (e) { + state._secret = randomBytes(2048).toString('hex'); + try { + await fs.writeFile(secretFilePath, state._secret, { encoding: 'utf8' }); + } catch (e) { + console.warn(`Failed to save new secret to disk.\n${e}`); + } + } + + // Connect to back process + await timeout(new Promise((resolve, reject) => { + console.log('secret: ' + state._secret); + const sock = new WebSocket(state.backHost.href); + sock.onclose = () => { reject(new Error('Failed to authenticate connection to back.')); }; + sock.onerror = (event: any) => { reject(event.error); }; + sock.onopen = () => { + sock.onmessage = () => { + sock.onclose = noop; + sock.onerror = noop; + resolve(sock); + }; + sock.send(state._secret); + }; + }), TIMEOUT_DELAY) + .then(ws => state.socket.setSocket(ws)) + .then(() => { + // Add Socket event listener(s) + state.socket.register(BackOut.QUIT, () => { + state.isQuitting = true; + app.quit(); + }); + }) + .then(() => app.whenReady()) + .then(() => { + if (!state.window) { + state.window = createBrowserWindow(); + } + }); + + // Add IPC event listener(s) + ipcMain.on(InitRendererChannel, onInit); + } + + function onAppReady(): void { + if (!session.defaultSession) { throw new Error('Default session is missing!'); } + + // Reject all permission requests since we don't need any permissions. + session.defaultSession.setPermissionRequestHandler((webContents, permission, callback) => callback(false)); + + session.defaultSession.webRequest.onHeadersReceived((details, callback) => { + callback({ ...details.responseHeaders }); + }); + } + + function onAppWindowAllClosed(): void { + // On OS X it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + app.quit(); + } + } + + function onAppWebContentsCreated(event: Electron.Event, webContents: Electron.WebContents): void { + // Open links to web pages in the OS-es default browser + // (instead of navigating to it with the electron window that opened it) + webContents.on('will-navigate', onNewPage); + webContents.on('new-window', onNewPage); + + function onNewPage(event: Electron.Event, navigationUrl: string): void { + event.preventDefault(); + shell.openExternal(navigationUrl); + } + } + + function onAppActivate(): void { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (!state.window) { state.window = createBrowserWindow(); } + } + + function createBrowserWindow(): BrowserWindow { + const window = new BrowserWindow({ + title: 'Flashpoint Logger', + icon: path.join(__dirname, '../window/images/icon.png'), + useContentSize: true, + width: init.args.width, + height: init.args.height, + webPreferences: { + preload: path.resolve(__dirname, './MainWindowPreload.js'), + nodeIntegration: true, + contextIsolation: false + }, + }); + remoteMain.enable(window.webContents); + window.setMenu(null); // Remove the menu bar + window.loadFile(path.join(__dirname, '../window/logger.html')); + + window.webContents.openDevTools(); + + window.on('closed', () => { + if (state.window === window) { + state.window = undefined; + } + }); + + return window; + } + + /** + * Resolves/Rejects when the wrapped promise does. Rejects if the timeout happens before that. + * @param promise Promise to wrap. + * @param ms Time to wait before timing out (in ms). + */ + function timeout(promise: Promise, ms: number): Promise { + return new Promise((resolve, reject) => { + const handle = setTimeout(() => { + reject(new Error(`Timeout (${(ms / 1000).toFixed(1)} seconds).`)); + }, ms); + promise.then(arg => { + clearTimeout(handle); + resolve(arg); + }).catch(error => { + clearTimeout(handle); + reject(error); + }); + }); + } + + function noop() { /* Do nothing. */ } +} diff --git a/src/main/Main.ts b/src/main/Main.ts index 252ba77cf..54b51012a 100644 --- a/src/main/Main.ts +++ b/src/main/Main.ts @@ -10,16 +10,12 @@ import { createErrorProxy } from '@shared/Util'; import { ChildProcess, fork } from 'child_process'; import { randomBytes } from 'crypto'; import { app, BrowserWindow, dialog, ipcMain, IpcMainEvent, session, shell, WebContents } from 'electron'; -import * as fs from 'fs'; +import * as fs from 'fs-extra'; import * as path from 'path'; -import { promisify } from 'util'; import * as WebSocket from 'ws'; import { Init } from './types'; import * as Util from './Util'; -const readFile = promisify(fs.readFile); -const writeFile = promisify(fs.writeFile); - const TIMEOUT_DELAY = 60_000; const ALLOWED_HOSTS = [ @@ -100,6 +96,9 @@ export function main(init: Init): void { app.commandLine.appendSwitch('ignore-connections-limit', 'localhost'); + state.mainFolderPath = Util.getMainFolderPath(state._installed); + console.log(path.join(state.mainFolderPath, 'secret.txt')); + // ---- Initialize ---- // Check if installed let p = exists('./.installed') @@ -121,17 +120,23 @@ export function main(init: Init): void { if (init.args['connect-remote'] || init.args['host-remote'] || init.args['back-only']) { const secretFilePath = path.join(state.mainFolderPath, 'secret.txt'); try { - state._secret = await readFile(secretFilePath, { encoding: 'utf8' }); + state._secret = await fs.readFile(secretFilePath, { encoding: 'utf8' }); } catch (e) { state._secret = randomBytes(2048).toString('hex'); try { - await writeFile(secretFilePath, state._secret, { encoding: 'utf8' }); + await fs.writeFile(secretFilePath, state._secret, { encoding: 'utf8' }); } catch (e) { console.warn(`Failed to save new secret to disk.\n${e}`); } } } else { + const secretFilePath = path.join(state.mainFolderPath, 'secret.txt'); state._secret = randomBytes(2048).toString('hex'); + try { + await fs.writeFile(secretFilePath, state._secret, { encoding: 'utf8' }); + } catch (e) { + console.warn(`Failed to save new secret to disk.\n${e}`); + } } }); // Start back process diff --git a/src/main/index.ts b/src/main/index.ts index 6b40d60f5..ef4f9e1e3 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,6 +1,7 @@ import * as remoteMain from '@electron/remote/main'; import { Coerce } from '@shared/utils/Coerce'; import { startBrowserMode } from './BrowserMode'; +import { startLogger } from './LogsWindow'; import { main } from './Main'; import { Init } from './types'; @@ -8,7 +9,9 @@ remoteMain.initialize(); const init = getArgs(); -if (init.args['browser_mode']) { +if (init.args['logger']) { + startLogger(init); +} else if (init.args['browser_mode']) { startBrowserMode(init); } else { main(init); @@ -36,6 +39,7 @@ function getArgs(): Init { lastArgIndex = i; break; // Boolean value + case 'logger': case 'host-remote': case 'back-only': case 'browser_mode': diff --git a/src/main/types.ts b/src/main/types.ts index 3739e68ad..c00c3e3b5 100644 --- a/src/main/types.ts +++ b/src/main/types.ts @@ -5,6 +5,8 @@ export type InitArgs = Partial<{ 'back-only': boolean; // Browser mode + /** If the application should start in "logger mode" */ + 'logger': boolean; /** If the application should start in "browser mode". */ 'browser_mode': boolean; /** URL that browser mode should open */ diff --git a/src/renderer/components/pages/LogsPage.tsx b/src/renderer/components/pages/LogsPage.tsx index fc16a85a4..c934543e7 100644 --- a/src/renderer/components/pages/LogsPage.tsx +++ b/src/renderer/components/pages/LogsPage.tsx @@ -14,7 +14,9 @@ import { LangContext } from '../../util/lang'; import { Dropdown } from '../Dropdown'; import { LogData } from '../LogData'; -type OwnProps = {}; +type OwnProps = { + isLogsWindow?: boolean; +}; export type LogsPageProps = OwnProps & WithPreferencesProps; @@ -132,6 +134,19 @@ export class LogsPage extends React.Component {
+ {/* Create Logs Window Button */} + { !this.props.isLogsWindow && ( +
+
+ +
+
+ )} + {/* Upload Logs Button */}
@@ -216,6 +231,10 @@ export class LogsPage extends React.Component { clipboard.writeText(urls.join('\n')); } + onOpenLogsWindowClick = async (): Promise => { + window.Shared.back.send(BackIn.OPEN_LOGS_WINDOW); + } + onUploadClick = async (): Promise => { this.setState({ uploading: true }); const strings = this.context; diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index 808e781b8..92eae375e 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -58,7 +58,7 @@ import { logFactory } from './util/logging'; ), document.getElementById('root')); })(); -function createInitialMainState(): MainState { +export function createInitialMainState(): MainState { const preferencesData = window.Shared.preferences.data; // Prepare libraries diff --git a/src/renderer/logger.tsx b/src/renderer/logger.tsx new file mode 100644 index 000000000..234059ebb --- /dev/null +++ b/src/renderer/logger.tsx @@ -0,0 +1,59 @@ +import { BackOut } from '@shared/back/types'; +import { LogLevel } from '@shared/Log/interface'; +import { ConnectedRouter } from 'connected-react-router'; +import { createMemoryHistory } from 'history'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { Provider } from 'react-redux'; +import { createInitialMainState } from '.'; +import configureStore from './configureStore'; +import { ConnectedLogsPage } from './containers/ConnectedLogsPage'; +import { PreferencesContextProvider } from './context/PreferencesContext'; +import { LangContext } from './util/lang'; +import { logFactory } from './util/logging'; + +(async () => { + window.log = { + trace: logFactory(LogLevel.TRACE, window.Shared.back), + debug: logFactory(LogLevel.DEBUG, window.Shared.back), + info: logFactory(LogLevel.INFO, window.Shared.back), + warn: logFactory(LogLevel.WARN, window.Shared.back), + error: logFactory(LogLevel.ERROR, window.Shared.back) + }; + // Toggle DevTools when CTRL+SHIFT+I is pressed + window.addEventListener('keypress', (event) => { + if (event.ctrlKey && event.shiftKey && event.code === 'KeyI') { + window.Shared.toggleDevtools(); + event.preventDefault(); + } + }); + + // Wait for the preferences and config to initialize + await window.Shared.waitUntilInitialized(); + + // Create history + const history = createMemoryHistory(); + + // Create Redux store + const store = configureStore(history, { + main: createInitialMainState(), + }); + + // Connect to backend + window.Shared.back.register(BackOut.LOG_ENTRY_ADDED, (event, entry, index) => { + window.Shared.log.entries[index - window.Shared.log.offset] = entry; + }); + + // Render the application + ReactDOM.render(( + + + + + + + + + + ), document.getElementById('root')); +})(); diff --git a/src/shared/back/types.ts b/src/shared/back/types.ts index 122f1ed90..581798b68 100644 --- a/src/shared/back/types.ts +++ b/src/shared/back/types.ts @@ -103,8 +103,10 @@ export enum BackIn { BROWSE_VIEW_KEYSET, /** Get all data needed on init (by the renderer). */ GET_RENDERER_INIT_DATA, - /** Get all data needed on init (by the renderer). */ + /** Get all data needed on init (by the main process). */ GET_MAIN_INIT_DATA, + /** Get all data needed on init (by the independent logger window) */ + GET_LOGGER_INIT_DATA, /** Update any number of configs. */ UPDATE_CONFIG, /** Update any number of preferences. */ @@ -121,6 +123,7 @@ export enum BackIn { RUN_COMMAND, // Misc + OPEN_LOGS_WINDOW, UPLOAD_LOG, SET_EXT_CONFIG_VALUE, } @@ -251,6 +254,7 @@ export type BackInTemplate = SocketTemplate number; [BackIn.BROWSE_VIEW_KEYSET]: (library: string, query: SearchGamesOpts) => BrowseViewKeysetResponse; + [BackIn.GET_LOGGER_INIT_DATA]: () => GetLoggerInitDataResponse; [BackIn.GET_RENDERER_INIT_DATA]: () => GetRendererInitDataResponse; [BackIn.GET_MAIN_INIT_DATA]: () => GetMainInitDataResponse; [BackIn.UPDATE_CONFIG]: (data: Partial) => void; @@ -267,6 +271,7 @@ export type BackInTemplate = SocketTemplate RunCommandResponse; // Misc + [BackIn.OPEN_LOGS_WINDOW]: () => void; [BackIn.UPLOAD_LOG]: () => string | undefined; [BackIn.SET_EXT_CONFIG_VALUE]: (key: string, value: any) => void; }> @@ -349,6 +354,12 @@ export type GetMainInitDataResponse = { preferences: AppPreferencesData; } +export type GetLoggerInitDataResponse = { + config: AppConfigData; + preferences: AppPreferencesData; + log: ILogEntry[]; +} + export type GetRendererInitDataResponse = { config: AppConfigData; preferences: AppPreferencesData; diff --git a/src/shared/lang.ts b/src/shared/lang.ts index dc0354671..941694a3c 100644 --- a/src/shared/lang.ts +++ b/src/shared/lang.ts @@ -121,6 +121,7 @@ const langTemplate = { 'copy404Urls', 'uploadLog', 'copiedToClipboard', + 'openLogsWindow', ] as const, app: [ 'home', diff --git a/webpack.config.js b/webpack.config.js index c6bd21a51..a4b2fad7e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,9 +2,12 @@ const path = require('path'); const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); module.exports = { - entry: './src/renderer/index.tsx', + entry: { + main: './src/renderer/index.tsx', + logger: './src/renderer/logger.tsx' + }, output: { - filename: 'main.bundle.js', + filename: '[name].bundle.js', path: path.resolve(__dirname, './build/window') }, // Allows importing build-in node modules in electron render. From ae2546456e6aa14cda3d2d316b97f791a15aeced Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Mon, 30 May 2022 08:33:20 +0100 Subject: [PATCH 11/62] fix: Browser Mode + Logger paths --- package.json | 2 +- src/back/GameLauncher.ts | 6 +++--- src/back/responses.ts | 4 ++-- src/back/util/misc.ts | 4 ++++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 99229ad73..09a93d775 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build-api-docs": "typedoc --out docs/api typings/flashpoint-launcher.d.ts --includeDeclarations --excludeExternals --disableOutputCheck", "watch": "gulp watch", "pack": "gulp pack", - "snapshot": "cross-env-shell NODE_ENV=production \"npm run build && npm run pack\"", + "snapshot": "cross-env-shell PACK_PLATFORM=win32 NODE_ENV=production \"npm run build && npm run pack\"", "test": "jest", "lint": "eslint ./src/**/*.{ts,tsx} ./gulpfile.js", "pack:linux": "cross-env PACK_PLATFORM=linux PACK_ARCH=x64 gulp pack", diff --git a/src/back/GameLauncher.ts b/src/back/GameLauncher.ts index bcebb81ea..9257ca3a5 100644 --- a/src/back/GameLauncher.ts +++ b/src/back/GameLauncher.ts @@ -11,7 +11,7 @@ import { AppPathOverride, GameData, ManagedChildProcess } from 'flashpoint-launc import * as path from 'path'; import { ApiEmitter } from './extensions/ApiEmitter'; import { OpenExternalFunc, ShowMessageBoxFunc } from './types'; -import { isBrowserOpts } from './util/misc'; +import { getCwd, isBrowserOpts } from './util/misc'; import * as GameDataManager from '@back/game/GameDataManager'; const { str } = Coerce; @@ -187,7 +187,7 @@ export namespace GameLauncher { gameArgs: browserLaunchArgs, useWine: false, env, - cwd: process.cwd(), + cwd: getCwd(opts.isDev, opts.exePath), execFile: true } }; @@ -230,7 +230,7 @@ export namespace GameLauncher { gameArgs: [path.join(__dirname, '../main/index.js'), 'browser_mode=true', `browser_url=${path.join(__dirname, '../window/flash_index.html')}?data=${encodeURI(opts.game.launchCommand)}`], useWine: false, env, - cwd: process.cwd(), + cwd: getCwd(opts.isDev, opts.exePath), execFile: true } }; diff --git a/src/back/responses.ts b/src/back/responses.ts index c7842dbd3..b485bc3e7 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -39,7 +39,7 @@ import { ManagedChildProcess } from './ManagedChildProcess'; import { importAllMetaEdits } from './MetaEdit'; import { BackState, BareTag, TagsFile } from './types'; import { pathToBluezip } from './util/Bluezip'; -import { copyError, createAddAppFromLegacy, createContainer, createGameFromLegacy, createPlaylistFromJson, exit, pathExists, procToService, removeService, runService } from './util/misc'; +import { copyError, createAddAppFromLegacy, createContainer, createGameFromLegacy, createPlaylistFromJson, exit, getCwd, pathExists, procToService, removeService, runService } from './util/misc'; import { sanitizeFilename } from './util/sanitizeFilename'; import { uuid } from './util/uuid'; @@ -1138,7 +1138,7 @@ export function registerRequestCallbacks(state: BackState): void { { detached: false, shell: true, - cwd: process.cwd(), + cwd: getCwd(state.isDev, state.exePath), execFile: true, env: env }, diff --git a/src/back/util/misc.ts b/src/back/util/misc.ts index fd8041b3a..cf3367e86 100644 --- a/src/back/util/misc.ts +++ b/src/back/util/misc.ts @@ -333,3 +333,7 @@ export function isBrowserOpts(val: any): val is BrowserApplicationOpts { return typeof val.url === 'string' && (val.proxy === undefined || typeof val.proxy === 'string'); } + +export function getCwd(isDev: boolean, exePath: string) { + return isDev ? process.cwd() : process.platform == 'darwin' ? path.resolve(path.dirname(exePath), '..') : path.dirname(exePath); +} From 2cd4fb4566e7bd92a82f8afdb2822f2823212010 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Tue, 31 May 2022 18:15:53 +0100 Subject: [PATCH 12/62] fix: Add logger.html --- static/window/logger.html | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 static/window/logger.html diff --git a/static/window/logger.html b/static/window/logger.html new file mode 100644 index 000000000..b8026b942 --- /dev/null +++ b/static/window/logger.html @@ -0,0 +1,15 @@ + + + + + + + + + + + +
+ + + From d054b75019f13527082e14b5085f9741e30ec271 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 9 Jun 2022 16:05:06 -0400 Subject: [PATCH 13/62] Merge redundant `shell` and `execFile` arguments. Create a new parameter, `noshell`, which is negated to get the value of the `shell` parameter given to `spawn()`. Make spawning use `spawn(file, args, {'shell': false})` instead of `execFile(file, args)`. `spawn` gives a stdio stream, whereas `execFile` waits for the subprocess to exit, and then gives all the output. --- src/back/GameLauncher.ts | 6 ++--- src/back/ManagedChildProcess.ts | 46 +++++++++++++------------------- src/back/responses.ts | 9 +++---- typings/flashpoint-launcher.d.ts | 2 +- 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/src/back/GameLauncher.ts b/src/back/GameLauncher.ts index bcebb81ea..73de1aa45 100644 --- a/src/back/GameLauncher.ts +++ b/src/back/GameLauncher.ts @@ -38,7 +38,7 @@ export type LaunchInfo = { useWine: boolean; env: NodeJS.ProcessEnv; cwd?: string; - execFile?: boolean; + noshell?: boolean; } type LaunchBaseOpts = { @@ -188,7 +188,7 @@ export namespace GameLauncher { useWine: false, env, cwd: process.cwd(), - execFile: true + noshell: true } }; onWillEvent.fire(gameLaunchInfo) @@ -231,7 +231,7 @@ export namespace GameLauncher { useWine: false, env, cwd: process.cwd(), - execFile: true + noshell: true } }; onWillEvent.fire(gameLaunchInfo) diff --git a/src/back/ManagedChildProcess.ts b/src/back/ManagedChildProcess.ts index 1d0393b35..7b8a7a15b 100644 --- a/src/back/ManagedChildProcess.ts +++ b/src/back/ManagedChildProcess.ts @@ -1,7 +1,7 @@ import { IBackProcessInfo, INamedBackProcessInfo, ProcessState } from '@shared/interfaces'; import { ILogPreEntry } from '@shared/Log/interface'; import { Coerce } from '@shared/utils/Coerce'; -import { ChildProcess, execFile, spawn } from 'child_process'; +import { ChildProcess, spawn } from 'child_process'; import { EventEmitter } from 'events'; import * as flashpoint from 'flashpoint-launcher'; import { readFileSync } from 'fs'; @@ -32,9 +32,8 @@ export interface ManagedChildProcess { export type ProcessOpts = { detached?: boolean; autoRestart?: boolean; - shell?: boolean; + noshell?: boolean; cwd?: string; - execFile?: boolean; env?: NodeJS.ProcessEnv; } @@ -65,8 +64,6 @@ export class ManagedChildProcess extends EventEmitter { private autoRestartCount: number; /** Whether to run in a shell */ private shell: boolean; - /** Whether to use execFile instead of spawn */ - private execFile: boolean; /** Launch with these Environmental Variables */ private env?: NodeJS.ProcessEnv; /** A timestamp of when the process was started. */ @@ -76,7 +73,7 @@ export class ManagedChildProcess extends EventEmitter { constructor(id: string, name: string, cwd: string, opts: ProcessOpts, info: INamedBackProcessInfo | IBackProcessInfo) { super(); - const { detached, autoRestart, shell, execFile, env } = opts; + const { detached, autoRestart, noshell, env } = opts; this.id = id; this.name = name; this.cwd = cwd; @@ -84,8 +81,7 @@ export class ManagedChildProcess extends EventEmitter { this.autoRestart = !!autoRestart; this.autoRestartCount = 0; this.info = info; - this.shell = !!shell; - this.execFile = !!execFile; + this.shell = !noshell; this.env = env; } @@ -112,28 +108,24 @@ export class ManagedChildProcess extends EventEmitter { this.autoRestartCount = 0; } // Spawn process - if (this.execFile) { - this.process = execFile(this.info.filename, this.info.arguments, { cwd: this.cwd, env: this.env }); - } else { - if (process.platform == 'darwin') { - if (this.env === undefined) { - this.env = {}; - } - if (this.env.PATH === undefined) { - this.env.PATH = ''; - } - const pathArr: string[] = this.env.PATH.split(':'); - // HACK: manually read in /etc/paths to PATH. Needs to be done on Mac, because otherwise - // the brew path won't be found. - for (const entry of readFileSync('/etc/paths').toString().split('\n')) { - if (entry != '' && !pathArr.includes(entry)) { - pathArr.push(entry); - } + if (process.platform == 'darwin') { + if (this.env === undefined) { + this.env = {}; + } + if (this.env.PATH === undefined) { + this.env.PATH = ''; + } + const pathArr: string[] = this.env.PATH.split(':'); + // HACK: manually read in /etc/paths to PATH. Needs to be done on Mac, because otherwise + // the brew path won't be found. + for (const entry of readFileSync('/etc/paths').toString().split('\n')) { + if (entry != '' && !pathArr.includes(entry)) { + pathArr.push(entry); } - this.env.PATH = pathArr.join(':'); } - this.process = spawn(this.info.filename, this.info.arguments, { cwd: this.cwd, detached: this.detached, shell: this.shell , env: this.env}); + this.env.PATH = pathArr.join(':'); } + this.process = spawn(this.info.filename, this.info.arguments, { cwd: this.cwd, detached: this.detached, shell: this.shell , env: this.env}); // Set start timestamp this.startTime = Date.now(); // Log diff --git a/src/back/responses.ts b/src/back/responses.ts index 9adbc2abe..47c82549f 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -1325,14 +1325,13 @@ function runGameFactory(state: BackState) { '', { detached: false, - shell: true, cwd: gameLaunchInfo.launchInfo.cwd, - execFile: !!gameLaunchInfo.launchInfo.execFile, + noshell: !!gameLaunchInfo.launchInfo.noshell, env: gameLaunchInfo.launchInfo.env }, { path: dirname, - filename: createCommand(gameLaunchInfo.launchInfo.gamePath, gameLaunchInfo.launchInfo.useWine, !!gameLaunchInfo.launchInfo.execFile), + filename: createCommand(gameLaunchInfo.launchInfo.gamePath, gameLaunchInfo.launchInfo.useWine, !!gameLaunchInfo.launchInfo.noshell), arguments: escapeArgsForShell(gameLaunchInfo.launchInfo.gameArgs), kill: true } @@ -1347,13 +1346,13 @@ function runGameFactory(state: BackState) { }; } -function createCommand(filename: string, useWine: boolean, execFile: boolean): string { +function createCommand(filename: string, useWine: boolean, noshell: boolean): string { // This whole escaping thing is horribly broken. We probably want to switch // to an array representing the argv instead and not have a shell // in between. switch (process.platform) { case 'win32': - return execFile ? filename : `"${filename}"`; // Quotes cause issues with execFile + return noshell ? filename : `"${filename}"`; // Quotes cause issues without a shell. case 'darwin': case 'linux': if (useWine) { diff --git a/typings/flashpoint-launcher.d.ts b/typings/flashpoint-launcher.d.ts index ec8c2c11c..44987ffc9 100644 --- a/typings/flashpoint-launcher.d.ts +++ b/typings/flashpoint-launcher.d.ts @@ -967,7 +967,7 @@ declare module 'flashpoint-launcher' { type ProcessOpts = { detached?: boolean; autoRestart?: boolean; - shell?: boolean; + noshell?: boolean; }; type ServiceChange = { From 8a955cff62be34756e6b001b37e2228dc69aacb5 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 9 Jun 2022 18:49:30 -0400 Subject: [PATCH 14/62] Fix the behavior of createCommand on mac and linux Also don't escape args for a shell when we don't use a shell. --- src/back/responses.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/back/responses.ts b/src/back/responses.ts index 47c82549f..064c0b606 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -1332,7 +1332,12 @@ function runGameFactory(state: BackState) { { path: dirname, filename: createCommand(gameLaunchInfo.launchInfo.gamePath, gameLaunchInfo.launchInfo.useWine, !!gameLaunchInfo.launchInfo.noshell), - arguments: escapeArgsForShell(gameLaunchInfo.launchInfo.gameArgs), + // Don't escape args if we're not using a shell. + arguments: gameLaunchInfo.launchInfo.noshell + ? typeof gameLaunchInfo.launchInfo.gameArgs == "string" + ? [gameLaunchInfo.launchInfo.gameArgs] + : gameLaunchInfo.launchInfo.gameArgs + : escapeArgsForShell(gameLaunchInfo.launchInfo.gameArgs), kill: true } ); @@ -1358,7 +1363,7 @@ function createCommand(filename: string, useWine: boolean, noshell: boolean): st if (useWine) { return `wine start /wait /unix "${filename}"`; } - return `"${filename}"`; + return noshell ? filename : `"${filename}"`; default: throw Error('Unsupported platform'); } From 4a0984602d0040ea84fefd4c2f1b2b12b21cfeb3 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 9 Jun 2022 18:58:52 -0400 Subject: [PATCH 15/62] Make the linter happy. --- src/back/responses.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/back/responses.ts b/src/back/responses.ts index 064c0b606..5e4dd2fac 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -1333,10 +1333,10 @@ function runGameFactory(state: BackState) { path: dirname, filename: createCommand(gameLaunchInfo.launchInfo.gamePath, gameLaunchInfo.launchInfo.useWine, !!gameLaunchInfo.launchInfo.noshell), // Don't escape args if we're not using a shell. - arguments: gameLaunchInfo.launchInfo.noshell - ? typeof gameLaunchInfo.launchInfo.gameArgs == "string" - ? [gameLaunchInfo.launchInfo.gameArgs] - : gameLaunchInfo.launchInfo.gameArgs + arguments: gameLaunchInfo.launchInfo.noshell + ? typeof gameLaunchInfo.launchInfo.gameArgs == 'string' + ? [gameLaunchInfo.launchInfo.gameArgs] + : gameLaunchInfo.launchInfo.gameArgs : escapeArgsForShell(gameLaunchInfo.launchInfo.gameArgs), kill: true } From ccdb5904765a8a022d4ea9c104de81916d84d069 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sat, 11 Jun 2022 13:39:59 +0100 Subject: [PATCH 16/62] feat: Protocol basics (Run game) --- gulpfile.js | 8 +++++++- src/main/Main.ts | 25 ++++++++++++++++++++++++- src/renderer/app.tsx | 19 +++++++++++++++++++ src/shared/back/types.ts | 2 ++ src/shared/interfaces.ts | 2 ++ 5 files changed, 54 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 7aad170cc..52c2ad359 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -73,7 +73,13 @@ const extraOptions = { }, mac: { target: ['dmg', '7z'], - icon: './icons/icon.icns' + icon: './icons/icon.icns', + protocols: { + name: 'flashpoint-protocol', + schemes: [ + 'flashpoint' + ] + } }, linux: { target: ['deb', '7z'], diff --git a/src/main/Main.ts b/src/main/Main.ts index 54b51012a..457dc006e 100644 --- a/src/main/Main.ts +++ b/src/main/Main.ts @@ -68,6 +68,15 @@ export function main(init: Init): void { // -- Functions -- function startup() { + // Register flashpoint:// protocol + if (process.defaultApp) { + if (process.argv.length >= 2) { + app.setAsDefaultProtocolClient('flashpoint', process.execPath, [path.resolve(process.argv[1])]); + } + } else { + app.setAsDefaultProtocolClient('flashpoint'); + } + app.disableHardwareAcceleration(); // Single process @@ -84,6 +93,7 @@ export function main(init: Init): void { app.once('web-contents-created', onAppWebContentsCreated); app.on('activate', onAppActivate); app.on('second-instance', onAppSecondInstance); + app.on('open-url', onAppOpenUrl); // Add IPC event listener(s) ipcMain.on(InitRendererChannel, onInit); @@ -142,7 +152,8 @@ export function main(init: Init): void { // Start back process if (!init.args['connect-remote']) { p = p.then(() => new Promise((resolve, reject) => { - state.backProc = fork(path.join(__dirname, '../back/index.js'), undefined, { detached: true }); + // Fork backend, init.rest will contain possible flashpoint:// message + state.backProc = fork(path.join(__dirname, '../back/index.js'), [init.rest], { detached: true }); // Wait for process to initialize state.backProc.once('message', (message: any) => { if (message.port) { @@ -328,6 +339,13 @@ export function main(init: Init): void { function onAppSecondInstance(event: Electron.Event, argv: string[], workingDirectory: string): void { if (state.window) { + if (process.platform !== 'darwin') { + // Find the arg that is our custom protocol url and store it + const url = argv.find((arg) => arg.startsWith('flashpoint://')); + if (state.window.webContents) { + state.window.webContents.send(WindowIPC.PROTOCOL, url); + } + } // Focus the window // (this is a hacky work around because focusing is kinda broken in win10, see https://github.com/electron/electron/issues/2867 ) state.window.setAlwaysOnTop(true); @@ -337,6 +355,11 @@ export function main(init: Init): void { } } + function onAppOpenUrl(event: Electron.Event, url: string) { + event.preventDefault(); + dialog.showErrorBox('Welcome Back', `Mac - You arrived from: ${url}`); + } + function onInit(event: IpcMainEvent) { const data: InitRendererData = { isBackRemote: !!init.args['connect-remote'], diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index a120563d5..adfb6eeca 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -32,6 +32,7 @@ import HeaderContainer from './containers/HeaderContainer'; import { WithMainStateProps } from './containers/withMainState'; import { WithPreferencesProps } from './containers/withPreferences'; import { WithTagCategoriesProps } from './containers/withTagCategories'; +import { newProgress } from './context/ProgressContext'; import { CreditsFile } from './credits/CreditsFile'; import { UpdateView, UpgradeStageState } from './interfaces'; import { Paths } from './Paths'; @@ -122,6 +123,24 @@ export class App extends React.Component { ipcRenderer.on(WindowIPC.WINDOW_MAXIMIZE, (sender, isMaximized: boolean) => { updatePreferencesData({ mainWindow: { maximized: isMaximized } }); }); + ipcRenderer.on(WindowIPC.PROTOCOL, (sender, url: string) => { + const parts = url.split('/'); + if (parts.length > 2) { + // remove "flashpoint:" and "" elements + parts.splice(0, 2); + switch (parts[0]) { + case 'run': { + if (parts.length >= 2) { + window.Shared.back.send(BackIn.LAUNCH_GAME, parts[1]); + } + break; + } + default: + remote.dialog.showMessageBox({ title: 'Protocol Error', message: `Unsupported action "${parts[0]}"` }); + break; + } + } + }); window.Shared.back.request(BackIn.INIT_LISTEN) .then(data => { diff --git a/src/shared/back/types.ts b/src/shared/back/types.ts index 581798b68..f8b9ad090 100644 --- a/src/shared/back/types.ts +++ b/src/shared/back/types.ts @@ -121,6 +121,7 @@ export enum BackIn { // Extensions RUN_COMMAND, + DOWNLOAD_EXTENSION, // Misc OPEN_LOGS_WINDOW, @@ -269,6 +270,7 @@ export type BackInTemplate = SocketTemplate RunCommandResponse; + [BackIn.DOWNLOAD_EXTENSION]: (downloadPath: string) => void; // Misc [BackIn.OPEN_LOGS_WINDOW]: () => void; diff --git a/src/shared/interfaces.ts b/src/shared/interfaces.ts index 2b5f39bec..e30560e70 100644 --- a/src/shared/interfaces.ts +++ b/src/shared/interfaces.ts @@ -158,6 +158,8 @@ export enum WindowIPC { WINDOW_MOVE = 'window-move', /** Sent whenever the windows size changes. (main -> renderer). */ WINDOW_RESIZE = 'window-resize', + /** Sent whenever a flashpoint:// protocol is run */ + PROTOCOL = 'protocol' } /** IPC channels used to relay game manager events from */ From f8d146737c0d649eeb2a46139e1ee7335e48cae0 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 23 Jun 2022 23:29:38 -0400 Subject: [PATCH 17/62] fix: turn strictNullChecks on. --- src/back/util/misc.ts | 2 +- src/renderer/components/GameItemContainer.tsx | 11 +++++++- src/renderer/components/TagItemContainer.tsx | 7 +++++- src/shared/back/SocketClient.ts | 6 ++--- src/shared/socket/SocketAPI.ts | 25 ++++++++++++------- src/shared/socket/SocketServer.ts | 4 +-- src/shared/socket/types.ts | 19 +++++++++++--- tsconfig.json | 2 +- 8 files changed, 54 insertions(+), 22 deletions(-) diff --git a/src/back/util/misc.ts b/src/back/util/misc.ts index fd8041b3a..1159d9693 100644 --- a/src/back/util/misc.ts +++ b/src/back/util/misc.ts @@ -271,7 +271,7 @@ export function runService(state: BackState, id: string, name: string, basePath: proc.spawn(); } catch (error) { log.error(SERVICES_SOURCE, `An unexpected error occurred while trying to run the background process "${proc.name}".` + - ` ${error.toString()}`); + ` ${(error as Error).toString()}`); } state.apiEmitters.services.onServiceNew.fire(proc); return proc; diff --git a/src/renderer/components/GameItemContainer.tsx b/src/renderer/components/GameItemContainer.tsx index 6537db41b..e13b09f0d 100644 --- a/src/renderer/components/GameItemContainer.tsx +++ b/src/renderer/components/GameItemContainer.tsx @@ -89,7 +89,16 @@ export class GameItemContainer extends React.Component { * @param props Properties to copy. */ function filterDivProps(props: GameItemContainerProps): JSX.IntrinsicElements['div'] { - const rest = Object.assign({}, props); + const rest: HTMLDivProps & { + // These need to be explicitly specified: the compiler doesn't infer them correctly. + realRef?: any; + onGameSelect?: any; + onGameLaunch?: any; + onGameContextMenu?: any; + onGameDragStart?: any; + onGameDragEnd?: any; + findGameId?: any; + } = Object.assign({}, props); delete rest.realRef; delete rest.onGameSelect; delete rest.onGameLaunch; diff --git a/src/renderer/components/TagItemContainer.tsx b/src/renderer/components/TagItemContainer.tsx index c1e172dbc..574683b55 100644 --- a/src/renderer/components/TagItemContainer.tsx +++ b/src/renderer/components/TagItemContainer.tsx @@ -49,7 +49,12 @@ export class TagItemContainer extends React.Component { * @param props Properties to copy. */ function filterDivProps(props: TagItemContainerProps): JSX.IntrinsicElements['div'] { - const rest = Object.assign({}, props); + const rest: HTMLDivProps & { + // These need to be explicitly specified: the compiler doesn't infer them correctly. + realRef?: any; + onTagSelect?: any; + findTagId?: any; + } = Object.assign({}, props); delete rest.realRef; delete rest.onTagSelect; delete rest.findTagId; diff --git a/src/shared/back/SocketClient.ts b/src/shared/back/SocketClient.ts index 85a8df83b..07d563ded 100644 --- a/src/shared/back/SocketClient.ts +++ b/src/shared/back/SocketClient.ts @@ -1,7 +1,7 @@ import { parse_message_data, validate_socket_message } from '@shared/socket/shared'; import { api_handle_message, api_register, api_register_any, api_unregister, api_unregister_any, create_api, SocketAPIData } from '@shared/socket/SocketAPI'; import { server_request, server_send, SocketServerClient } from '@shared/socket/SocketServer'; -import { BaseSocket, SocketResponseData } from '@shared/socket/types'; +import { BaseSocket, SocketResponseData, isErrorResponse } from '@shared/socket/types'; import { BackIn, BackInTemplate, BackOut, BackOutTemplate } from './types'; interface SocketConstructor { @@ -191,8 +191,8 @@ export class SocketClient { log( ' Response', '\n ID: ', inc.id, - '\n Result:', inc.result, - '\n Error: ', inc.error, + '\n Result:', isErrorResponse(inc) ? undefined : inc.result, + '\n Error: ', isErrorResponse(inc) ? inc.error : undefined, ); this.client.sent.splice(index, 1); diff --git a/src/shared/socket/SocketAPI.ts b/src/shared/socket/SocketAPI.ts index e680353e9..db513fcb8 100644 --- a/src/shared/socket/SocketAPI.ts +++ b/src/shared/socket/SocketAPI.ts @@ -1,4 +1,4 @@ -import { SocketRequestData, SocketResponseData, SocketTemplate } from './types'; +import { SocketRequestData, SocketResponseData, SocketTemplate, isErrorResponse } from './types'; /** Callback that is registered to a specific type. */ type Callback any> = (event: T, ...args: Parameters) => (ReturnType | Promise>) @@ -126,23 +126,30 @@ export async function api_handle_message< result = await callback(event, ...data.args as any); } catch (e) { // console.error(`An error was thrown from inside a callback (type: ${data.type}).`, e); - error = e.toString(); + error = (e as Error).toString(); } } // Respond if (data.id !== undefined) { if (typeof data.id === 'number') { - const response: SocketResponseData = { - id: data.id, - }; - if (result !== undefined) { response.result = result; } - if (error !== undefined) { response.error = error; } + let response: SocketResponseData; + if (result !== undefined) { + response = { + id: data.id, + result: result + }; + } else { + response = { + id: data.id, + error: error + }; + } log( ' Response', - '\n Result:', response.result, - '\n Error: ', response.error, + '\n Result:', isErrorResponse(response) ? undefined : response.result, + '\n Error: ', isErrorResponse(response)? response.error : undefined, ); return [undefined, response]; diff --git a/src/shared/socket/SocketServer.ts b/src/shared/socket/SocketServer.ts index 11944488b..5ef128c99 100644 --- a/src/shared/socket/SocketServer.ts +++ b/src/shared/socket/SocketServer.ts @@ -1,4 +1,4 @@ -import { BaseSocket, SocketRequestData, SocketResponseData, SocketTemplate } from './types'; +import { BaseSocket, SocketRequestData, SocketResponseData, isErrorResponse, SocketTemplate } from './types'; // Base types of generics type T_BASE = number @@ -85,7 +85,7 @@ export function server_request< client.sent.push({ id: id, resolve: (sent) => { - if (sent.error !== undefined) { + if (isErrorResponse(sent)) { reject(sent.error); } else { resolve(sent.result); diff --git a/src/shared/socket/types.ts b/src/shared/socket/types.ts index 7ed1afe1f..ed89a6d0d 100644 --- a/src/shared/socket/types.ts +++ b/src/shared/socket/types.ts @@ -13,15 +13,26 @@ export type SocketRequestData = { } /** Data of a websocket response message. */ -export type SocketResponseData = { +export type SocketResponseData = SocketResponseData_Error | SocketResponseData_Result; + +export type SocketResponseData_Error = { /** Unique ID of the message. */ id: number; - /** Type of message (determines what callback to use). */ - result?: T; /** Arguments to call the callback with. */ - error?: any; + error: any; +} + +export type SocketResponseData_Result = { + /** Unique ID of the message. */ + id: number; + /** Type of message (determines what callback to use). */ + result: T; } +export function isErrorResponse(variable: SocketResponseData): variable is SocketResponseData_Error { + return variable.hasOwnProperty('error'); +} + /** Minimal WebSocket interface. */ export interface BaseSocket { onclose: CB; diff --git a/tsconfig.json b/tsconfig.json index c6d53411b..de20c5cc3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "strictPropertyInitialization": false, - "strictNullChecks": false, + "strictNullChecks": true, "paths": { "@shared/*": [ "./src/shared/*" ], "@main/*": [ "./src/main/*" ], From 1f9e982efe6238517a7eed35198b9770b49f939e Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 23 Jun 2022 23:47:25 -0400 Subject: [PATCH 18/62] style: make the linter happy --- src/shared/socket/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/socket/types.ts b/src/shared/socket/types.ts index ed89a6d0d..063a904f2 100644 --- a/src/shared/socket/types.ts +++ b/src/shared/socket/types.ts @@ -30,8 +30,8 @@ export type SocketResponseData_Result = { } export function isErrorResponse(variable: SocketResponseData): variable is SocketResponseData_Error { - return variable.hasOwnProperty('error'); -} + return Object.prototype.hasOwnProperty.call(variable, 'error'); +} /** Minimal WebSocket interface. */ export interface BaseSocket { From 340ee1f4c832fccdf5f0ff7abe9655ca8704c7c0 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Tue, 28 Jun 2022 12:08:30 +0100 Subject: [PATCH 19/62] fix: Build issues --- .github/workflows/build.yml | 4 ++-- src/back/responses.ts | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 16493ef1b..a3cc83628 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ on: jobs: build: name: Build - runs-on: windows-2019 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -37,6 +37,6 @@ jobs: env: CI: true - name: npm build - run: npm run build:win32 --if-present + run: npm run build env: CI: true diff --git a/src/back/responses.ts b/src/back/responses.ts index 53c78a29f..b8ff071ea 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -1137,9 +1137,8 @@ export function registerRequestCallbacks(state: BackState): void { '', { detached: false, - shell: true, + noshell: false, cwd: getCwd(state.isDev, state.exePath), - execFile: true, env: env }, { @@ -1151,7 +1150,7 @@ export function registerRequestCallbacks(state: BackState): void { ); } else { const loggerService = state.services.get('logger_window'); - if (loggerService.getState() !== ProcessState.RUNNING) { + if (loggerService && loggerService.getState() !== ProcessState.RUNNING) { loggerService.restart(); } } From b750d700bd96696930f332507620f9fbe7d726d3 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Tue, 28 Jun 2022 12:11:21 +0100 Subject: [PATCH 20/62] refactor: Remove unused import --- src/renderer/app.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index adfb6eeca..c89131b00 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -32,7 +32,6 @@ import HeaderContainer from './containers/HeaderContainer'; import { WithMainStateProps } from './containers/withMainState'; import { WithPreferencesProps } from './containers/withPreferences'; import { WithTagCategoriesProps } from './containers/withTagCategories'; -import { newProgress } from './context/ProgressContext'; import { CreditsFile } from './credits/CreditsFile'; import { UpdateView, UpgradeStageState } from './interfaces'; import { Paths } from './Paths'; From 3f6ac1f718a075811d2a13a8e2035d14717ca468 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Tue, 28 Jun 2022 12:57:46 +0100 Subject: [PATCH 21/62] feat: Highlight active page --- package.json | 2 +- src/renderer/components/Header.tsx | 12 +++++++++--- static/window/styles/fancy.css | 4 ++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 09a93d775..5fc259ec9 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@types/react-color": "3.0.1", "@types/react-dom": "16.9.7", "@types/react-redux": "7.1.7", - "@types/react-router-dom": "4.3.4", + "@types/react-router-dom": "5.1.7", "@types/react-virtualized": "9.21.11", "@types/tail": "2.0.0", "@types/uuid": "3.4.5", diff --git a/src/renderer/components/Header.tsx b/src/renderer/components/Header.tsx index 05776131e..7bba0c8d7 100644 --- a/src/renderer/components/Header.tsx +++ b/src/renderer/components/Header.tsx @@ -4,7 +4,7 @@ import { LangContainer } from '@shared/lang'; import { getLibraryItemTitle } from '@shared/library/util'; import { GameOrderBy, GameOrderReverse } from '@shared/order/interfaces'; import * as React from 'react'; -import { Link, RouteComponentProps } from 'react-router-dom'; +import { Link, RouteComponentProps, useLocation } from 'react-router-dom'; import { WithPreferencesProps } from '../containers/withPreferences'; import { Paths } from '../Paths'; import { SearchQuery } from '../store/search'; @@ -253,12 +253,18 @@ export class Header extends React.Component { static contextType = LangContext; } +type MenuItemProps = { + title: string; + link: string; +}; /** An item in the header menu. Used as buttons to switch between tabs/pages. */ -function MenuItem({ title, link }: { title: string, link: string }) { +function MenuItem({ title, link }: MenuItemProps) { + const location = useLocation(); + const selected = link === '/' ? location.pathname === link : location.pathname.startsWith(link); return (
  • - {title} + {title}
  • ); } diff --git a/static/window/styles/fancy.css b/static/window/styles/fancy.css index 886096145..13599848f 100644 --- a/static/window/styles/fancy.css +++ b/static/window/styles/fancy.css @@ -392,6 +392,10 @@ body { background-color: var(--layout__header-menu-item-hover-background); border-color: var(--layout__header-menu-item-hover-border); } +.header__menu__item__link-selected { + font-weight: bold; + text-decoration: underline; +} .header__search { border: 1px solid var(--layout__primary-outline-color); background-color: var(--layout__primary-background); From 49656c9a486d16dbdcb9061411f71e32df87e3b7 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Wed, 29 Jun 2022 07:17:10 +0100 Subject: [PATCH 22/62] feat: Persistent right sidebar feat: Reworked game context menus --- package-lock.json | 42 +- src/renderer/app.tsx | 409 +++++++++++++++++- src/renderer/components/RandomGames.tsx | 47 +- .../components/RightBrowseSidebar.tsx | 2 +- src/renderer/components/pages/BrowsePage.tsx | 173 +------- src/renderer/components/pages/HomePage.tsx | 15 +- src/renderer/index.tsx | 3 +- src/renderer/router.tsx | 8 +- src/renderer/store/main/enums.ts | 2 +- src/renderer/store/main/reducer.ts | 17 +- src/renderer/store/main/types.ts | 13 +- static/window/styles/core.css | 3 + 12 files changed, 507 insertions(+), 227 deletions(-) diff --git a/package-lock.json b/package-lock.json index f92d4ea02..dcac7d68d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "flashpoint-launcher", - "version": "10.1.2", + "version": "10.1.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "flashpoint-launcher", - "version": "10.1.2", + "version": "10.1.3", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -57,7 +57,7 @@ "@types/react-color": "3.0.1", "@types/react-dom": "16.9.7", "@types/react-redux": "7.1.7", - "@types/react-router-dom": "4.3.4", + "@types/react-router-dom": "5.1.7", "@types/react-virtualized": "9.21.11", "@types/tail": "2.0.0", "@types/uuid": "3.4.5", @@ -2311,9 +2311,9 @@ } }, "node_modules/@types/react-router-dom": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.4.tgz", - "integrity": "sha512-xrwaWHpnxKk/TTRe7pmoGy3E4SyF/ojFqNfFJacw7OLdfLXRvGfk4r/XePVaZNVfeJzL8fcnNilPN7xOdJ/vGw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.7.tgz", + "integrity": "sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg==", "dev": true, "dependencies": { "@types/history": "*", @@ -9047,6 +9047,12 @@ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", "dev": true }, + "node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "peer": true + }, "node_modules/import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -16098,6 +16104,12 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/seamless-immutable": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/seamless-immutable/-/seamless-immutable-7.1.4.tgz", + "integrity": "sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A==", + "peer": true + }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -21459,9 +21471,9 @@ } }, "@types/react-router-dom": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.4.tgz", - "integrity": "sha512-xrwaWHpnxKk/TTRe7pmoGy3E4SyF/ojFqNfFJacw7OLdfLXRvGfk4r/XePVaZNVfeJzL8fcnNilPN7xOdJ/vGw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.7.tgz", + "integrity": "sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg==", "dev": true, "requires": { "@types/history": "*", @@ -26693,6 +26705,12 @@ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", "dev": true }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "peer": true + }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -31958,6 +31976,12 @@ "ajv-keywords": "^3.5.2" } }, + "seamless-immutable": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/seamless-immutable/-/seamless-immutable-7.1.4.tgz", + "integrity": "sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A==", + "peer": true + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index c89131b00..4ef8a307d 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -14,7 +14,7 @@ import { canReadWrite, deepCopy, getFileServerURL, recursiveReplace, sizeToStrin import { arrayShallowStrictEquals } from '@shared/utils/compare'; import { debounce } from '@shared/utils/debounce'; import { formatString } from '@shared/utils/StringFormatter'; -import { ipcRenderer } from 'electron'; +import { clipboard, ipcRenderer, Menu, MenuItemConstructorOptions } from 'electron'; import { AppUpdater } from 'electron-updater'; import * as fs from 'fs-extra'; import * as path from 'path'; @@ -25,9 +25,11 @@ import { FloatingContainer } from './components/FloatingContainer'; import { GameOrderChangeEvent } from './components/GameOrder'; import { MetaEditExporter, MetaEditExporterConfirmData } from './components/MetaEditExporter'; import { placeholderProgressData, ProgressBar } from './components/ProgressComponents'; +import { ResizableSidebar, SidebarResizeEvent } from './components/ResizableSidebar'; import { SplashScreen } from './components/SplashScreen'; import { TitleBar } from './components/TitleBar'; import { ConnectedFooter } from './containers/ConnectedFooter'; +import { ConnectedRightBrowseSidebar } from './containers/ConnectedRightBrowseSidebar'; import HeaderContainer from './containers/HeaderContainer'; import { WithMainStateProps } from './containers/withMainState'; import { WithPreferencesProps } from './containers/withPreferences'; @@ -42,8 +44,9 @@ import { MainState } from './store/main/types'; import { SearchQuery } from './store/search'; import { UpgradeStage } from './upgrade/types'; import { UpgradeFile } from './upgrade/UpgradeFile'; -import { getBrowseSubPath, isFlashpointValidCheck, joinLibraryRoute, openConfirmDialog } from './Util'; +import { getBrowseSubPath, getGamePath, isFlashpointValidCheck, joinLibraryRoute, openConfirmDialog } from './Util'; import { LangContext } from './util/lang'; +import { queueOne } from './util/queue'; import { checkUpgradeStateInstalled, checkUpgradeStateUpdated, downloadAndInstallUpgrade } from './util/upgrade'; const autoUpdater: AppUpdater = remote.require('electron-updater').autoUpdater; @@ -56,9 +59,13 @@ type AppOwnProps = { export type AppProps = AppOwnProps & RouteComponentProps & WithPreferencesProps & WithTagCategoriesProps & WithMainStateProps; export class App extends React.Component { + appRef: React.RefObject; + constructor(props: AppProps) { super(props); + this.appRef = React.createRef(); + // Initialize app this.init(); } @@ -613,6 +620,18 @@ export class App extends React.Component { } } + // Check for selected game changes + + // Check if it started or ended editing + if (this.props.main.isEditingGame != prevProps.main.isEditingGame) { + this.updateCurrentGame(this.props.main.selectedGameId, this.props.main.selectedPlaylistId); + } + // Update current game and add-apps if the selected game changes + if (this.props.main.selectedGameId && this.props.main.selectedGameId !== prevProps.main.selectedGameId) { + this.updateCurrentGame(this.props.main.selectedGameId, this.props.main.selectedPlaylistId); + this.setState({ isEditingGame: false }); + } + // Update preference "lastSelectedLibrary" const gameLibrary = getBrowseSubPath(location.pathname); if (location.pathname.startsWith(Paths.BROWSE) && @@ -630,7 +649,7 @@ export class App extends React.Component { const view = this.props.main.views[route]; if (view && view.selectedGameId !== undefined) { this.props.dispatchMain({ - type: MainActionType.SET_VIEW_SELECTED_GAME, + type: MainActionType.SET_SELECTED_GAME, library: route, gameId: undefined, }); @@ -641,11 +660,311 @@ export class App extends React.Component { } } + getGameBrowserDivWidth(): number { + if (!document.defaultView) { throw new Error('"document.defaultView" missing.'); } + if (!this.appRef.current) { throw new Error('"game-browser" div is missing.'); } + return parseInt(document.defaultView.getComputedStyle(this.appRef.current).width || '', 10); + } + + onRightSidebarResize = (event: SidebarResizeEvent): void => { + const maxWidth = (this.getGameBrowserDivWidth() - this.props.preferencesData.browsePageLeftSidebarWidth) - 5; + const targetWidth = event.startWidth + event.startX - event.event.clientX; + updatePreferencesData({ + browsePageRightSidebarWidth: Math.min(targetWidth, maxWidth) + }); + } + + onGameLaunch = async (gameId: string): Promise => { + await window.Shared.back.request(BackIn.LAUNCH_GAME, gameId); + } + + onDeleteSelectedGame = async (): Promise => { + // Delete the game + if (this.props.main.selectedGameId) { + this.onDeleteGame(this.props.main.selectedGameId); + } + // Deselect the game + this.onSelectGame(undefined); + // Reset the state related to the selected game + this.setState({ + currentGame: undefined, + currentPlaylistEntry: undefined, + isNewGame: false, + isEditingGame: false + }); + // Focus the game grid/list + // this.focusGameGridOrList(); + } + + onEditGame = (game: Partial) => { + log.debug('Launcher', `Editing: ${JSON.stringify(game)}`); + if (this.props.main.currentGame) { + const newGame = new Game(); + Object.assign(newGame, {...this.props.main.currentGame, ...game}); + newGame.updateTagsStr(); + this.props.setMainState({ + currentGame: newGame + }); + } + } + + onSaveEditClick = async (): Promise => { + if (!this.props.main.currentGame) { + console.error('Can\'t save game. "currentGame" is missing.'); + return; + } + const game = await this.onSaveGame(this.props.main.currentGame, this.props.main.currentPlaylistEntry); + this.props.setMainState({ + currentGame: game, + isEditingGame: false + }); + // this.focusGameGridOrList(); + } + + onDiscardEditClick = (): void => { + this.props.setMainState({ + isEditingGame: false, + currentGame: this.props.main.currentGame, + }); + // this.focusGameGridOrList(); + } + + onStartEditClick = (): void => { + if (this.props.preferencesData.enableEditing) { + this.props.setMainState({ isEditingGame: true }); + } + } + + onEditPlaylistNotes = (text: string): void => { + if (this.props.main.currentPlaylistEntry) { + this.setState({ + currentPlaylistEntry: { + ...this.props.main.currentPlaylistEntry, + notes: text + } + }); + } + } + + onUpdateActiveGameData = (activeDataOnDisk: boolean, activeDataId?: number): void => { + if (this.props.main.currentGame) { + const newGame = new Game(); + Object.assign(newGame, {...this.props.main.currentGame, activeDataOnDisk, activeDataId }); + window.Shared.back.request(BackIn.SAVE_GAME, newGame) + .then(() => { + if (this.props.main.currentGame) { + const newGame = new Game(); + Object.assign(newGame, {...this.props.main.currentGame, activeDataOnDisk, activeDataId }); + this.props.setMainState({ currentGame: newGame }); + } + }); + } + } + + onRemoveSelectedGameFromPlaylist = async (): Promise => { + // Remove game from playlist + if (this.props.main.currentGame) { + if (this.props.main.currentPlaylist) { + await window.Shared.back.request(BackIn.DELETE_PLAYLIST_GAME, this.props.main.currentPlaylist.id, this.props.main.currentGame.id); + } else { logError('No playlist is selected'); } + } else { logError('No game is selected'); } + + // Deselect the game + this.onSelectGame(undefined); + + // Reset the state related to the selected game + this.props.setMainState({ + currentGame: undefined, + currentPlaylistEntry: undefined, + isEditingGame: false + }); + + if (this.props.main.currentPlaylist) { + this.onUpdatePlaylist(this.props.main.currentPlaylist); + } + + function logError(text: string) { + console.error('Unable to remove game from selected playlist - ' + text); + } + } + + /** Deselect without clearing search (Right sidebar will search itself) */ + onRightSidebarDeselectPlaylist = (): void => { + this.onSelectPlaylist(getBrowseSubPath(this.props.location.pathname), undefined); + } + + /** Replace the "current game" with the selected game (in the appropriate circumstances). */ + updateCurrentGame = queueOne(async (gameId?: string, playlistId?: string): Promise => { + // Find the selected game in the selected playlist + if (gameId) { + let gamePlaylistEntry: PlaylistGame | undefined; + + if (playlistId) { + gamePlaylistEntry = await window.Shared.back.request(BackIn.GET_PLAYLIST_GAME, playlistId, gameId); + } + + // Update game + window.Shared.back.request(BackIn.GET_GAME, gameId) + .then(game => { + if (game) { + this.props.setMainState({ + currentGame: game, + currentPlaylistEntry: gamePlaylistEntry + }); + } else { console.log(`Failed to get game. Game is undefined (GameID: "${gameId}").`); } + }); + } + }); + + private onGameContextMenuMemo = memoizeOne((playlists: Playlist[], strings: LangContainer, selectedPlaylistId?: string) => { + return (gameId: string) => { + let contextButtons: MenuItemConstructorOptions[] = [{ + /* File Location */ + label: strings.menu.openFileLocation, + enabled: !window.Shared.isBackRemote, // (Local "back" only) + click: () => { + window.Shared.back.request(BackIn.GET_GAME, gameId) + .then(async (game) => { + if (game) { + const gamePath = await getGamePath(game, window.Shared.config.fullFlashpointPath, window.Shared.preferences.data.htdocsFolderPath, window.Shared.preferences.data.dataPacksFolderPath); + try { + if (gamePath) { + await fs.promises.stat(gamePath); + remote.shell.showItemInFolder(gamePath); + } else { + const opts: Electron.MessageBoxOptions = { + type: 'warning', + message: 'GameData has not been downloaded yet, cannot open the file location!', + buttons: ['Ok'], + }; + remote.dialog.showMessageBox(opts); + return; + } + } catch (error: any) { + const opts: Electron.MessageBoxOptions = { + type: 'warning', + message: '', + buttons: ['Ok'], + }; + if (error.code === 'ENOENT') { + opts.title = this.context.dialog.fileNotFound; + opts.message = ( + 'Failed to find the game file.\n'+ + 'If you are using Flashpoint Infinity, make sure you download the game first.\n' + ); + } else { + opts.title = 'Unexpected error'; + opts.message = ( + 'Failed to check the game file.\n'+ + 'If you see this, please report it back to us (a screenshot would be great)!\n\n'+ + `Error: ${error}\n` + ); + } + opts.message += `Path: "${gamePath}"\n\nNote: If the path is too long, some portion will be replaced with three dots ("...").`; + remote.dialog.showMessageBox(opts); + } + } + }); + }, + }, { + type: 'submenu', + label: strings.menu.addToPlaylist, + enabled: playlists.length > 0, + submenu: UniquePlaylistMenuFactory(playlists, + strings, + (playlistId) => window.Shared.back.send(BackIn.ADD_PLAYLIST_GAME, playlistId, gameId), + selectedPlaylistId) + }, { type: 'separator' }, { + /* Copy Game UUID */ + label: strings.menu.copyGameUUID, + enabled: true, + click : () => { + clipboard.writeText(gameId); + } + }, { type: 'separator' }]; + + // Add editing mode fields + if (this.props.preferencesData.enableEditing) { + const editingButtons: MenuItemConstructorOptions[] = [ + { + /* Duplicate Meta */ + label: strings.menu.duplicateMetaOnly, + enabled: this.props.preferencesData.enableEditing, + click: () => { window.Shared.back.request(BackIn.DUPLICATE_GAME, gameId, false); }, + }, { + /* Duplicate Meta & Images */ + label: strings.menu.duplicateMetaAndImages, // ("&&" will be shown as "&") + enabled: this.props.preferencesData.enableEditing, + click: () => { window.Shared.back.request(BackIn.DUPLICATE_GAME, gameId, true); }, + }, { type: 'separator' }, { + /* Export Meta */ + label: strings.menu.exportMetaOnly, + enabled: !window.Shared.isBackRemote, // (Local "back" only) + click: () => { + const filePath = remote.dialog.showSaveDialogSync({ + title: strings.dialog.selectFileToExportMeta, + defaultPath: 'meta.yaml', + filters: [{ + name: 'Meta file', + extensions: ['yaml'], + }] + }); + if (filePath) { window.Shared.back.request(BackIn.EXPORT_GAME, gameId, filePath, true); } + }, + }, { + /* Export Meta & Images */ + label: strings.menu.exportMetaAndImages, // ("&&" will be shown as "&") + enabled: !window.Shared.isBackRemote, // (Local "back" only) + click: () => { + const filePaths = window.Shared.showOpenDialogSync({ + title: strings.dialog.selectFolderToExportMetaAndImages, + properties: ['promptToCreate', 'openDirectory'] + }); + if (filePaths && filePaths.length > 0) { + window.Shared.back.request(BackIn.EXPORT_GAME, gameId, filePaths[0], false); + } + }, + }, { + /* Export Partial Meta */ + label: strings.menu.exportMetaEdit, // ("&&" will be shown as "&") + enabled: !window.Shared.isBackRemote, // (Local "back" only) + click: () => { + this.onOpenExportMetaEdit(gameId); + }, + }, { type: 'separator' } + ]; + contextButtons = contextButtons.concat(editingButtons); + } + + // Add extension contexts + for (const contribution of this.props.main.contextButtons) { + for (const contextButton of contribution.value) { + if (contextButton.context === 'game') { + contextButtons.push({ + label: contextButton.name, + click: () => { + window.Shared.back.request(BackIn.GET_GAME, gameId) + .then((game) => { + window.Shared.back.request(BackIn.RUN_COMMAND, contextButton.command, [game]); + }); + } + }); + } + } + } + + return ( + openContextMenu(contextButtons) + ); + }; + }); + render() { const loaded = isInitDone(this.props.main); const libraryPath = getBrowseSubPath(this.props.location.pathname); const view = this.props.main.views[libraryPath]; const playlists = this.filterAndOrderPlaylistsMemo(this.props.main.playlists, libraryPath); + const extremeTags = this.props.preferencesData.tagFilters.filter(t => !t.enabled && t.extreme).reduce((prev, cur) => prev.concat(cur.tags), []); // Props to set to the router const routerProps: AppRouterProps = { @@ -660,6 +979,7 @@ export class App extends React.Component { platforms: this.props.main.platforms, platformsFlat: this.flattenPlatformsMemo(this.props.main.platforms), playlistIconCache: this.props.main.playlistIconCache, + onGameContextMenu: this.onGameContextMenuMemo(this.props.main.playlists, this.props.main.lang, this.props.main.selectedPlaylistId), onSaveGame: this.onSaveGame, onDeleteGame: this.onDeleteGame, onLaunchGame: this.onLaunchGame, @@ -673,8 +993,8 @@ export class App extends React.Component { upgrades: this.props.main.upgrades, creditsData: this.props.main.creditsData, creditsDoneLoading: this.props.main.creditsDoneLoading, - selectedGameId: view && view.selectedGameId, - gameRunning: view ? this.checkGameRunningMemo(view.selectedGameId, this.props.main.services) : false, + selectedGameId: this.props.main.selectedGameId, + gameRunning: this.checkGameRunningMemo(this.props.main.selectedGameId, this.props.main.services), selectedPlaylistId: view && view.query.filter.playlistId, onSelectGame: this.onSelectGame, onDeletePlaylist: this.onPlaylistDelete, @@ -728,13 +1048,41 @@ export class App extends React.Component { orderBy={this.props.preferencesData.gamesOrderBy} orderReverse={this.props.preferencesData.gamesOrder} /> {/* Main */} -
    +
    + + ((prev, next) => extremeTags.includes(next.primaryAlias.name), false) : false} + gameRunning={routerProps.gameRunning} + currentPlaylistEntry={this.props.main.currentPlaylistEntry} + currentLibrary={routerProps.gameLibrary} + onGameLaunch={this.onGameLaunch} + onDeleteSelectedGame={this.onDeleteSelectedGame} + onRemoveSelectedGameFromPlaylist={this.onRemoveSelectedGameFromPlaylist} + onDeselectPlaylist={this.onRightSidebarDeselectPlaylist} + onEditPlaylistNotes={this.onEditPlaylistNotes} + isEditing={this.props.main.isEditingGame} + isNewGame={false} /* Deprecated */ + onEditGame={this.onEditGame} + onUpdateActiveGameData={this.onUpdateActiveGameData} + onEditClick={this.onStartEditClick} + onDiscardClick={this.onDiscardEditClick} + onSaveGame={this.onSaveEditClick} + tagCategories={this.props.tagCategories} + suggestions={this.props.main.suggestions} + onOpenExportMetaEdit={this.onOpenExportMetaEdit} /> +
    {/* Footer */} @@ -798,15 +1146,10 @@ export class App extends React.Component { } private onSelectGame = (gameId?: string): void => { - const library = getBrowseSubPath(this.props.location.pathname); - const view = this.props.main.views[library]; - if (view) { - this.props.dispatchMain({ - type: MainActionType.SET_VIEW_SELECTED_GAME, - library: library, - gameId: gameId, - }); - } + this.props.dispatchMain({ + type: MainActionType.SET_SELECTED_GAME, + gameId: gameId, + }); } /** Set the selected playlist for a single "browse route" */ @@ -1172,3 +1515,39 @@ function isInitDone(state: MainState): boolean { state.loaded[BackInit.EXEC] ); } + +function openContextMenu(template: MenuItemConstructorOptions[]): Menu { + const menu = remote.Menu.buildFromTemplate(template); + menu.popup({ window: remote.getCurrentWindow() }); + return menu; +} + +type MenuItemLibrary = MenuItemConstructorOptions & { + library: string; +} + +function UniquePlaylistMenuFactory(playlists: Playlist[], strings: LangContainer, onClick: (playlistId: string) => any, selectedPlaylistId?: string): MenuItemConstructorOptions[] { + const grouped: Array = []; + for (const p of playlists.filter(p => p.id != selectedPlaylistId)) { + let group = grouped.find(g => g.library === p.library); + if (!group) { + const newGroup: MenuItemLibrary = { + type: 'submenu', + library: p.library, + enabled: true, + label: strings.libraries[p.library] || p.library, + submenu: [] + }; + group = newGroup; + grouped.push(group); + } + if (group.submenu && Array.isArray(group.submenu)) { + group.submenu.push({ + label: p.title || 'No Title', + enabled: true, + click: () => onClick(p.id) + }); + } + } + return grouped; +} \ No newline at end of file diff --git a/src/renderer/components/RandomGames.tsx b/src/renderer/components/RandomGames.tsx index 1a52f4b64..cec2791de 100644 --- a/src/renderer/components/RandomGames.tsx +++ b/src/renderer/components/RandomGames.tsx @@ -11,7 +11,11 @@ import { SimpleButton } from './SimpleButton'; type RandomGamesProps = { games: ViewGame[]; + selectedGameId?: string; + /** Generator for game context menu */ + onGameContextMenu: (gameId: string) => void; onLaunchGame: (gameId: string) => void; + onGameSelect: (gameId: string | undefined) => void; rollRandomGames: () => void; extremeTags: string[]; /** Update to clear platform icon cache */ @@ -24,6 +28,10 @@ type RandomGamesProps = { export function RandomGames(props: RandomGamesProps) { const strings = React.useContext(LangContext); + const onGameSelect = React.useCallback((event: React.MouseEvent, gameId: string | undefined) => { + props.onGameSelect(gameId); + }, [props.onGameSelect]); + const onLaunchGame = React.useCallback((event: React.MouseEvent, gameId: string) => { props.onLaunchGame(gameId); }, [props.onLaunchGame]); @@ -32,27 +40,36 @@ export function RandomGames(props: RandomGamesProps) { props.rollRandomGames(); }, [props.rollRandomGames]); - const gameItems = React.useMemo(() => ( + const gameItems = React.useMemo(() => { /* Games is a long queue, only render front */ - props.games.slice(0, 6).map(game => ( - props.extremeTags.includes(t.trim())) !== -1 : false} - extremeIconPath={getExtremeIconURL(props.logoVersion)} - thumbnail={getGameImageURL(LOGOS, game.id)} - logoVersion={props.logoVersion} - isSelected={false} - isDragged={false} /> - )) - ), [props.games]); + log.debug('TEST', `Rendering Random Games - ${props.selectedGameId || 'None Selected'}`); + return ( + props.games.slice(0, 6).map(game => ( + props.extremeTags.includes(t.trim())) !== -1 : false} + extremeIconPath={getExtremeIconURL(props.logoVersion)} + thumbnail={getGameImageURL(LOGOS, game.id)} + logoVersion={props.logoVersion} + isSelected={props.selectedGameId === game.id} + isDragged={false} /> + )) + ); + }, [props.games, props.selectedGameId, props.logoVersion, props.extremeTags]); + + const onGameContextMenu = (event: React.MouseEvent, gameId: string) => { + return props.onGameContextMenu(gameId); + } const render = ( <> {gameItems} diff --git a/src/renderer/components/RightBrowseSidebar.tsx b/src/renderer/components/RightBrowseSidebar.tsx index 411e4dd45..2816336fb 100644 --- a/src/renderer/components/RightBrowseSidebar.tsx +++ b/src/renderer/components/RightBrowseSidebar.tsx @@ -726,7 +726,7 @@ export class RightBrowseSidebar extends React.Component -

    {formatString(strings.noGameSelected, allStrings.libraries[this.props.currentLibrary + 'Singular'])}

    +

    {formatString(strings.noGameSelected, allStrings.libraries[this.props.currentLibrary + 'Singular'] || 'UNKNOWN')}

    {strings.clickToSelectGame}

    ); diff --git a/src/renderer/components/pages/BrowsePage.tsx b/src/renderer/components/pages/BrowsePage.tsx index 485b8c654..de64db3f2 100644 --- a/src/renderer/components/pages/BrowsePage.tsx +++ b/src/renderer/components/pages/BrowsePage.tsx @@ -11,15 +11,13 @@ import { LangContainer } from '@shared/lang'; import { memoizeOne } from '@shared/memoize'; import { updatePreferencesData } from '@shared/preferences/util'; import { formatString } from '@shared/utils/StringFormatter'; -import { clipboard, Menu, MenuItemConstructorOptions } from 'electron'; -import * as fs from 'fs'; +import { Menu, MenuItemConstructorOptions } from 'electron'; import * as React from 'react'; import { ConnectedLeftBrowseSidebar } from '../../containers/ConnectedLeftBrowseSidebar'; -import { ConnectedRightBrowseSidebar } from '../../containers/ConnectedRightBrowseSidebar'; import { WithPreferencesProps } from '../../containers/withPreferences'; import { UpdateView, ViewGameSet } from '../../interfaces'; import { SearchQuery } from '../../store/search'; -import { gameIdDataType, gameScaleSpan, getGamePath } from '../../Util'; +import { gameIdDataType, gameScaleSpan } from '../../Util'; import { LangContext } from '../../util/lang'; import { queueOne } from '../../util/queue'; import { uuid } from '../../util/uuid'; @@ -51,6 +49,8 @@ type OwnProps = { gameRunning: boolean; /** Currently selected playlist (if any). */ selectedPlaylistId?: string; + /** Generator for game context menu */ + onGameContextMenu: (gameId: string) => void; /** Called when a game is selected. */ onSelectGame: (gameId?: string) => void; /** Called when a playlist is selected. */ @@ -235,7 +235,7 @@ export class BrowsePage extends React.Component - - ((prev, next) => extremeTags.includes(next.primaryAlias.name), false) : false} - gameRunning={this.props.gameRunning} - currentPlaylistEntry={this.state.currentPlaylistEntry} - currentLibrary={this.props.gameLibrary} - onGameLaunch={this.onGameLaunch} - onDeleteSelectedGame={this.onDeleteSelectedGame} - onRemoveSelectedGameFromPlaylist={this.onRemoveSelectedGameFromPlaylist} - onDeselectPlaylist={this.onRightSidebarDeselectPlaylist} - onEditPlaylistNotes={this.onEditPlaylistNotes} - isEditing={this.state.isEditingGame} - isNewGame={this.state.isNewGame} - onEditGame={this.onEditGame} - onUpdateActiveGameData={this.onUpdateActiveGameData} - onEditClick={this.onStartEditClick} - onDiscardClick={this.onDiscardEditClick} - onSaveGame={this.onSaveEditClick} - tagCategories={this.props.tagCategories} - suggestions={this.props.suggestions} - onOpenExportMetaEdit={this.props.onOpenExportMetaEdit} /> -
    ); } @@ -368,140 +341,6 @@ export class BrowsePage extends React.Component { - return (gameId: string) => { - const contextButtons: MenuItemConstructorOptions[] = [{ - /* File Location */ - label: strings.menu.openFileLocation, - enabled: !window.Shared.isBackRemote, // (Local "back" only) - click: () => { - window.Shared.back.request(BackIn.GET_GAME, gameId) - .then(async (game) => { - if (game) { - const gamePath = await getGamePath(game, window.Shared.config.fullFlashpointPath, window.Shared.preferences.data.htdocsFolderPath, window.Shared.preferences.data.dataPacksFolderPath); - try { - if (gamePath) { - await fs.promises.stat(gamePath); - remote.shell.showItemInFolder(gamePath); - } else { - const opts: Electron.MessageBoxOptions = { - type: 'warning', - message: 'GameData has not been downloaded yet, cannot open the file location!', - buttons: ['Ok'], - }; - remote.dialog.showMessageBox(opts); - return; - } - } catch (error: any) { - const opts: Electron.MessageBoxOptions = { - type: 'warning', - message: '', - buttons: ['Ok'], - }; - if (error.code === 'ENOENT') { - opts.title = this.context.dialog.fileNotFound; - opts.message = ( - 'Failed to find the game file.\n'+ - 'If you are using Flashpoint Infinity, make sure you download the game first.\n' - ); - } else { - opts.title = 'Unexpected error'; - opts.message = ( - 'Failed to check the game file.\n'+ - 'If you see this, please report it back to us (a screenshot would be great)!\n\n'+ - `Error: ${error}\n` - ); - } - opts.message += `Path: "${gamePath}"\n\nNote: If the path is too long, some portion will be replaced with three dots ("...").`; - remote.dialog.showMessageBox(opts); - } - } - }); - }, - }, { - type: 'submenu', - label: strings.menu.addToPlaylist, - enabled: playlists.length > 0, - submenu: UniquePlaylistMenuFactory(playlists, - (playlistId) => window.Shared.back.send(BackIn.ADD_PLAYLIST_GAME, playlistId, gameId), - selectedPlaylistId) - }, { type: 'separator' }, { - /* Duplicate Meta */ - label: strings.menu.duplicateMetaOnly, - enabled: this.props.preferencesData.enableEditing, - click: () => { window.Shared.back.request(BackIn.DUPLICATE_GAME, gameId, false); }, - }, { - /* Duplicate Meta & Images */ - label: strings.menu.duplicateMetaAndImages, // ("&&" will be shown as "&") - enabled: this.props.preferencesData.enableEditing, - click: () => { window.Shared.back.request(BackIn.DUPLICATE_GAME, gameId, true); }, - }, { type: 'separator' }, { - /* Export Meta */ - label: strings.menu.exportMetaOnly, - enabled: !window.Shared.isBackRemote, // (Local "back" only) - click: () => { - const filePath = remote.dialog.showSaveDialogSync({ - title: strings.dialog.selectFileToExportMeta, - defaultPath: 'meta.yaml', - filters: [{ - name: 'Meta file', - extensions: ['yaml'], - }] - }); - if (filePath) { window.Shared.back.request(BackIn.EXPORT_GAME, gameId, filePath, true); } - }, - }, { - /* Export Meta & Images */ - label: strings.menu.exportMetaAndImages, // ("&&" will be shown as "&") - enabled: !window.Shared.isBackRemote, // (Local "back" only) - click: () => { - const filePaths = window.Shared.showOpenDialogSync({ - title: strings.dialog.selectFolderToExportMetaAndImages, - properties: ['promptToCreate', 'openDirectory'] - }); - if (filePaths && filePaths.length > 0) { - window.Shared.back.request(BackIn.EXPORT_GAME, gameId, filePaths[0], false); - } - }, - }, { type: 'separator' }, { - /* Copy Game UUID */ - label: strings.menu.copyGameUUID, - enabled: true, - click : () => { - clipboard.writeText(gameId); - } - }, { - /* Export Partial Meta */ - label: strings.menu.exportMetaEdit, // ("&&" will be shown as "&") - enabled: !window.Shared.isBackRemote, // (Local "back" only) - click: () => { - this.props.onOpenExportMetaEdit(gameId); - }, - }, { type: 'separator' }]; - - // Add extension contexts - for (const contribution of this.props.contextButtons) { - for (const contextButton of contribution.value) { - if (contextButton.context === 'game') { - contextButtons.push({ - label: contextButton.name, - click: () => { - window.Shared.back.request(BackIn.GET_GAME, gameId) - .then((game) => { - window.Shared.back.request(BackIn.RUN_COMMAND, contextButton.command, [game]); - }); - } - }); - } - } - } - - return ( - openContextMenu(contextButtons) - ); - }; - }); - /** Deselect without clearing search (Right sidebar will search itself) */ onRightSidebarDeselectPlaylist = (): void => { const { onSelectPlaylist } = this.props; diff --git a/src/renderer/components/pages/HomePage.tsx b/src/renderer/components/pages/HomePage.tsx index b70c0343d..52a48c7d4 100644 --- a/src/renderer/components/pages/HomePage.tsx +++ b/src/renderer/components/pages/HomePage.tsx @@ -33,8 +33,11 @@ type OwnProps = { playlists: Playlist[]; /** Data and state used for the upgrade system (optional install-able downloads from the HomePage). */ upgrades: UpgradeStage[]; + /** Generator for game context menu */ + onGameContextMenu: (gameId: string) => void; onSelectPlaylist: (library: string, playlistId: string | undefined) => void; onLaunchGame: (gameId: string) => void; + onGameSelect: (gameId: string | undefined) => void; /** Clear the current search query (resets the current search filters). */ clearSearch: () => void; /** Called when the "download tech" button is clicked. */ @@ -51,6 +54,7 @@ type OwnProps = { logoVersion: number; /** Raw HTML of the Update page grabbed */ updateFeedMarkdown: string; + selectedGameId?: string; }; export type HomePageProps = OwnProps & WithPreferencesProps & WithSearchProps; @@ -85,6 +89,10 @@ export function HomePage(props: HomePageProps) { }); }, [props.preferencesData.minimizedHomePageBoxes]); + const onGameSelect = React.useCallback((gameId: string | undefined) => { + props.onGameSelect(gameId); + }, [props.onGameSelect]); + const onLaunchGame = React.useCallback((gameId: string) => { props.onLaunchGame(gameId); }, [props.onLaunchGame]); @@ -340,18 +348,23 @@ export function HomePage(props: HomePageProps) { ); }, [strings, props.preferencesData.minimizedHomePageBoxes, toggleMinimizeBox]); + log.debug('Launcher', 'Selected - ' + (props.selectedGameId || 'None')); + const renderedRandomGames = React.useMemo(() => ( !tfg.enabled && tfg.extreme).reduce((prev, cur) => prev.concat(cur.tags), [])} logoVersion={props.logoVersion} + selectedGameId={props.selectedGameId} minimized={props.preferencesData.minimizedHomePageBoxes.includes('random-games')} onToggleMinimize={() => toggleMinimizeBox('random-games')} /> - ), [strings, props.randomGames, onLaunchGame, props.rollRandomGames, props.preferencesData.minimizedHomePageBoxes, toggleMinimizeBox]); + ), [strings, props.onGameContextMenu, props.selectedGameId, props.logoVersion, props.preferencesData.tagFilters, props.randomGames, onLaunchGame, props.rollRandomGames, props.preferencesData.minimizedHomePageBoxes, toggleMinimizeBox]); const renderedUpdateFeed = React.useMemo(() => { if (props.updateFeedMarkdown) { diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index 92eae375e..4ab91aeca 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -145,6 +145,7 @@ export function createInitialMainState(): MainState { downloadPercent: 0, downloadSize: 0, downloadOpen: false, - downloadVerifying: false + downloadVerifying: false, + isEditingGame: false, }; } diff --git a/src/renderer/router.tsx b/src/renderer/router.tsx index 800524510..08c275ff0 100644 --- a/src/renderer/router.tsx +++ b/src/renderer/router.tsx @@ -3,10 +3,11 @@ import { Playlist } from '@database/entity/Playlist'; import { PlaylistGame } from '@database/entity/PlaylistGame'; import { ViewGame } from '@shared/back/types'; import { AppExtConfigData } from '@shared/config/interfaces'; -import { ExtensionContribution, ILogoSet, IExtensionDescription } from '@shared/extensions/interfaces'; +import { ExtensionContribution, IExtensionDescription, ILogoSet } from '@shared/extensions/interfaces'; import { GamePropSuggestions, IService } from '@shared/interfaces'; import { LangContainer, LangFile } from '@shared/lang'; import { ITheme } from '@shared/ThemeFile'; +import { Menu } from 'electron'; import { AppUpdater, UpdateInfo } from 'electron-updater'; import * as React from 'react'; import { Route, Switch } from 'react-router-dom'; @@ -54,6 +55,7 @@ export type AppRouterProps = { selectedGameId?: string; gameRunning: boolean; selectedPlaylistId?: string; + onGameContextMenu: (gameId: string) => Menu; onSelectGame: (gameId?: string) => void; onUpdatePlaylist: (playlist: Playlist) => void; onDeletePlaylist: (playlist: Playlist) => void; @@ -82,8 +84,10 @@ export class AppRouter extends React.Component { platforms: this.props.platforms, playlists: this.props.playlists, upgrades: this.props.upgrades, + onGameContextMenu: this.props.onGameContextMenu, onSelectPlaylist: this.props.onSelectPlaylist, onLaunchGame: this.props.onLaunchGame, + onGameSelect: this.props.onSelectGame, onDownloadUpgradeClick: this.props.onDownloadUpgradeClick, updateInfo: this.props.updateInfo, autoUpdater: this.props.autoUpdater, @@ -91,6 +95,7 @@ export class AppRouter extends React.Component { rollRandomGames: this.props.rollRandomGames, logoVersion: this.props.logoVersion, updateFeedMarkdown: this.props.updateFeedMarkdown, + selectedGameId: this.props.selectedGameId }; const browseProps: ConnectedBrowsePageProps = { games: this.props.games, @@ -99,6 +104,7 @@ export class AppRouter extends React.Component { playlists: this.props.playlists, suggestions: this.props.suggestions, playlistIconCache: this.props.playlistIconCache, + onGameContextMenu: this.props.onGameContextMenu, onSaveGame: this.props.onSaveGame, onDeleteGame: this.props.onDeleteGame, onQuickSearch: this.props.onQuickSearch, diff --git a/src/renderer/store/main/enums.ts b/src/renderer/store/main/enums.ts index d64aeca2c..1d6e0b90a 100644 --- a/src/renderer/store/main/enums.ts +++ b/src/renderer/store/main/enums.ts @@ -16,7 +16,7 @@ export enum MainActionType { /** Add pages to a view. */ ADD_VIEW_PAGES = '@@main/ADD_VIEW_PAGES', /** Set the selected game of a view. */ - SET_VIEW_SELECTED_GAME = '@@main/SET_VIEW_SELECTED_GAME', + SET_SELECTED_GAME = '@@main/SET_VIEW_SELECTED_GAME', /** Set the credits data (or at least flag it as done loading it). */ SET_CREDITS = '@@main/SET_CREDITS', /** Stop rendering. */ diff --git a/src/renderer/store/main/reducer.ts b/src/renderer/store/main/reducer.ts index 64b796535..01d7d4352 100644 --- a/src/renderer/store/main/reducer.ts +++ b/src/renderer/store/main/reducer.ts @@ -196,20 +196,10 @@ export function mainStateReducer(state: MainState = createInitialState(), action }; } - case MainActionType.SET_VIEW_SELECTED_GAME: { - const view = state.views[action.library]; - - if (!view) { return state; } - + case MainActionType.SET_SELECTED_GAME: { return { ...state, - views: { - ...state.views, - [action.library]: { - ...view, - selectedGameId: action.gameId, - }, - }, + selectedGameId: action.gameId }; } @@ -435,6 +425,7 @@ function createInitialState(): MainState { downloadOpen: false, downloadPercent: 0, downloadSize: 0, - downloadVerifying: false + downloadVerifying: false, + isEditingGame: false }; } diff --git a/src/renderer/store/main/types.ts b/src/renderer/store/main/types.ts index e3e8f7d26..20b574374 100644 --- a/src/renderer/store/main/types.ts +++ b/src/renderer/store/main/types.ts @@ -1,4 +1,6 @@ +import { Game } from '@database/entity/Game'; import { Playlist } from '@database/entity/Playlist'; +import { PlaylistGame } from '@database/entity/PlaylistGame'; import { CreditsData } from '@renderer/credits/types'; import { ViewGameSet } from '@renderer/interfaces'; import { UpgradeStage } from '@renderer/upgrade/types'; @@ -9,10 +11,10 @@ import { GamePropSuggestions, IService } from '@shared/interfaces'; import { LangContainer, LangFile } from '@shared/lang'; import { GameOrderBy, GameOrderReverse } from '@shared/order/interfaces'; import { ITheme, Theme } from '@shared/ThemeFile'; +import * as axiosImport from 'axios'; import { UpdateInfo } from 'electron-updater'; import { TagFilterGroup } from 'flashpoint-launcher'; import { MainActionType, RequestState } from './enums'; -import * as axiosImport from 'axios'; export type View = { /** The most recent query used for this view. */ @@ -121,6 +123,12 @@ export type MainState = { downloadOpen: boolean; cancelToken?: axiosImport.CancelToken; downloadVerifying: boolean; + selectedGameId?: string; + selectedPlaylistId?: string; + currentGame?: Game; + currentPlaylist?: Playlist; + currentPlaylistEntry?: PlaylistGame; + isEditingGame: boolean; } export type MainAction = { @@ -163,8 +171,7 @@ export type MainAction = { queryId: number; pages: number[]; } | { - type: MainActionType.SET_VIEW_SELECTED_GAME; - library: string; + type: MainActionType.SET_SELECTED_GAME; gameId?: string; } | { type: MainActionType.SET_CREDITS; diff --git a/static/window/styles/core.css b/static/window/styles/core.css index bad596e69..5e076d5dc 100644 --- a/static/window/styles/core.css +++ b/static/window/styles/core.css @@ -421,6 +421,9 @@ body { height: 100%; } .main { + display: flex; + width: 100%; + height: 100%; flex: 1; overflow: hidden; } From 322583f3c0408a5c09eb2c878818ade315e163ea Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Wed, 29 Jun 2022 07:24:00 +0100 Subject: [PATCH 23/62] fix: Right sidebar empty text wrong on some pages --- src/renderer/app.tsx | 19 ++++++++++--------- .../components/RightBrowseSidebar.tsx | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index 4ef8a307d..6a41aa257 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -818,7 +818,16 @@ export class App extends React.Component { private onGameContextMenuMemo = memoizeOne((playlists: Playlist[], strings: LangContainer, selectedPlaylistId?: string) => { return (gameId: string) => { - let contextButtons: MenuItemConstructorOptions[] = [{ + let contextButtons: MenuItemConstructorOptions[] = [ + { + type: 'submenu', + label: strings.menu.addToPlaylist, + enabled: playlists.length > 0, + submenu: UniquePlaylistMenuFactory(playlists, + strings, + (playlistId) => window.Shared.back.send(BackIn.ADD_PLAYLIST_GAME, playlistId, gameId), + selectedPlaylistId) + }, { /* File Location */ label: strings.menu.openFileLocation, enabled: !window.Shared.isBackRemote, // (Local "back" only) @@ -867,14 +876,6 @@ export class App extends React.Component { }); }, }, { - type: 'submenu', - label: strings.menu.addToPlaylist, - enabled: playlists.length > 0, - submenu: UniquePlaylistMenuFactory(playlists, - strings, - (playlistId) => window.Shared.back.send(BackIn.ADD_PLAYLIST_GAME, playlistId, gameId), - selectedPlaylistId) - }, { type: 'separator' }, { /* Copy Game UUID */ label: strings.menu.copyGameUUID, enabled: true, diff --git a/src/renderer/components/RightBrowseSidebar.tsx b/src/renderer/components/RightBrowseSidebar.tsx index 2816336fb..12611c71b 100644 --- a/src/renderer/components/RightBrowseSidebar.tsx +++ b/src/renderer/components/RightBrowseSidebar.tsx @@ -726,7 +726,7 @@ export class RightBrowseSidebar extends React.Component -

    {formatString(strings.noGameSelected, allStrings.libraries[this.props.currentLibrary + 'Singular'] || 'UNKNOWN')}

    +

    {formatString(strings.noGameSelected, allStrings.libraries[this.props.currentLibrary + 'Singular'] || allStrings.libraries['arcadeSingular'] || 'Game')}

    {strings.clickToSelectGame}

    ); From a2eea15a35583f7dbbb6e98ea40e6e66b3ac004a Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Wed, 29 Jun 2022 07:32:42 +0100 Subject: [PATCH 24/62] linter: Various --- src/renderer/app.tsx | 115 +++++++++---------- src/renderer/components/RandomGames.tsx | 2 +- src/renderer/components/pages/BrowsePage.tsx | 11 -- 3 files changed, 58 insertions(+), 70 deletions(-) diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index 6a41aa257..eb725776a 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -629,7 +629,7 @@ export class App extends React.Component { // Update current game and add-apps if the selected game changes if (this.props.main.selectedGameId && this.props.main.selectedGameId !== prevProps.main.selectedGameId) { this.updateCurrentGame(this.props.main.selectedGameId, this.props.main.selectedPlaylistId); - this.setState({ isEditingGame: false }); + this.props.setMainState({ isEditingGame: false }); } // Update preference "lastSelectedLibrary" @@ -686,10 +686,9 @@ export class App extends React.Component { // Deselect the game this.onSelectGame(undefined); // Reset the state related to the selected game - this.setState({ + this.props.setMainState({ currentGame: undefined, currentPlaylistEntry: undefined, - isNewGame: false, isEditingGame: false }); // Focus the game grid/list @@ -737,7 +736,7 @@ export class App extends React.Component { onEditPlaylistNotes = (text: string): void => { if (this.props.main.currentPlaylistEntry) { - this.setState({ + this.props.setMainState({ currentPlaylistEntry: { ...this.props.main.currentPlaylistEntry, notes: text @@ -819,70 +818,70 @@ export class App extends React.Component { private onGameContextMenuMemo = memoizeOne((playlists: Playlist[], strings: LangContainer, selectedPlaylistId?: string) => { return (gameId: string) => { let contextButtons: MenuItemConstructorOptions[] = [ - { - type: 'submenu', - label: strings.menu.addToPlaylist, - enabled: playlists.length > 0, - submenu: UniquePlaylistMenuFactory(playlists, - strings, - (playlistId) => window.Shared.back.send(BackIn.ADD_PLAYLIST_GAME, playlistId, gameId), - selectedPlaylistId) - }, { + { + type: 'submenu', + label: strings.menu.addToPlaylist, + enabled: playlists.length > 0, + submenu: UniquePlaylistMenuFactory(playlists, + strings, + (playlistId) => window.Shared.back.send(BackIn.ADD_PLAYLIST_GAME, playlistId, gameId), + selectedPlaylistId) + }, { /* File Location */ - label: strings.menu.openFileLocation, - enabled: !window.Shared.isBackRemote, // (Local "back" only) - click: () => { - window.Shared.back.request(BackIn.GET_GAME, gameId) - .then(async (game) => { - if (game) { - const gamePath = await getGamePath(game, window.Shared.config.fullFlashpointPath, window.Shared.preferences.data.htdocsFolderPath, window.Shared.preferences.data.dataPacksFolderPath); - try { - if (gamePath) { - await fs.promises.stat(gamePath); - remote.shell.showItemInFolder(gamePath); - } else { + label: strings.menu.openFileLocation, + enabled: !window.Shared.isBackRemote, // (Local "back" only) + click: () => { + window.Shared.back.request(BackIn.GET_GAME, gameId) + .then(async (game) => { + if (game) { + const gamePath = await getGamePath(game, window.Shared.config.fullFlashpointPath, window.Shared.preferences.data.htdocsFolderPath, window.Shared.preferences.data.dataPacksFolderPath); + try { + if (gamePath) { + await fs.promises.stat(gamePath); + remote.shell.showItemInFolder(gamePath); + } else { + const opts: Electron.MessageBoxOptions = { + type: 'warning', + message: 'GameData has not been downloaded yet, cannot open the file location!', + buttons: ['Ok'], + }; + remote.dialog.showMessageBox(opts); + return; + } + } catch (error: any) { const opts: Electron.MessageBoxOptions = { type: 'warning', - message: 'GameData has not been downloaded yet, cannot open the file location!', + message: '', buttons: ['Ok'], }; - remote.dialog.showMessageBox(opts); - return; - } - } catch (error: any) { - const opts: Electron.MessageBoxOptions = { - type: 'warning', - message: '', - buttons: ['Ok'], - }; - if (error.code === 'ENOENT') { - opts.title = this.context.dialog.fileNotFound; - opts.message = ( - 'Failed to find the game file.\n'+ + if (error.code === 'ENOENT') { + opts.title = this.context.dialog.fileNotFound; + opts.message = ( + 'Failed to find the game file.\n'+ 'If you are using Flashpoint Infinity, make sure you download the game first.\n' - ); - } else { - opts.title = 'Unexpected error'; - opts.message = ( - 'Failed to check the game file.\n'+ + ); + } else { + opts.title = 'Unexpected error'; + opts.message = ( + 'Failed to check the game file.\n'+ 'If you see this, please report it back to us (a screenshot would be great)!\n\n'+ `Error: ${error}\n` - ); + ); + } + opts.message += `Path: "${gamePath}"\n\nNote: If the path is too long, some portion will be replaced with three dots ("...").`; + remote.dialog.showMessageBox(opts); } - opts.message += `Path: "${gamePath}"\n\nNote: If the path is too long, some portion will be replaced with three dots ("...").`; - remote.dialog.showMessageBox(opts); } - } - }); - }, - }, { + }); + }, + }, { /* Copy Game UUID */ - label: strings.menu.copyGameUUID, - enabled: true, - click : () => { - clipboard.writeText(gameId); - } - }, { type: 'separator' }]; + label: strings.menu.copyGameUUID, + enabled: true, + click : () => { + clipboard.writeText(gameId); + } + }, { type: 'separator' }]; // Add editing mode fields if (this.props.preferencesData.enableEditing) { @@ -1551,4 +1550,4 @@ function UniquePlaylistMenuFactory(playlists: Playlist[], strings: LangContainer } } return grouped; -} \ No newline at end of file +} diff --git a/src/renderer/components/RandomGames.tsx b/src/renderer/components/RandomGames.tsx index cec2791de..23191180a 100644 --- a/src/renderer/components/RandomGames.tsx +++ b/src/renderer/components/RandomGames.tsx @@ -62,7 +62,7 @@ export function RandomGames(props: RandomGamesProps) { const onGameContextMenu = (event: React.MouseEvent, gameId: string) => { return props.onGameContextMenu(gameId); - } + }; const render = ( <> diff --git a/src/renderer/components/pages/BrowsePage.tsx b/src/renderer/components/pages/BrowsePage.tsx index de64db3f2..d7f215798 100644 --- a/src/renderer/components/pages/BrowsePage.tsx +++ b/src/renderer/components/pages/BrowsePage.tsx @@ -859,14 +859,3 @@ function toDataURL(url: string): Promise { reader.readAsDataURL(blob); })); } - -function UniquePlaylistMenuFactory(playlists: Playlist[], onClick: (playlistId: string) => any, selectedPlaylistId?: string): MenuItemConstructorOptions[] { - return playlists.filter(p => p.id != selectedPlaylistId) - .map(p => { - return { - label: p.title || 'No Title', - enabled: true, - click: () => onClick(p.id) - }; - }); -} From e2d5773aa7d4b6187f0e8a546683a5b14cfa579a Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Wed, 29 Jun 2022 14:28:11 +0100 Subject: [PATCH 25/62] feat: SWC WIP --- .github/workflows/build.yml | 2 +- .github/workflows/coveralls.yml | 2 +- .github/workflows/latest.yml | 2 +- .github/workflows/lint.yml | 2 +- .swcrc | 15 + gulpfile.js | 4 +- package-lock.json | 1740 ++++++++++++++++++++++++------- package.json | 33 +- swcrc.back.json | 23 + tsconfig.json | 10 +- webpack.config.js | 5 +- 11 files changed, 1457 insertions(+), 381 deletions(-) create mode 100644 .swcrc create mode 100644 swcrc.back.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a3cc83628..5e4827283 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: **/node_modules key: ${{ runner.os }}-${{ hashFiles('package.json') }} - name: Install Dependencies - run: npm install + run: npm install --force if: steps.cache.outputs.cache-hit != 'true' env: CI: true diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index 9d88ea1d2..b844c1130 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -29,7 +29,7 @@ jobs: **/node_modules key: ${{ runner.os }}-${{ hashFiles('package.json') }} - name: Install Dependencies - run: npm install + run: npm install --force if: steps.cache.outputs.cache-hit != 'true' env: CI: true diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml index d4c886d88..74d053efb 100644 --- a/.github/workflows/latest.yml +++ b/.github/workflows/latest.yml @@ -35,7 +35,7 @@ jobs: **/node_modules key: ${{ runner.os }}-${{ hashFiles('package.json') }} - name: Install Dependencies - run: npm install + run: npm install --force if: steps.cache.outputs.cache-hit != 'true' env: CI: true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bf82aec18..17bad85dc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,7 +19,7 @@ jobs: **/node_modules key: ${{ runner.os }}-${{ hashFiles('package.json') }} - name: Install Dependencies - run: npm install + run: npm install --force if: steps.cache.outputs.cache-hit != 'true' env: CI: true diff --git a/.swcrc b/.swcrc new file mode 100644 index 000000000..d9296a9b1 --- /dev/null +++ b/.swcrc @@ -0,0 +1,15 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + }, + "transform": { + "react": { + "runtime": "automatic" + }, + "legacyDecorator": true, + "decoratorMetadata": true + } + } +} \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 52c2ad359..c22b371d1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -97,7 +97,7 @@ const publishInfo = [ /* ------ Watch ------ */ gulp.task('watch-back', (done) => { - execute('npx ttsc --project tsconfig.backend.json --pretty --watch', done); + execute('npx swc -w --config-file swcrc.back.json -d build src', done); }); gulp.task('watch-renderer', (done) => { @@ -113,7 +113,7 @@ gulp.task('watch-static', () => { /* ------ Build ------ */ gulp.task('build-back', (done) => { - execute('npx ttsc --project tsconfig.backend.json --pretty', done); + execute('npx swc --config-file swcrc.back.json -d build src', done); }); gulp.task('build-renderer', (done) => { diff --git a/package-lock.json b/package-lock.json index f92d4ea02..ed4008533 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,21 @@ { "name": "flashpoint-launcher", - "version": "10.1.2", + "version": "10.1.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "flashpoint-launcher", - "version": "10.1.2", + "version": "10.1.3", "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/remote": "2.0.8", - "@fortawesome/fontawesome-svg-core": "^1.2.36", - "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@fortawesome/react-fontawesome": "^0.1.15", + "@fortawesome/fontawesome-svg-core": "1.2.36", + "@fortawesome/free-solid-svg-icons": "5.15.4", + "@fortawesome/react-fontawesome": "0.1.18", "axios": "0.21.4", - "connected-react-router": "6.8.0", + "connected-react-router": "6.9.2", "electron-updater": "4.3.1", "electron-util": "0.14.2", "fast-xml-parser": "3.16.0", @@ -23,18 +23,18 @@ "fs-extra": "8.1.0", "mime": "2.4.4", "node-7z": "1.1.1", - "ps-tree": "^1.2.0", - "react": "16.13.1", + "ps-tree": "1.2.0", + "react": "17.0.2", "react-color": "2.18.0", - "react-dom": "16.13.1", - "react-markdown": "^7.0.1", - "react-redux": "7.2.0", + "react-dom": "17.0.2", + "react-markdown": "7.0.1", + "react-redux": "7.2.8", "react-router-dom": "5.1.2", "react-virtualized": "9.22.3", "redux": "4.0.5", "redux-devtools-extension": "2.13.8", "reflect-metadata": "0.1.10", - "remark-gfm": "^2.0.0", + "remark-gfm": "2.0.0", "sqlite3": "5.0.8", "tail": "2.0.3", "typeorm": "0.2.37", @@ -46,6 +46,8 @@ "yaml": "2.1.0" }, "devDependencies": { + "@swc/cli": "0.1.57", + "@swc/core": "1.2.207", "@types/electron-devtools-installer": "2.2.0", "@types/follow-redirects": "1.8.0", "@types/fs-extra": "8.1.0", @@ -53,11 +55,11 @@ "@types/mime": "2.0.1", "@types/node": "14.14.31", "@types/ps-tree": "1.1.2", - "@types/react": "16.9.34", + "@types/react": "17.0.2", "@types/react-color": "3.0.1", - "@types/react-dom": "16.9.7", + "@types/react-dom": "17.0.2", "@types/react-redux": "7.1.7", - "@types/react-router-dom": "4.3.4", + "@types/react-router-dom": "5.1.7", "@types/react-virtualized": "9.21.11", "@types/tail": "2.0.0", "@types/uuid": "3.4.5", @@ -66,6 +68,7 @@ "@types/ws": "6.0.3", "@typescript-eslint/eslint-plugin": "3.3.0", "@typescript-eslint/parser": "3.3.0", + "chokidar": "^3.5.3", "coveralls": "3.1.0", "cross-env": "7.0.2", "electron": "17.1.2", @@ -76,13 +79,13 @@ "eslint-plugin-react": "7.20.0", "gulp": "4.0.2", "jest": "28.1.0", + "swc-loader": "0.2.3", "ts-jest": "28.0.3", "ts-loader": "9.3.0", - "ts-node": "^10.8.0", - "ts-transform-paths": "2.0.1", + "ts-node": "10.8.0", "tsconfig-paths-webpack-plugin": "3.2.0", "tslint": "5.18.0", - "ttypescript": "1.5.13", + "tswc": "^1.1.1", "typedoc": "0.22.15", "typescript": "4.6.2", "webpack": "5.72.1", @@ -539,11 +542,14 @@ } }, "node_modules/@babel/runtime": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", - "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz", + "integrity": "sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==", "dependencies": { "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/runtime-corejs3": { @@ -658,9 +664,9 @@ } }, "node_modules/@electron/get": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.13.0.tgz", - "integrity": "sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", @@ -674,7 +680,7 @@ "node": ">=8.6" }, "optionalDependencies": { - "global-agent": "^2.0.2", + "global-agent": "^3.0.0", "global-tunnel-ng": "^2.7.1" } }, @@ -768,14 +774,14 @@ } }, "node_modules/@fortawesome/react-fontawesome": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.15.tgz", - "integrity": "sha512-/HFHdcoLESxxMkqZAcZ6RXDJ69pVApwdwRos/B2kiMWxDSAX2dFK8Er2/+rG+RsrzWB/dsAyjefLmemgmfE18g==", + "version": "0.1.18", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz", + "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==", "dependencies": { - "prop-types": "^15.7.2" + "prop-types": "^15.8.1" }, "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "^1.2.32", + "@fortawesome/fontawesome-svg-core": "~1 || ~6", "react": ">=16.x" } }, @@ -1880,6 +1886,41 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@npmcli/fs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", @@ -1952,6 +1993,291 @@ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.3.tgz", "integrity": "sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg==" }, + "node_modules/@swc/cli": { + "version": "0.1.57", + "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.57.tgz", + "integrity": "sha512-HxM8TqYHhAg+zp7+RdTU69bnkl4MWdt1ygyp6BDIPjTiaJVH6Dizn2ezbgDS8mnFZI1FyhKvxU/bbaUs8XhzQg==", + "dev": true, + "dependencies": { + "commander": "^7.1.0", + "fast-glob": "^3.2.5", + "slash": "3.0.0", + "source-map": "^0.7.3" + }, + "bin": { + "spack": "bin/spack.js", + "swc": "bin/swc.js" + }, + "engines": { + "node": ">= 12.13" + }, + "peerDependencies": { + "@swc/core": "^1.2.66", + "chokidar": "^3.5.1" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@swc/cli/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/cli/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@swc/core": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.207.tgz", + "integrity": "sha512-4LgdAwZv+dLQsIBaWpK4eEOpeeJlcuOM6LRkUXJLZ0CUIkZHm2zQ4N6jksm/YJYgF++mYwjM6JWwCvLpW3ZTuA==", + "dev": true, + "bin": { + "swcx": "run_swcx.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-android-arm-eabi": "1.2.207", + "@swc/core-android-arm64": "1.2.207", + "@swc/core-darwin-arm64": "1.2.207", + "@swc/core-darwin-x64": "1.2.207", + "@swc/core-freebsd-x64": "1.2.207", + "@swc/core-linux-arm-gnueabihf": "1.2.207", + "@swc/core-linux-arm64-gnu": "1.2.207", + "@swc/core-linux-arm64-musl": "1.2.207", + "@swc/core-linux-x64-gnu": "1.2.207", + "@swc/core-linux-x64-musl": "1.2.207", + "@swc/core-win32-arm64-msvc": "1.2.207", + "@swc/core-win32-ia32-msvc": "1.2.207", + "@swc/core-win32-x64-msvc": "1.2.207" + } + }, + "node_modules/@swc/core-android-arm-eabi": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.207.tgz", + "integrity": "sha512-hmMw4EaDMh8qH3DYbNPmBvrxzRPbOlF5+wMGm0NmmWQjKSCblRZUEkM4GJFtejO/DX75HHOfvh/Op+/+1UOyRQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-android-arm64": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.207.tgz", + "integrity": "sha512-jCDGX+yIb9RRQ1BOLz2o7fcJuiGz8+8l/xgKTanx+c7cNg43hm5EzlNzIxQs6oklKE/dlVw75H1Y90QJoH5a2Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.207.tgz", + "integrity": "sha512-Xp4LcmVBemLLZpB8d/XR6TnlVvtXKdFSxGmbwC7uiTl26Cji2HRjBXaQnQQfSnlKGcSB+W1sH8q9K2jDJO+hIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.207.tgz", + "integrity": "sha512-4TG7KW5FB/4Uli3ef9Hdx4igQYDv0DK0ZnGWmvteUyYPjhoR6YhIkr04CrqfHeYNOhK6oWG58K6t3ydGfnIJSw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-freebsd-x64": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.207.tgz", + "integrity": "sha512-rOj1TRbeY1ysG2cPaFdFUlup/0EJ3c1S+jWJkPK5HYt3mlvbdDu68wa8HIR6oTdbGiyMVogVIZn+je+d92Xjrg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.207.tgz", + "integrity": "sha512-hQRgp2LLr0a0aBkch7qvTAoNx3wTQYMLiPvAzlwFLwOWlv122BmfdMvxobLvwligduFZ9XBEHTdhLc/V8hsWog==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.207.tgz", + "integrity": "sha512-WF/plJ2chQXaNvcfM9OOaJbYPryr1ljhB5I0Iaz0AChoyOQfnaQ6Iq09ed1lwoHGBFS3SdrucPxu1z0vkfNIzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.207.tgz", + "integrity": "sha512-9h2HVHrxj7bB5DB6l04jvVVQCirfwIHWIctd5BqAyAI4HnYzirFaDqFntZHpZ0PfaJIa/l04hmhj1t/9f3GYww==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.207.tgz", + "integrity": "sha512-BjmpgEkT9OibBWC/ulnT2MxZpUbMZrfEWgdThCv2KiL0wYZ6ZAzkgjjArktmw6vjLLrB+1qnTysUN3RAuTrzxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.207.tgz", + "integrity": "sha512-jzBjXdjpw1+eR/GVhENreIpqS8zVezKObutiJLSSayNG3YT9MwyEz58qEHdALPhOJBjctaExr0nYCAPq9U8k2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.207.tgz", + "integrity": "sha512-zF2rID7fzgDxa0Aev92+NcSy4j1Ct87KaDhOiL/BofAOrmf274UHn6yl8HUOjbejD/WEoGG62Dv7EFlzbtVOBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.207.tgz", + "integrity": "sha512-AUI349ky8Xh4KqmySx7Yd+HdmaEU9Q67Cr5VO1ZJYEu/XRI9aiHlwLFkIb24Jio0LLddN/0GIwnDm+5Evr3deg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.207.tgz", + "integrity": "sha512-clRP+rfNcGrgu2AXb/H02iKm2tTNHPd5cgqTP2bFe9PalKh2mBFR52+g44b3ca7vwdwIYie39ZoIu7jNkKEVMA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, "node_modules/@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -2145,7 +2471,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dev": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -2262,12 +2587,12 @@ "dev": true }, "node_modules/@types/react": { - "version": "16.9.34", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.34.tgz", - "integrity": "sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.2.tgz", + "integrity": "sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA==", "dependencies": { "@types/prop-types": "*", - "csstype": "^2.2.0" + "csstype": "^3.0.2" } }, "node_modules/@types/react-color": { @@ -2280,9 +2605,9 @@ } }, "node_modules/@types/react-dom": { - "version": "16.9.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.7.tgz", - "integrity": "sha512-GHTYhM8/OwUCf254WO5xqR/aqD3gC9kSTLpopWGpQLpnw23jk44RvMHsyUSEplvRJZdHxhJGMMLF0kCPYHPhQA==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-Icd9KEgdnFfJs39KyRyr0jQ7EKhq8U6CcHRMGAS45fp5qgUvxL3ujUCfWFttUK2UErqZNj97t9gsVPNAqcwoCg==", "dev": true, "dependencies": { "@types/react": "*" @@ -2311,9 +2636,9 @@ } }, "node_modules/@types/react-router-dom": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.4.tgz", - "integrity": "sha512-xrwaWHpnxKk/TTRe7pmoGy3E4SyF/ojFqNfFJacw7OLdfLXRvGfk4r/XePVaZNVfeJzL8fcnNilPN7xOdJ/vGw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.7.tgz", + "integrity": "sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg==", "dev": true, "dependencies": { "@types/history": "*", @@ -2331,11 +2656,6 @@ "@types/react": "*" } }, - "node_modules/@types/react/node_modules/csstype": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz", - "integrity": "sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==" - }, "node_modules/@types/semver": { "version": "7.3.3", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.3.tgz", @@ -3825,12 +4145,12 @@ } }, "node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/bindings": { @@ -3859,9 +4179,9 @@ } }, "node_modules/boolean": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz", - "integrity": "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", "optional": true }, "node_modules/boxen": { @@ -4439,6 +4759,15 @@ "node": ">=0.10.0" } }, + "node_modules/cac": { + "version": "6.7.12", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.12.tgz", + "integrity": "sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/cacache": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", @@ -4645,67 +4974,102 @@ "dev": true }, "node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" }, "optionalDependencies": { - "fsevents": "^1.2.7" + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/chokidar/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/chokidar/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, "optional": true, "os": [ "darwin" ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, "engines": { - "node": ">= 4.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "node_modules/chokidar/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "engines": { + "node": ">=0.12.0" } }, - "node_modules/chokidar/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "node_modules/chokidar/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "is-extglob": "^2.1.0" + "is-number": "^7.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0" } }, "node_modules/chownr": { @@ -5402,20 +5766,23 @@ } }, "node_modules/connected-react-router": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.8.0.tgz", - "integrity": "sha512-E64/6krdJM3Ag3MMmh2nKPtMbH15s3JQDuaYJvOVXzu6MbHbDyIvuwLOyhQIuP4Om9zqEfZYiVyflROibSsONg==", + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.9.2.tgz", + "integrity": "sha512-bE8kNBiZv9Mivp7pYn9JvLH5ItTjLl45kk1/Vha0rmAK9I/ETb5JPJrAm0h2KCG9qLfv7vqU3Jo4UUDo0oJnQg==", "dependencies": { + "lodash.isequalwith": "^4.4.0", "prop-types": "^15.7.2" }, + "optionalDependencies": { + "immutable": "^3.8.1 || ^4.0.0", + "seamless-immutable": "^7.1.3" + }, "peerDependencies": { "history": "^4.7.2", - "immutable": "^3.8.1 || ^4.0.0-rc.1", - "react": "^16.4.0", + "react": "^16.4.0 || ^17.0.0", "react-redux": "^6.0.0 || ^7.1.0", "react-router": "^4.3.1 || ^5.0.0", - "redux": "^3.6.0 || ^4.0.0", - "seamless-immutable": "^7.1.3" + "redux": "^3.6.0 || ^4.0.0" } }, "node_modules/console-control-strings": { @@ -5460,17 +5827,6 @@ "node": ">=0.10.0" } }, - "node_modules/core-js": { - "version": "3.18.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.1.tgz", - "integrity": "sha512-vJlUi/7YdlCZeL6fXvWNaLUPh/id12WXj3MbkMw5uOyF0PfWPBNOCNbs53YqgrvtujLNlt9JQpruyIKkUZ+PKA==", - "hasInstallScript": true, - "optional": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-js-pure": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", @@ -7761,6 +8117,80 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/fast-glob/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/fast-glob/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -7788,6 +8218,15 @@ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", "dev": true }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fb-watchman": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", @@ -8341,7 +8780,7 @@ "node_modules/glob-stream": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", "dev": true, "dependencies": { "extend": "^3.0.0", @@ -8362,7 +8801,7 @@ "node_modules/glob-stream/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", "dev": true, "dependencies": { "is-glob": "^3.1.0", @@ -8405,6 +8844,105 @@ "node": ">= 0.10" } }, + "node_modules/glob-watcher/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/glob-watcher/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/glob-watcher/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-watcher/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -8417,13 +8955,12 @@ } }, "node_modules/global-agent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz", - "integrity": "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "optional": true, "dependencies": { "boolean": "^3.0.1", - "core-js": "^3.6.5", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", @@ -8528,9 +9065,9 @@ } }, "node_modules/globalthis": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", - "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "optional": true, "dependencies": { "define-properties": "^1.1.3" @@ -8586,11 +9123,6 @@ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", "dev": true }, - "node_modules/gud": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", - "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" - }, "node_modules/gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", @@ -9047,6 +9579,12 @@ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", "dev": true }, + "node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "optional": true + }, "node_modules/import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -9380,15 +9918,15 @@ "dev": true }, "node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "dependencies": { - "binary-extensions": "^1.0.0" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/is-buffer": { @@ -12274,6 +12812,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -12662,6 +13209,11 @@ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, + "node_modules/lodash.isequalwith": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz", + "integrity": "sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -13159,6 +13711,15 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/micromark": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.5.tgz", @@ -13719,20 +14280,6 @@ "node": ">=4" } }, - "node_modules/mini-create-react-context": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz", - "integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==", - "dependencies": { - "@babel/runtime": "^7.4.0", - "gud": "^1.0.0", - "tiny-warning": "^1.0.2" - }, - "peerDependencies": { - "prop-types": "^15.0.0", - "react": "^0.14.0 || ^15.0.0 || ^16.0.0" - } - }, "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -13905,9 +14452,9 @@ } }, "node_modules/nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", "dev": true, "optional": true }, @@ -15133,13 +15680,13 @@ } }, "node_modules/prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "node_modules/property-information": { @@ -15243,6 +15790,26 @@ "node": ">=0.6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -15268,13 +15835,12 @@ } }, "node_modules/react": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", - "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", "dependencies": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "object-assign": "^4.1.1" }, "engines": { "node": ">=0.10.0" @@ -15294,17 +15860,16 @@ } }, "node_modules/react-dom": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", - "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" + "scheduler": "^0.20.2" }, "peerDependencies": { - "react": "^16.13.1" + "react": "17.0.2" } }, "node_modules/react-is": { @@ -15351,19 +15916,19 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-redux": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz", - "integrity": "sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==", + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.8.tgz", + "integrity": "sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==", "dependencies": { - "@babel/runtime": "^7.5.5", - "hoist-non-react-statics": "^3.3.0", + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", "loose-envify": "^1.4.0", "prop-types": "^15.7.2", - "react-is": "^16.9.0" + "react-is": "^17.0.2" }, "peerDependencies": { - "react": "^16.8.3", - "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0" + "react": "^16.8.3 || ^17 || ^18" }, "peerDependenciesMeta": { "react-dom": { @@ -15374,6 +15939,22 @@ } } }, + "node_modules/react-redux/node_modules/@types/react-redux": { + "version": "7.1.24", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz", + "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, "node_modules/react-router": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz", @@ -15411,6 +15992,19 @@ "react": ">=15" } }, + "node_modules/react-router/node_modules/mini-create-react-context": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.3.tgz", + "integrity": "sha512-TtF6hZE59SGmS4U8529qB+jJFeW6asTLDIpPgvPLSCsooAwJS7QprHIFTqv9/Qh3NdLwQxFYgiHX5lqb6jqzPA==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "tiny-warning": "^1.0.3" + }, + "peerDependencies": { + "prop-types": "^15.0.0", + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/react-virtualized": { "version": "9.22.3", "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.3.tgz", @@ -15546,17 +16140,15 @@ } }, "node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=0.10" + "node": ">=8.10.0" } }, "node_modules/rechoir": { @@ -15979,6 +16571,16 @@ "node": ">= 4" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -16025,6 +16627,29 @@ "node": ">=0.12.0" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/rxjs": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", @@ -16072,9 +16697,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "node_modules/scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -16098,6 +16723,12 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/seamless-immutable": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/seamless-immutable/-/seamless-immutable-7.1.4.tgz", + "integrity": "sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A==", + "optional": true + }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -16115,7 +16746,7 @@ "node_modules/semver-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "optional": true }, "node_modules/semver-diff": { @@ -17031,6 +17662,16 @@ "es6-symbol": "^3.1.1" } }, + "node_modules/swc-loader": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.3.tgz", + "integrity": "sha512-D1p6XXURfSPleZZA/Lipb3A8pZ17fP4NObZvFCDjK/OKljroqDpPmsBdTraWhVBqUNpcWBQY1imWdoPScRlQ7A==", + "dev": true, + "peerDependencies": { + "@swc/core": "^1.2.147", + "webpack": ">=2" + } + }, "node_modules/symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", @@ -17849,12 +18490,6 @@ "node": ">=0.3.1" } }, - "node_modules/ts-transform-paths": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ts-transform-paths/-/ts-transform-paths-2.0.1.tgz", - "integrity": "sha512-L/u9jCh+eH6a3lacDEReydP6TkHnVcHNK5U366IbXCsROOIKV42BUhjmW83axyhONRKX02KGPRbBjdf7pOTGwA==", - "dev": true - }, "node_modules/tsconfig-paths": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", @@ -17899,6 +18534,17 @@ "node": ">=4" } }, + "node_modules/tsconfig-to-swcconfig": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tsconfig-to-swcconfig/-/tsconfig-to-swcconfig-2.0.1.tgz", + "integrity": "sha512-GD4rQFgw2Vdi9D81eO7F0OfKZPiBPdyB5mwbUgnGqD3jWBT8dW9TDT9vLMkbyEYM+FSv09nqmbZpzAdRDni0WQ==", + "dev": true, + "dependencies": { + "deepmerge": "^4.2.2", + "joycon": "^3.0.1", + "jsonc-parser": "^3.0.0" + } + }, "node_modules/tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", @@ -17971,21 +18617,21 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "node_modules/ttypescript": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.13.tgz", - "integrity": "sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==", + "node_modules/tswc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tswc/-/tswc-1.1.1.tgz", + "integrity": "sha512-A2x0shwgaQIapHBon5ABuBbs2j+E082s/RL8QGAnxi0LjJSxYDMZLbaDZBJrQAl3G13TcYuUXneRe0DeDMSzIA==", "dev": true, "dependencies": { - "resolve": ">=1.9.0" + "@swc/cli": "^0.1.49", + "cac": "^6.7.3", + "tsconfig-to-swcconfig": "^2.0.1" }, "bin": { - "ttsc": "bin/tsc", - "ttsserver": "bin/tsserver" + "tswc": "dist/cli.js" }, "peerDependencies": { - "ts-node": ">=8.0.2", - "typescript": ">=3.2.2" + "@swc/core": ">= 1.2.58" } }, "node_modules/tunnel": { @@ -20019,9 +20665,9 @@ } }, "@babel/runtime": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", - "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz", + "integrity": "sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -20115,14 +20761,14 @@ "dev": true }, "@electron/get": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.13.0.tgz", - "integrity": "sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", "requires": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", - "global-agent": "^2.0.2", + "global-agent": "^3.0.0", "global-tunnel-ng": "^2.7.1", "got": "^9.6.0", "progress": "^2.0.3", @@ -20198,11 +20844,11 @@ } }, "@fortawesome/react-fontawesome": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.15.tgz", - "integrity": "sha512-/HFHdcoLESxxMkqZAcZ6RXDJ69pVApwdwRos/B2kiMWxDSAX2dFK8Er2/+rG+RsrzWB/dsAyjefLmemgmfE18g==", + "version": "0.1.18", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz", + "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==", "requires": { - "prop-types": "^15.7.2" + "prop-types": "^15.8.1" } }, "@gar/promisify": { @@ -21037,6 +21683,32 @@ "tar": "^6.1.11" } }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@npmcli/fs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", @@ -21099,6 +21771,144 @@ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.3.tgz", "integrity": "sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg==" }, + "@swc/cli": { + "version": "0.1.57", + "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.57.tgz", + "integrity": "sha512-HxM8TqYHhAg+zp7+RdTU69bnkl4MWdt1ygyp6BDIPjTiaJVH6Dizn2ezbgDS8mnFZI1FyhKvxU/bbaUs8XhzQg==", + "dev": true, + "requires": { + "commander": "^7.1.0", + "fast-glob": "^3.2.5", + "slash": "3.0.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } + } + }, + "@swc/core": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.207.tgz", + "integrity": "sha512-4LgdAwZv+dLQsIBaWpK4eEOpeeJlcuOM6LRkUXJLZ0CUIkZHm2zQ4N6jksm/YJYgF++mYwjM6JWwCvLpW3ZTuA==", + "dev": true, + "requires": { + "@swc/core-android-arm-eabi": "1.2.207", + "@swc/core-android-arm64": "1.2.207", + "@swc/core-darwin-arm64": "1.2.207", + "@swc/core-darwin-x64": "1.2.207", + "@swc/core-freebsd-x64": "1.2.207", + "@swc/core-linux-arm-gnueabihf": "1.2.207", + "@swc/core-linux-arm64-gnu": "1.2.207", + "@swc/core-linux-arm64-musl": "1.2.207", + "@swc/core-linux-x64-gnu": "1.2.207", + "@swc/core-linux-x64-musl": "1.2.207", + "@swc/core-win32-arm64-msvc": "1.2.207", + "@swc/core-win32-ia32-msvc": "1.2.207", + "@swc/core-win32-x64-msvc": "1.2.207" + } + }, + "@swc/core-android-arm-eabi": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.207.tgz", + "integrity": "sha512-hmMw4EaDMh8qH3DYbNPmBvrxzRPbOlF5+wMGm0NmmWQjKSCblRZUEkM4GJFtejO/DX75HHOfvh/Op+/+1UOyRQ==", + "dev": true, + "optional": true + }, + "@swc/core-android-arm64": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.207.tgz", + "integrity": "sha512-jCDGX+yIb9RRQ1BOLz2o7fcJuiGz8+8l/xgKTanx+c7cNg43hm5EzlNzIxQs6oklKE/dlVw75H1Y90QJoH5a2Q==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-arm64": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.207.tgz", + "integrity": "sha512-Xp4LcmVBemLLZpB8d/XR6TnlVvtXKdFSxGmbwC7uiTl26Cji2HRjBXaQnQQfSnlKGcSB+W1sH8q9K2jDJO+hIA==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.207.tgz", + "integrity": "sha512-4TG7KW5FB/4Uli3ef9Hdx4igQYDv0DK0ZnGWmvteUyYPjhoR6YhIkr04CrqfHeYNOhK6oWG58K6t3ydGfnIJSw==", + "dev": true, + "optional": true + }, + "@swc/core-freebsd-x64": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.207.tgz", + "integrity": "sha512-rOj1TRbeY1ysG2cPaFdFUlup/0EJ3c1S+jWJkPK5HYt3mlvbdDu68wa8HIR6oTdbGiyMVogVIZn+je+d92Xjrg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.207.tgz", + "integrity": "sha512-hQRgp2LLr0a0aBkch7qvTAoNx3wTQYMLiPvAzlwFLwOWlv122BmfdMvxobLvwligduFZ9XBEHTdhLc/V8hsWog==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.207.tgz", + "integrity": "sha512-WF/plJ2chQXaNvcfM9OOaJbYPryr1ljhB5I0Iaz0AChoyOQfnaQ6Iq09ed1lwoHGBFS3SdrucPxu1z0vkfNIzg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.207.tgz", + "integrity": "sha512-9h2HVHrxj7bB5DB6l04jvVVQCirfwIHWIctd5BqAyAI4HnYzirFaDqFntZHpZ0PfaJIa/l04hmhj1t/9f3GYww==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.207.tgz", + "integrity": "sha512-BjmpgEkT9OibBWC/ulnT2MxZpUbMZrfEWgdThCv2KiL0wYZ6ZAzkgjjArktmw6vjLLrB+1qnTysUN3RAuTrzxg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.207.tgz", + "integrity": "sha512-jzBjXdjpw1+eR/GVhENreIpqS8zVezKObutiJLSSayNG3YT9MwyEz58qEHdALPhOJBjctaExr0nYCAPq9U8k2A==", + "dev": true, + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.207.tgz", + "integrity": "sha512-zF2rID7fzgDxa0Aev92+NcSy4j1Ct87KaDhOiL/BofAOrmf274UHn6yl8HUOjbejD/WEoGG62Dv7EFlzbtVOBw==", + "dev": true, + "optional": true + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.207.tgz", + "integrity": "sha512-AUI349ky8Xh4KqmySx7Yd+HdmaEU9Q67Cr5VO1ZJYEu/XRI9aiHlwLFkIb24Jio0LLddN/0GIwnDm+5Evr3deg==", + "dev": true, + "optional": true + }, + "@swc/core-win32-x64-msvc": { + "version": "1.2.207", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.207.tgz", + "integrity": "sha512-clRP+rfNcGrgu2AXb/H02iKm2tTNHPd5cgqTP2bFe9PalKh2mBFR52+g44b3ca7vwdwIYie39ZoIu7jNkKEVMA==", + "dev": true, + "optional": true + }, "@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -21286,7 +22096,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dev": true, "requires": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -21403,19 +22212,12 @@ "dev": true }, "@types/react": { - "version": "16.9.34", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.34.tgz", - "integrity": "sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.2.tgz", + "integrity": "sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA==", "requires": { "@types/prop-types": "*", - "csstype": "^2.2.0" - }, - "dependencies": { - "csstype": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz", - "integrity": "sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==" - } + "csstype": "^3.0.2" } }, "@types/react-color": { @@ -21428,9 +22230,9 @@ } }, "@types/react-dom": { - "version": "16.9.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.7.tgz", - "integrity": "sha512-GHTYhM8/OwUCf254WO5xqR/aqD3gC9kSTLpopWGpQLpnw23jk44RvMHsyUSEplvRJZdHxhJGMMLF0kCPYHPhQA==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-Icd9KEgdnFfJs39KyRyr0jQ7EKhq8U6CcHRMGAS45fp5qgUvxL3ujUCfWFttUK2UErqZNj97t9gsVPNAqcwoCg==", "dev": true, "requires": { "@types/react": "*" @@ -21459,9 +22261,9 @@ } }, "@types/react-router-dom": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.4.tgz", - "integrity": "sha512-xrwaWHpnxKk/TTRe7pmoGy3E4SyF/ojFqNfFJacw7OLdfLXRvGfk4r/XePVaZNVfeJzL8fcnNilPN7xOdJ/vGw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.7.tgz", + "integrity": "sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg==", "dev": true, "requires": { "@types/history": "*", @@ -22644,9 +23446,9 @@ } }, "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "bindings": { @@ -22675,9 +23477,9 @@ } }, "boolean": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz", - "integrity": "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", "optional": true }, "boxen": { @@ -23105,6 +23907,12 @@ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, + "cac": { + "version": "6.7.12", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.12.tgz", + "integrity": "sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA==", + "dev": true + }, "cacache": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", @@ -23253,55 +24061,69 @@ "dev": true }, "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "dependencies": { - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "optional": true, "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" } } } @@ -23848,11 +24670,14 @@ } }, "connected-react-router": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.8.0.tgz", - "integrity": "sha512-E64/6krdJM3Ag3MMmh2nKPtMbH15s3JQDuaYJvOVXzu6MbHbDyIvuwLOyhQIuP4Om9zqEfZYiVyflROibSsONg==", + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.9.2.tgz", + "integrity": "sha512-bE8kNBiZv9Mivp7pYn9JvLH5ItTjLl45kk1/Vha0rmAK9I/ETb5JPJrAm0h2KCG9qLfv7vqU3Jo4UUDo0oJnQg==", "requires": { - "prop-types": "^15.7.2" + "immutable": "^3.8.1 || ^4.0.0", + "lodash.isequalwith": "^4.4.0", + "prop-types": "^15.7.2", + "seamless-immutable": "^7.1.3" } }, "console-control-strings": { @@ -23893,12 +24718,6 @@ } } }, - "core-js": { - "version": "3.18.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.1.tgz", - "integrity": "sha512-vJlUi/7YdlCZeL6fXvWNaLUPh/id12WXj3MbkMw5uOyF0PfWPBNOCNbs53YqgrvtujLNlt9JQpruyIKkUZ+PKA==", - "optional": true - }, "core-js-pure": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", @@ -25671,6 +26490,64 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -25694,6 +26571,15 @@ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", "dev": true }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "fb-watchman": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", @@ -26142,7 +27028,7 @@ "glob-stream": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", "dev": true, "requires": { "extend": "^3.0.0", @@ -26160,7 +27046,7 @@ "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", "dev": true, "requires": { "is-glob": "^3.1.0", @@ -26197,16 +27083,95 @@ "just-debounce": "^1.0.0", "normalize-path": "^3.0.0", "object.defaults": "^1.1.0" + }, + "dependencies": { + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + } } }, "global-agent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz", - "integrity": "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "optional": true, "requires": { "boolean": "^3.0.1", - "core-js": "^3.6.5", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", @@ -26285,9 +27250,9 @@ } }, "globalthis": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", - "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "optional": true, "requires": { "define-properties": "^1.1.3" @@ -26331,11 +27296,6 @@ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", "dev": true }, - "gud": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", - "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" - }, "gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", @@ -26693,6 +27653,12 @@ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", "dev": true }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "optional": true + }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -26947,12 +27913,12 @@ "dev": true }, "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "^2.0.0" } }, "is-buffer": { @@ -29081,6 +30047,12 @@ } } }, + "joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -29405,6 +30377,11 @@ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, + "lodash.isequalwith": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz", + "integrity": "sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ==" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -29786,6 +30763,12 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "micromark": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.5.tgz", @@ -30101,16 +31084,6 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, - "mini-create-react-context": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz", - "integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==", - "requires": { - "@babel/runtime": "^7.4.0", - "gud": "^1.0.0", - "tiny-warning": "^1.0.2" - } - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -30247,9 +31220,9 @@ } }, "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", "dev": true, "optional": true }, @@ -31195,13 +32168,13 @@ } }, "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "property-information": { @@ -31288,6 +32261,12 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -31310,13 +32289,12 @@ } }, "react": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", - "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", "requires": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "object-assign": "^4.1.1" } }, "react-color": { @@ -31333,14 +32311,13 @@ } }, "react-dom": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", - "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" + "scheduler": "^0.20.2" } }, "react-is": { @@ -31381,15 +32358,34 @@ } }, "react-redux": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz", - "integrity": "sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==", + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.8.tgz", + "integrity": "sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==", "requires": { - "@babel/runtime": "^7.5.5", - "hoist-non-react-statics": "^3.3.0", + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", "loose-envify": "^1.4.0", "prop-types": "^15.7.2", - "react-is": "^16.9.0" + "react-is": "^17.0.2" + }, + "dependencies": { + "@types/react-redux": { + "version": "7.1.24", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz", + "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } } }, "react-router": { @@ -31407,6 +32403,17 @@ "react-is": "^16.6.0", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" + }, + "dependencies": { + "mini-create-react-context": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.3.tgz", + "integrity": "sha512-TtF6hZE59SGmS4U8529qB+jJFeW6asTLDIpPgvPLSCsooAwJS7QprHIFTqv9/Qh3NdLwQxFYgiHX5lqb6jqzPA==", + "requires": { + "@babel/runtime": "^7.12.1", + "tiny-warning": "^1.0.3" + } + } } }, "react-router-dom": { @@ -31537,14 +32544,12 @@ } }, "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "picomatch": "^2.2.1" } }, "rechoir": { @@ -31859,6 +32864,12 @@ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "optional": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -31895,6 +32906,15 @@ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "rxjs": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", @@ -31939,9 +32959,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -31958,6 +32978,12 @@ "ajv-keywords": "^3.5.2" } }, + "seamless-immutable": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/seamless-immutable/-/seamless-immutable-7.1.4.tgz", + "integrity": "sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A==", + "optional": true + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -31969,7 +32995,7 @@ "semver-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "optional": true }, "semver-diff": { @@ -32699,6 +33725,13 @@ "es6-symbol": "^3.1.1" } }, + "swc-loader": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.3.tgz", + "integrity": "sha512-D1p6XXURfSPleZZA/Lipb3A8pZ17fP4NObZvFCDjK/OKljroqDpPmsBdTraWhVBqUNpcWBQY1imWdoPScRlQ7A==", + "dev": true, + "requires": {} + }, "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", @@ -33298,12 +34331,6 @@ } } }, - "ts-transform-paths": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ts-transform-paths/-/ts-transform-paths-2.0.1.tgz", - "integrity": "sha512-L/u9jCh+eH6a3lacDEReydP6TkHnVcHNK5U366IbXCsROOIKV42BUhjmW83axyhONRKX02KGPRbBjdf7pOTGwA==", - "dev": true - }, "tsconfig-paths": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", @@ -33344,6 +34371,17 @@ "tsconfig-paths": "^3.4.0" } }, + "tsconfig-to-swcconfig": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tsconfig-to-swcconfig/-/tsconfig-to-swcconfig-2.0.1.tgz", + "integrity": "sha512-GD4rQFgw2Vdi9D81eO7F0OfKZPiBPdyB5mwbUgnGqD3jWBT8dW9TDT9vLMkbyEYM+FSv09nqmbZpzAdRDni0WQ==", + "dev": true, + "requires": { + "deepmerge": "^4.2.2", + "joycon": "^3.0.1", + "jsonc-parser": "^3.0.0" + } + }, "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", @@ -33397,13 +34435,15 @@ "tslib": "^1.8.1" } }, - "ttypescript": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.13.tgz", - "integrity": "sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==", + "tswc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tswc/-/tswc-1.1.1.tgz", + "integrity": "sha512-A2x0shwgaQIapHBon5ABuBbs2j+E082s/RL8QGAnxi0LjJSxYDMZLbaDZBJrQAl3G13TcYuUXneRe0DeDMSzIA==", "dev": true, "requires": { - "resolve": ">=1.9.0" + "@swc/cli": "^0.1.49", + "cac": "^6.7.3", + "tsconfig-to-swcconfig": "^2.0.1" } }, "tunnel": { diff --git a/package.json b/package.json index 5fc259ec9..2e9c85b11 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,11 @@ "license": "MIT", "dependencies": { "@electron/remote": "2.0.8", - "@fortawesome/fontawesome-svg-core": "^1.2.36", - "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@fortawesome/react-fontawesome": "^0.1.15", + "@fortawesome/fontawesome-svg-core": "1.2.36", + "@fortawesome/free-solid-svg-icons": "5.15.4", + "@fortawesome/react-fontawesome": "0.1.18", "axios": "0.21.4", - "connected-react-router": "6.8.0", + "connected-react-router": "6.9.2", "electron-updater": "4.3.1", "electron-util": "0.14.2", "fast-xml-parser": "3.16.0", @@ -46,18 +46,18 @@ "fs-extra": "8.1.0", "mime": "2.4.4", "node-7z": "1.1.1", - "ps-tree": "^1.2.0", - "react": "16.13.1", + "ps-tree": "1.2.0", + "react": "17.0.2", "react-color": "2.18.0", - "react-dom": "16.13.1", - "react-markdown": "^7.0.1", - "react-redux": "7.2.0", + "react-dom": "17.0.2", + "react-markdown": "7.0.1", + "react-redux": "7.2.8", "react-router-dom": "5.1.2", "react-virtualized": "9.22.3", "redux": "4.0.5", "redux-devtools-extension": "2.13.8", "reflect-metadata": "0.1.10", - "remark-gfm": "^2.0.0", + "remark-gfm": "2.0.0", "sqlite3": "5.0.8", "tail": "2.0.3", "typeorm": "0.2.37", @@ -69,6 +69,8 @@ "yaml": "2.1.0" }, "devDependencies": { + "@swc/cli": "0.1.57", + "@swc/core": "1.2.207", "@types/electron-devtools-installer": "2.2.0", "@types/follow-redirects": "1.8.0", "@types/fs-extra": "8.1.0", @@ -76,9 +78,9 @@ "@types/mime": "2.0.1", "@types/node": "14.14.31", "@types/ps-tree": "1.1.2", - "@types/react": "16.9.34", + "@types/react": "17.0.2", "@types/react-color": "3.0.1", - "@types/react-dom": "16.9.7", + "@types/react-dom": "17.0.2", "@types/react-redux": "7.1.7", "@types/react-router-dom": "5.1.7", "@types/react-virtualized": "9.21.11", @@ -89,6 +91,7 @@ "@types/ws": "6.0.3", "@typescript-eslint/eslint-plugin": "3.3.0", "@typescript-eslint/parser": "3.3.0", + "chokidar": "^3.5.3", "coveralls": "3.1.0", "cross-env": "7.0.2", "electron": "17.1.2", @@ -99,13 +102,13 @@ "eslint-plugin-react": "7.20.0", "gulp": "4.0.2", "jest": "28.1.0", + "swc-loader": "0.2.3", "ts-jest": "28.0.3", "ts-loader": "9.3.0", - "ts-node": "^10.8.0", - "ts-transform-paths": "2.0.1", + "ts-node": "10.8.0", "tsconfig-paths-webpack-plugin": "3.2.0", "tslint": "5.18.0", - "ttypescript": "1.5.13", + "tswc": "^1.1.1", "typedoc": "0.22.15", "typescript": "4.6.2", "webpack": "5.72.1", diff --git a/swcrc.back.json b/swcrc.back.json new file mode 100644 index 000000000..7a5a0f985 --- /dev/null +++ b/swcrc.back.json @@ -0,0 +1,23 @@ +{ + "exclude": ["node_modules/**"], + "module": { + "type": "commonjs", + "strict": true + }, + "jsc": { + "baseUrl": ".", + "paths": { + "@shared/*": [ "./src/shared/*" ], + "@main/*": [ "./src/main/*" ], + "@back/*": [ "./src/back/*" ], + "@renderer/*": [ "./src/renderer/*" ], + "@database/*": [ "./src/database/*" ], + "@tests/*": [ "./tests/*" ] + }, + "parser": { + "syntax": "typescript", + "decorators": true, + "target": "es6" + } + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index de20c5cc3..bb663ad60 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "target": "esnext", "lib": ["es6", "es2015", "dom"], "strict": true, - "jsx": "react", + "jsx": "react-jsx", "emitDecoratorMetadata": true, "experimentalDecorators": true, "strictPropertyInitialization": false, @@ -20,13 +20,7 @@ "@renderer/*": [ "./src/renderer/*" ], "@database/*": [ "./src/database/*" ], "@tests/*": [ "./tests/*" ], - }, - "plugins": [ - { - "name": "empty", - "transform": "ts-transform-paths", - } - ] + } }, "exclude": [ "node_modules", diff --git a/webpack.config.js b/webpack.config.js index a4b2fad7e..8af8c04ed 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -21,10 +21,11 @@ module.exports = { rules: [ { test: /\.(ts|tsx)$/, + exclude: /(node_modules)/, use: { - loader: 'ts-loader', + loader: 'swc-loader', options: { - configFile: 'tsconfig.renderer.json' + parseMap: true } } } From a99cf78548e1dd9836b0319fb85feeb5dc3699b9 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Wed, 29 Jun 2022 10:48:43 -0400 Subject: [PATCH 26/62] Fix backend build. --- gulpfile.js | 4 ++-- swcrc.back.json | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index c22b371d1..40de5749a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -97,7 +97,7 @@ const publishInfo = [ /* ------ Watch ------ */ gulp.task('watch-back', (done) => { - execute('npx swc -w --config-file swcrc.back.json -d build src', done); + execute('npx swc -w --no-swcrc --config-file swcrc.back.json -d build src', done); }); gulp.task('watch-renderer', (done) => { @@ -113,7 +113,7 @@ gulp.task('watch-static', () => { /* ------ Build ------ */ gulp.task('build-back', (done) => { - execute('npx swc --config-file swcrc.back.json -d build src', done); + execute('npx swc --no-swcrc --config-file swcrc.back.json -d build src', done); }); gulp.task('build-renderer', (done) => { diff --git a/swcrc.back.json b/swcrc.back.json index 7a5a0f985..9f306ddef 100644 --- a/swcrc.back.json +++ b/swcrc.back.json @@ -2,9 +2,11 @@ "exclude": ["node_modules/**"], "module": { "type": "commonjs", - "strict": true + "strict": true, + "noInterop": true }, "jsc": { + "target": "es2018", "baseUrl": ".", "paths": { "@shared/*": [ "./src/shared/*" ], @@ -16,8 +18,10 @@ }, "parser": { "syntax": "typescript", - "decorators": true, - "target": "es6" + "decorators": true + }, + "transform": { + "decoratorMetadata": true } } -} \ No newline at end of file +} From c0c25bbc763f1ee7ec5c0a7b4ab666f79245a8b2 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Wed, 29 Jun 2022 11:18:39 -0400 Subject: [PATCH 27/62] Downgrade @swc/core to fix compilation issue. --- package-lock.json | 226 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 114 insertions(+), 114 deletions(-) diff --git a/package-lock.json b/package-lock.json index ed4008533..8be5417b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ }, "devDependencies": { "@swc/cli": "0.1.57", - "@swc/core": "1.2.207", + "@swc/core": "1.2.205", "@types/electron-devtools-installer": "2.2.0", "@types/follow-redirects": "1.8.0", "@types/fs-extra": "8.1.0", @@ -2040,9 +2040,9 @@ } }, "node_modules/@swc/core": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.207.tgz", - "integrity": "sha512-4LgdAwZv+dLQsIBaWpK4eEOpeeJlcuOM6LRkUXJLZ0CUIkZHm2zQ4N6jksm/YJYgF++mYwjM6JWwCvLpW3ZTuA==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.205.tgz", + "integrity": "sha512-evq0/tFyYdYgOhKb//+G93fxe9zwFxtme7NL7wSiEF8+4/ON4Y5AI9eHLoqddXqs3W8Y0HQi+rJmlrkCibrseA==", "dev": true, "bin": { "swcx": "run_swcx.js" @@ -2055,25 +2055,25 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-android-arm-eabi": "1.2.207", - "@swc/core-android-arm64": "1.2.207", - "@swc/core-darwin-arm64": "1.2.207", - "@swc/core-darwin-x64": "1.2.207", - "@swc/core-freebsd-x64": "1.2.207", - "@swc/core-linux-arm-gnueabihf": "1.2.207", - "@swc/core-linux-arm64-gnu": "1.2.207", - "@swc/core-linux-arm64-musl": "1.2.207", - "@swc/core-linux-x64-gnu": "1.2.207", - "@swc/core-linux-x64-musl": "1.2.207", - "@swc/core-win32-arm64-msvc": "1.2.207", - "@swc/core-win32-ia32-msvc": "1.2.207", - "@swc/core-win32-x64-msvc": "1.2.207" + "@swc/core-android-arm-eabi": "1.2.205", + "@swc/core-android-arm64": "1.2.205", + "@swc/core-darwin-arm64": "1.2.205", + "@swc/core-darwin-x64": "1.2.205", + "@swc/core-freebsd-x64": "1.2.205", + "@swc/core-linux-arm-gnueabihf": "1.2.205", + "@swc/core-linux-arm64-gnu": "1.2.205", + "@swc/core-linux-arm64-musl": "1.2.205", + "@swc/core-linux-x64-gnu": "1.2.205", + "@swc/core-linux-x64-musl": "1.2.205", + "@swc/core-win32-arm64-msvc": "1.2.205", + "@swc/core-win32-ia32-msvc": "1.2.205", + "@swc/core-win32-x64-msvc": "1.2.205" } }, "node_modules/@swc/core-android-arm-eabi": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.207.tgz", - "integrity": "sha512-hmMw4EaDMh8qH3DYbNPmBvrxzRPbOlF5+wMGm0NmmWQjKSCblRZUEkM4GJFtejO/DX75HHOfvh/Op+/+1UOyRQ==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.205.tgz", + "integrity": "sha512-HfiuVA1JDHMSRQ8nE1DcemUgZ1PKaPwit4i7q3xin0NVbVHY1xkJyQFuLVh3VxTvGKKkF3hi8GJMVQgOXWL6kg==", "cpu": [ "arm" ], @@ -2087,9 +2087,9 @@ } }, "node_modules/@swc/core-android-arm64": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.207.tgz", - "integrity": "sha512-jCDGX+yIb9RRQ1BOLz2o7fcJuiGz8+8l/xgKTanx+c7cNg43hm5EzlNzIxQs6oklKE/dlVw75H1Y90QJoH5a2Q==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.205.tgz", + "integrity": "sha512-sRGZBV2dOnmh8gWWFo9HVOHdKa33zIsF8/8oYEGtq+2/s96UlAKltO2AA7HH9RaO/fT1tzBZStp+fEMUhDk/FA==", "cpu": [ "arm64" ], @@ -2103,9 +2103,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.207.tgz", - "integrity": "sha512-Xp4LcmVBemLLZpB8d/XR6TnlVvtXKdFSxGmbwC7uiTl26Cji2HRjBXaQnQQfSnlKGcSB+W1sH8q9K2jDJO+hIA==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.205.tgz", + "integrity": "sha512-JwVDfKS7vp7zzOQXWNwwcF41h4r3DWEpK6DQjz18WJyS1VVOcpVQGyuE7kSPjcnG01ZxBL9JPwwT353i/8IwDg==", "cpu": [ "arm64" ], @@ -2119,9 +2119,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.207.tgz", - "integrity": "sha512-4TG7KW5FB/4Uli3ef9Hdx4igQYDv0DK0ZnGWmvteUyYPjhoR6YhIkr04CrqfHeYNOhK6oWG58K6t3ydGfnIJSw==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.205.tgz", + "integrity": "sha512-malz2I+w6xFF1QyTmPGt0Y0NEMbUcrvfr5gUfZDGjxMhPPlS7k6fXucuZxVr9VVaM+JGq1SidVODmZ84jb1qHg==", "cpu": [ "x64" ], @@ -2135,9 +2135,9 @@ } }, "node_modules/@swc/core-freebsd-x64": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.207.tgz", - "integrity": "sha512-rOj1TRbeY1ysG2cPaFdFUlup/0EJ3c1S+jWJkPK5HYt3mlvbdDu68wa8HIR6oTdbGiyMVogVIZn+je+d92Xjrg==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.205.tgz", + "integrity": "sha512-/nZrG1z0T7h97AsOb/wOtYlnh4WEuNppv3XKQIMPj32YNQdMBVgpybVTVRIs1GQGZMd1/7jAy5BVQcwQjUbrLg==", "cpu": [ "x64" ], @@ -2151,9 +2151,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.207.tgz", - "integrity": "sha512-hQRgp2LLr0a0aBkch7qvTAoNx3wTQYMLiPvAzlwFLwOWlv122BmfdMvxobLvwligduFZ9XBEHTdhLc/V8hsWog==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.205.tgz", + "integrity": "sha512-mTA3vETMdBmpecUyI9waZYsp7FABhew4e81psspmFpDyfty0SLISWZDnvPAn0pSnb2fWhzKwDC5kdXHKUmLJuA==", "cpu": [ "arm" ], @@ -2167,9 +2167,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.207.tgz", - "integrity": "sha512-WF/plJ2chQXaNvcfM9OOaJbYPryr1ljhB5I0Iaz0AChoyOQfnaQ6Iq09ed1lwoHGBFS3SdrucPxu1z0vkfNIzg==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.205.tgz", + "integrity": "sha512-qGzFGryeQE+O5SFK7Nn2ESqCEnv00rnzhf11WZF9V71EZ15amIhmbcwHqvFpoRSDw8hZnqoGqfPRfoJbouptnA==", "cpu": [ "arm64" ], @@ -2183,9 +2183,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.207.tgz", - "integrity": "sha512-9h2HVHrxj7bB5DB6l04jvVVQCirfwIHWIctd5BqAyAI4HnYzirFaDqFntZHpZ0PfaJIa/l04hmhj1t/9f3GYww==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.205.tgz", + "integrity": "sha512-uLJoX9L/4Xg3sLMjAbIhzbTe5gD/MBA8VETBeEkLtgb7a0ys1kvn9xQ6qLw6A71amEPlI+VABnoTRdUEaBSV9Q==", "cpu": [ "arm64" ], @@ -2199,9 +2199,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.207.tgz", - "integrity": "sha512-BjmpgEkT9OibBWC/ulnT2MxZpUbMZrfEWgdThCv2KiL0wYZ6ZAzkgjjArktmw6vjLLrB+1qnTysUN3RAuTrzxg==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.205.tgz", + "integrity": "sha512-gQsjcYlkWKP1kceQIsoHGrOrG7ygW3ojNsSnYoZ5DG5PipRA4eeUfO9YIfrmoa29LiVNjmRPfUJa8O1UHDG5ew==", "cpu": [ "x64" ], @@ -2215,9 +2215,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.207.tgz", - "integrity": "sha512-jzBjXdjpw1+eR/GVhENreIpqS8zVezKObutiJLSSayNG3YT9MwyEz58qEHdALPhOJBjctaExr0nYCAPq9U8k2A==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.205.tgz", + "integrity": "sha512-LR5ukqBltQc++2eX3qEj/H8KtOt0V3CmtgXNOiNCUxvPDT8mYz/8MJhYOrofonND0RKfXyyPW7dRxg62ceTLSQ==", "cpu": [ "x64" ], @@ -2231,9 +2231,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.207.tgz", - "integrity": "sha512-zF2rID7fzgDxa0Aev92+NcSy4j1Ct87KaDhOiL/BofAOrmf274UHn6yl8HUOjbejD/WEoGG62Dv7EFlzbtVOBw==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.205.tgz", + "integrity": "sha512-NjcLWm4mOy78LAEt7pqFl+SLcCyqnSlUP729XRd1uRvKwt1Cwch5SQRdoaFqwf1DaEQy4H4iuGPynkfarlb1kQ==", "cpu": [ "arm64" ], @@ -2247,9 +2247,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.207.tgz", - "integrity": "sha512-AUI349ky8Xh4KqmySx7Yd+HdmaEU9Q67Cr5VO1ZJYEu/XRI9aiHlwLFkIb24Jio0LLddN/0GIwnDm+5Evr3deg==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.205.tgz", + "integrity": "sha512-+6byrRxIXgZ0zmLL6ZeX1HBBrAqvCy8MR5Yz0SO26jR8OPZXJCgZXL9BTsZO+YEG4f32ZOlZh3nnHCl6Dcb4GA==", "cpu": [ "ia32" ], @@ -2263,9 +2263,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.207.tgz", - "integrity": "sha512-clRP+rfNcGrgu2AXb/H02iKm2tTNHPd5cgqTP2bFe9PalKh2mBFR52+g44b3ca7vwdwIYie39ZoIu7jNkKEVMA==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.205.tgz", + "integrity": "sha512-RRSkyAol0c7sU9gejtrpF8TLmdYdBjLutcmQHtLKbWTm74ZLidZpF28G0J2tD7HNmzQnMpLzyoT1jW9JgLwzVg==", "cpu": [ "x64" ], @@ -21798,114 +21798,114 @@ } }, "@swc/core": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.207.tgz", - "integrity": "sha512-4LgdAwZv+dLQsIBaWpK4eEOpeeJlcuOM6LRkUXJLZ0CUIkZHm2zQ4N6jksm/YJYgF++mYwjM6JWwCvLpW3ZTuA==", - "dev": true, - "requires": { - "@swc/core-android-arm-eabi": "1.2.207", - "@swc/core-android-arm64": "1.2.207", - "@swc/core-darwin-arm64": "1.2.207", - "@swc/core-darwin-x64": "1.2.207", - "@swc/core-freebsd-x64": "1.2.207", - "@swc/core-linux-arm-gnueabihf": "1.2.207", - "@swc/core-linux-arm64-gnu": "1.2.207", - "@swc/core-linux-arm64-musl": "1.2.207", - "@swc/core-linux-x64-gnu": "1.2.207", - "@swc/core-linux-x64-musl": "1.2.207", - "@swc/core-win32-arm64-msvc": "1.2.207", - "@swc/core-win32-ia32-msvc": "1.2.207", - "@swc/core-win32-x64-msvc": "1.2.207" + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.205.tgz", + "integrity": "sha512-evq0/tFyYdYgOhKb//+G93fxe9zwFxtme7NL7wSiEF8+4/ON4Y5AI9eHLoqddXqs3W8Y0HQi+rJmlrkCibrseA==", + "dev": true, + "requires": { + "@swc/core-android-arm-eabi": "1.2.205", + "@swc/core-android-arm64": "1.2.205", + "@swc/core-darwin-arm64": "1.2.205", + "@swc/core-darwin-x64": "1.2.205", + "@swc/core-freebsd-x64": "1.2.205", + "@swc/core-linux-arm-gnueabihf": "1.2.205", + "@swc/core-linux-arm64-gnu": "1.2.205", + "@swc/core-linux-arm64-musl": "1.2.205", + "@swc/core-linux-x64-gnu": "1.2.205", + "@swc/core-linux-x64-musl": "1.2.205", + "@swc/core-win32-arm64-msvc": "1.2.205", + "@swc/core-win32-ia32-msvc": "1.2.205", + "@swc/core-win32-x64-msvc": "1.2.205" } }, "@swc/core-android-arm-eabi": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.207.tgz", - "integrity": "sha512-hmMw4EaDMh8qH3DYbNPmBvrxzRPbOlF5+wMGm0NmmWQjKSCblRZUEkM4GJFtejO/DX75HHOfvh/Op+/+1UOyRQ==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.205.tgz", + "integrity": "sha512-HfiuVA1JDHMSRQ8nE1DcemUgZ1PKaPwit4i7q3xin0NVbVHY1xkJyQFuLVh3VxTvGKKkF3hi8GJMVQgOXWL6kg==", "dev": true, "optional": true }, "@swc/core-android-arm64": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.207.tgz", - "integrity": "sha512-jCDGX+yIb9RRQ1BOLz2o7fcJuiGz8+8l/xgKTanx+c7cNg43hm5EzlNzIxQs6oklKE/dlVw75H1Y90QJoH5a2Q==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.205.tgz", + "integrity": "sha512-sRGZBV2dOnmh8gWWFo9HVOHdKa33zIsF8/8oYEGtq+2/s96UlAKltO2AA7HH9RaO/fT1tzBZStp+fEMUhDk/FA==", "dev": true, "optional": true }, "@swc/core-darwin-arm64": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.207.tgz", - "integrity": "sha512-Xp4LcmVBemLLZpB8d/XR6TnlVvtXKdFSxGmbwC7uiTl26Cji2HRjBXaQnQQfSnlKGcSB+W1sH8q9K2jDJO+hIA==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.205.tgz", + "integrity": "sha512-JwVDfKS7vp7zzOQXWNwwcF41h4r3DWEpK6DQjz18WJyS1VVOcpVQGyuE7kSPjcnG01ZxBL9JPwwT353i/8IwDg==", "dev": true, "optional": true }, "@swc/core-darwin-x64": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.207.tgz", - "integrity": "sha512-4TG7KW5FB/4Uli3ef9Hdx4igQYDv0DK0ZnGWmvteUyYPjhoR6YhIkr04CrqfHeYNOhK6oWG58K6t3ydGfnIJSw==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.205.tgz", + "integrity": "sha512-malz2I+w6xFF1QyTmPGt0Y0NEMbUcrvfr5gUfZDGjxMhPPlS7k6fXucuZxVr9VVaM+JGq1SidVODmZ84jb1qHg==", "dev": true, "optional": true }, "@swc/core-freebsd-x64": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.207.tgz", - "integrity": "sha512-rOj1TRbeY1ysG2cPaFdFUlup/0EJ3c1S+jWJkPK5HYt3mlvbdDu68wa8HIR6oTdbGiyMVogVIZn+je+d92Xjrg==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.205.tgz", + "integrity": "sha512-/nZrG1z0T7h97AsOb/wOtYlnh4WEuNppv3XKQIMPj32YNQdMBVgpybVTVRIs1GQGZMd1/7jAy5BVQcwQjUbrLg==", "dev": true, "optional": true }, "@swc/core-linux-arm-gnueabihf": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.207.tgz", - "integrity": "sha512-hQRgp2LLr0a0aBkch7qvTAoNx3wTQYMLiPvAzlwFLwOWlv122BmfdMvxobLvwligduFZ9XBEHTdhLc/V8hsWog==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.205.tgz", + "integrity": "sha512-mTA3vETMdBmpecUyI9waZYsp7FABhew4e81psspmFpDyfty0SLISWZDnvPAn0pSnb2fWhzKwDC5kdXHKUmLJuA==", "dev": true, "optional": true }, "@swc/core-linux-arm64-gnu": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.207.tgz", - "integrity": "sha512-WF/plJ2chQXaNvcfM9OOaJbYPryr1ljhB5I0Iaz0AChoyOQfnaQ6Iq09ed1lwoHGBFS3SdrucPxu1z0vkfNIzg==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.205.tgz", + "integrity": "sha512-qGzFGryeQE+O5SFK7Nn2ESqCEnv00rnzhf11WZF9V71EZ15amIhmbcwHqvFpoRSDw8hZnqoGqfPRfoJbouptnA==", "dev": true, "optional": true }, "@swc/core-linux-arm64-musl": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.207.tgz", - "integrity": "sha512-9h2HVHrxj7bB5DB6l04jvVVQCirfwIHWIctd5BqAyAI4HnYzirFaDqFntZHpZ0PfaJIa/l04hmhj1t/9f3GYww==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.205.tgz", + "integrity": "sha512-uLJoX9L/4Xg3sLMjAbIhzbTe5gD/MBA8VETBeEkLtgb7a0ys1kvn9xQ6qLw6A71amEPlI+VABnoTRdUEaBSV9Q==", "dev": true, "optional": true }, "@swc/core-linux-x64-gnu": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.207.tgz", - "integrity": "sha512-BjmpgEkT9OibBWC/ulnT2MxZpUbMZrfEWgdThCv2KiL0wYZ6ZAzkgjjArktmw6vjLLrB+1qnTysUN3RAuTrzxg==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.205.tgz", + "integrity": "sha512-gQsjcYlkWKP1kceQIsoHGrOrG7ygW3ojNsSnYoZ5DG5PipRA4eeUfO9YIfrmoa29LiVNjmRPfUJa8O1UHDG5ew==", "dev": true, "optional": true }, "@swc/core-linux-x64-musl": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.207.tgz", - "integrity": "sha512-jzBjXdjpw1+eR/GVhENreIpqS8zVezKObutiJLSSayNG3YT9MwyEz58qEHdALPhOJBjctaExr0nYCAPq9U8k2A==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.205.tgz", + "integrity": "sha512-LR5ukqBltQc++2eX3qEj/H8KtOt0V3CmtgXNOiNCUxvPDT8mYz/8MJhYOrofonND0RKfXyyPW7dRxg62ceTLSQ==", "dev": true, "optional": true }, "@swc/core-win32-arm64-msvc": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.207.tgz", - "integrity": "sha512-zF2rID7fzgDxa0Aev92+NcSy4j1Ct87KaDhOiL/BofAOrmf274UHn6yl8HUOjbejD/WEoGG62Dv7EFlzbtVOBw==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.205.tgz", + "integrity": "sha512-NjcLWm4mOy78LAEt7pqFl+SLcCyqnSlUP729XRd1uRvKwt1Cwch5SQRdoaFqwf1DaEQy4H4iuGPynkfarlb1kQ==", "dev": true, "optional": true }, "@swc/core-win32-ia32-msvc": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.207.tgz", - "integrity": "sha512-AUI349ky8Xh4KqmySx7Yd+HdmaEU9Q67Cr5VO1ZJYEu/XRI9aiHlwLFkIb24Jio0LLddN/0GIwnDm+5Evr3deg==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.205.tgz", + "integrity": "sha512-+6byrRxIXgZ0zmLL6ZeX1HBBrAqvCy8MR5Yz0SO26jR8OPZXJCgZXL9BTsZO+YEG4f32ZOlZh3nnHCl6Dcb4GA==", "dev": true, "optional": true }, "@swc/core-win32-x64-msvc": { - "version": "1.2.207", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.207.tgz", - "integrity": "sha512-clRP+rfNcGrgu2AXb/H02iKm2tTNHPd5cgqTP2bFe9PalKh2mBFR52+g44b3ca7vwdwIYie39ZoIu7jNkKEVMA==", + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.205.tgz", + "integrity": "sha512-RRSkyAol0c7sU9gejtrpF8TLmdYdBjLutcmQHtLKbWTm74ZLidZpF28G0J2tD7HNmzQnMpLzyoT1jW9JgLwzVg==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index 2e9c85b11..7ae1b9faa 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ }, "devDependencies": { "@swc/cli": "0.1.57", - "@swc/core": "1.2.207", + "@swc/core": "1.2.205", "@types/electron-devtools-installer": "2.2.0", "@types/follow-redirects": "1.8.0", "@types/fs-extra": "8.1.0", From 5a9cb543877b19928022209c9184fda3c7f8e51b Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Wed, 29 Jun 2022 16:44:35 +0100 Subject: [PATCH 28/62] fix: JS transforms --- swcrc.back.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/swcrc.back.json b/swcrc.back.json index 9f306ddef..8b535a1eb 100644 --- a/swcrc.back.json +++ b/swcrc.back.json @@ -9,11 +9,11 @@ "target": "es2018", "baseUrl": ".", "paths": { - "@shared/*": [ "./src/shared/*" ], - "@main/*": [ "./src/main/*" ], - "@back/*": [ "./src/back/*" ], - "@renderer/*": [ "./src/renderer/*" ], - "@database/*": [ "./src/database/*" ], + "@shared/*": [ "./build/shared/*" ], + "@main/*": [ "./build/main/*" ], + "@back/*": [ "./build/back/*" ], + "@renderer/*": [ "./build/renderer/*" ], + "@database/*": [ "./build/database/*" ], "@tests/*": [ "./tests/*" ] }, "parser": { From c03b28ec86f42e444236443d7ed1a1ec5722dab7 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 30 Jun 2022 11:50:55 -0400 Subject: [PATCH 29/62] Move uuid.ts to shared --- src/back/importGame.ts | 2 +- src/renderer/components/RightBrowseSidebar.tsx | 2 +- src/renderer/components/pages/BrowsePage.tsx | 2 +- src/renderer/components/pages/CuratePage.tsx | 2 +- src/renderer/components/pages/DeveloperPage.tsx | 2 +- src/renderer/context/CurationContext.ts | 2 +- src/renderer/context/ProgressContext.ts | 2 +- src/renderer/curate/importCuration.ts | 2 +- src/renderer/upgrade/UpgradeFile.ts | 2 +- src/{renderer/util => shared/utils}/uuid.ts | 0 10 files changed, 9 insertions(+), 9 deletions(-) rename src/{renderer/util => shared/utils}/uuid.ts (100%) diff --git a/src/back/importGame.ts b/src/back/importGame.ts index 0a7287ab9..a30e7096a 100644 --- a/src/back/importGame.ts +++ b/src/back/importGame.ts @@ -3,7 +3,7 @@ import { AdditionalApp } from '@database/entity/AdditionalApp'; import { Game } from '@database/entity/Game'; import { Tag } from '@database/entity/Tag'; import { TagCategory } from '@database/entity/TagCategory'; -import { validateSemiUUID } from '@renderer/util/uuid'; +import { validateSemiUUID } from '@shared/utils/uuid'; import { LOGOS, SCREENSHOTS } from '@shared/constants'; import { convertEditToCurationMetaFile } from '@shared/curate/metaToMeta'; import { CurationIndexImage, EditAddAppCuration, EditAddAppCurationMeta, EditCuration, EditCurationMeta } from '@shared/curate/types'; diff --git a/src/renderer/components/RightBrowseSidebar.tsx b/src/renderer/components/RightBrowseSidebar.tsx index 411e4dd45..118c6ce86 100644 --- a/src/renderer/components/RightBrowseSidebar.tsx +++ b/src/renderer/components/RightBrowseSidebar.tsx @@ -20,7 +20,7 @@ import { WithPreferencesProps } from '../containers/withPreferences'; import { WithSearchProps } from '../containers/withSearch'; import { getGameImagePath, getGameImageURL } from '../Util'; import { LangContext } from '../util/lang'; -import { uuid } from '../util/uuid'; +import { uuid } from '@shared/utils/uuid'; import { CheckBox } from './CheckBox'; import { ConfirmElement, ConfirmElementArgs } from './ConfirmElement'; import { DropdownInputField } from './DropdownInputField'; diff --git a/src/renderer/components/pages/BrowsePage.tsx b/src/renderer/components/pages/BrowsePage.tsx index 485b8c654..a38db6024 100644 --- a/src/renderer/components/pages/BrowsePage.tsx +++ b/src/renderer/components/pages/BrowsePage.tsx @@ -22,7 +22,7 @@ import { SearchQuery } from '../../store/search'; import { gameIdDataType, gameScaleSpan, getGamePath } from '../../Util'; import { LangContext } from '../../util/lang'; import { queueOne } from '../../util/queue'; -import { uuid } from '../../util/uuid'; +import { uuid } from '@shared/utils/uuid'; import { GameGrid } from '../GameGrid'; import { GameList } from '../GameList'; import { InputElement } from '../InputField'; diff --git a/src/renderer/components/pages/CuratePage.tsx b/src/renderer/components/pages/CuratePage.tsx index 6ee922fab..8d24c7f43 100644 --- a/src/renderer/components/pages/CuratePage.tsx +++ b/src/renderer/components/pages/CuratePage.tsx @@ -23,7 +23,7 @@ import { createCurationIndexImage, importCurationArchive, importCurationFolder, import { curationLog, readCurationMeta, showWarningBox } from '../../curate/util'; import { getPlatformIconURL } from '../../Util'; import { LangContext } from '../../util/lang'; -import { uuid } from '../../util/uuid'; +import { uuid } from '@shared/utils/uuid'; import { CheckBox } from '../CheckBox'; import { ConfirmElement, ConfirmElementArgs } from '../ConfirmElement'; import { CurateBox, getCurationWarnings } from '../CurateBox'; diff --git a/src/renderer/components/pages/DeveloperPage.tsx b/src/renderer/components/pages/DeveloperPage.tsx index 06395db47..8cc681724 100644 --- a/src/renderer/components/pages/DeveloperPage.tsx +++ b/src/renderer/components/pages/DeveloperPage.tsx @@ -16,7 +16,7 @@ import * as path from 'path'; import * as React from 'react'; import { promisify } from 'util'; import { LangContext } from '../../util/lang'; -import { validateSemiUUID } from '../../util/uuid'; +import { validateSemiUUID } from '@shared/utils/uuid'; import { LogData } from '../LogData'; import { ServiceBox } from '../ServiceBox'; import { SimpleButton } from '../SimpleButton'; diff --git a/src/renderer/context/CurationContext.ts b/src/renderer/context/CurationContext.ts index 295e19d7a..27e0b54f4 100644 --- a/src/renderer/context/CurationContext.ts +++ b/src/renderer/context/CurationContext.ts @@ -4,7 +4,7 @@ import { CurationIndexImage, EditAddAppCurationMeta, EditCuration, EditCurationM import { createContextReducer } from '../context-reducer/contextReducer'; import { ReducerAction } from '../context-reducer/interfaces'; import { createCurationIndexImage } from '../curate/importCuration'; -import { uuid } from '../util/uuid'; +import { uuid } from '@shared/utils/uuid'; const curationDefaultState: CurationsState = { defaultMetaData: undefined, diff --git a/src/renderer/context/ProgressContext.ts b/src/renderer/context/ProgressContext.ts index 48fd82d8a..4a17e7bad 100644 --- a/src/renderer/context/ProgressContext.ts +++ b/src/renderer/context/ProgressContext.ts @@ -1,7 +1,7 @@ import { createContextReducer } from '../context-reducer/contextReducer'; import { ReducerAction } from '../context-reducer/interfaces'; -import { uuid } from '../util/uuid'; +import { uuid } from '@shared/utils/uuid'; export type ProgressData = { /* Key identifier */ diff --git a/src/renderer/curate/importCuration.ts b/src/renderer/curate/importCuration.ts index 134bec5cf..dcc06f7d9 100644 --- a/src/renderer/curate/importCuration.ts +++ b/src/renderer/curate/importCuration.ts @@ -5,7 +5,7 @@ import { extractFull } from 'node-7z'; import * as path from 'path'; import { ProgressDispatch, ProgressHandle } from '../context/ProgressContext'; import { pathTo7z } from '../util/SevenZip'; -import { uuid, validateSemiUUID } from '../util/uuid'; +import { uuid, validateSemiUUID } from '@shared/utils/uuid'; import { curationLog } from './util'; /** diff --git a/src/renderer/upgrade/UpgradeFile.ts b/src/renderer/upgrade/UpgradeFile.ts index eeaacaff4..1766929aa 100644 --- a/src/renderer/upgrade/UpgradeFile.ts +++ b/src/renderer/upgrade/UpgradeFile.ts @@ -3,7 +3,7 @@ import { readJsonFile } from '@shared/Util'; import { Coerce } from '@shared/utils/Coerce'; import { IObjectParserProp, ObjectParser } from '@shared/utils/ObjectParser'; import { UpgradeStageState } from '../interfaces'; -import { uuid } from '../util/uuid'; +import { uuid } from '@shared/utils/uuid'; import { UpgradeStage } from './types'; const { str } = Coerce; diff --git a/src/renderer/util/uuid.ts b/src/shared/utils/uuid.ts similarity index 100% rename from src/renderer/util/uuid.ts rename to src/shared/utils/uuid.ts From 8ac426fd3d13340af1b725de91d641dd29aed21d Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 30 Jun 2022 12:00:34 -0400 Subject: [PATCH 30/62] Move chunkArray() to shared --- src/back/game/GameManager.ts | 2 +- src/back/game/SourceDataManager.ts | 2 +- src/back/game/TagManager.ts | 2 +- src/back/util/misc.ts | 11 ----------- src/renderer/components/pages/DeveloperPage.tsx | 2 +- src/shared/utils/misc.ts | 9 +++++++++ 6 files changed, 13 insertions(+), 15 deletions(-) create mode 100644 src/shared/utils/misc.ts diff --git a/src/back/game/GameManager.ts b/src/back/game/GameManager.ts index 571fc484d..c989f6667 100644 --- a/src/back/game/GameManager.ts +++ b/src/back/game/GameManager.ts @@ -1,5 +1,5 @@ import { ApiEmitter } from '@back/extensions/ApiEmitter'; -import { chunkArray } from '@back/util/misc'; +import { chunkArray } from '@shared/utils/misc'; import { validateSqlName, validateSqlOrder } from '@back/util/sql'; import { AdditionalApp } from '@database/entity/AdditionalApp'; import { Game } from '@database/entity/Game'; diff --git a/src/back/game/SourceDataManager.ts b/src/back/game/SourceDataManager.ts index 4ccafc1a5..d892ca28a 100644 --- a/src/back/game/SourceDataManager.ts +++ b/src/back/game/SourceDataManager.ts @@ -1,4 +1,4 @@ -import { chunkArray } from '@back/util/misc'; +import { chunkArray } from '@shared/utils/misc'; import { SourceData } from '@database/entity/SourceData'; import { getManager } from 'typeorm'; diff --git a/src/back/game/TagManager.ts b/src/back/game/TagManager.ts index 9f9c9d3bd..8cbf7f78d 100644 --- a/src/back/game/TagManager.ts +++ b/src/back/game/TagManager.ts @@ -1,6 +1,6 @@ import { SocketServer } from '@back/SocketServer'; import { ShowMessageBoxFunc } from '@back/types'; -import { chunkArray } from '@back/util/misc'; +import { chunkArray } from '@shared/utils/misc'; import { Tag } from '@database/entity/Tag'; import { TagAlias } from '@database/entity/TagAlias'; import { TagCategory } from '@database/entity/TagCategory'; diff --git a/src/back/util/misc.ts b/src/back/util/misc.ts index 8e3f8e89b..d3216c453 100644 --- a/src/back/util/misc.ts +++ b/src/back/util/misc.ts @@ -239,17 +239,6 @@ export function createPlaylistFromJson(jsonData: any, library?: string): Playlis return playlist; } - -export function chunkArray(array: T[], chunkSize: number): T[][] { - const chunks: T[][] = []; - - for (let i = 0; i < array.length; i += chunkSize) { - chunks.push(array.slice(i, i + chunkSize)); - } - - return chunks; -} - export function runService(state: BackState, id: string, name: string, basePath: string, opts: ProcessOpts, info: INamedBackProcessInfo | IBackProcessInfo): ManagedChildProcess { // Already exists, bad! if (state.services.has(id)) { diff --git a/src/renderer/components/pages/DeveloperPage.tsx b/src/renderer/components/pages/DeveloperPage.tsx index 8cc681724..a70600a32 100644 --- a/src/renderer/components/pages/DeveloperPage.tsx +++ b/src/renderer/components/pages/DeveloperPage.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/no-unused-state */ -import { chunkArray } from '@back/util/misc'; +import { chunkArray } from '@shared/utils/misc'; import { Game } from '@database/entity/Game'; import { Playlist } from '@database/entity/Playlist'; import * as remote from '@electron/remote'; diff --git a/src/shared/utils/misc.ts b/src/shared/utils/misc.ts new file mode 100644 index 000000000..4e5a02eda --- /dev/null +++ b/src/shared/utils/misc.ts @@ -0,0 +1,9 @@ +export function chunkArray(array: T[], chunkSize: number): T[][] { + const chunks: T[][] = []; + + for (let i = 0; i < array.length; i += chunkSize) { + chunks.push(array.slice(i, i + chunkSize)); + } + + return chunks; + } \ No newline at end of file From c1139a591946424fa2f6bf8e635fb5d606ccb690 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 30 Jun 2022 12:02:18 -0400 Subject: [PATCH 31/62] Move sanitizeFilename to shared --- src/back/responses.ts | 2 +- src/renderer/components/CurateBox.tsx | 2 +- src/{back/util => shared/utils}/sanitizeFilename.ts | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{back/util => shared/utils}/sanitizeFilename.ts (100%) diff --git a/src/back/responses.ts b/src/back/responses.ts index b8ff071ea..1f1d89af9 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -40,7 +40,7 @@ import { importAllMetaEdits } from './MetaEdit'; import { BackState, BareTag, TagsFile } from './types'; import { pathToBluezip } from './util/Bluezip'; import { copyError, createAddAppFromLegacy, createContainer, createGameFromLegacy, createPlaylistFromJson, exit, getCwd, pathExists, procToService, removeService, runService } from './util/misc'; -import { sanitizeFilename } from './util/sanitizeFilename'; +import { sanitizeFilename } from '@shared/utils/sanitizeFilename'; import { uuid } from './util/uuid'; const axios = axiosImport.default; diff --git a/src/renderer/components/CurateBox.tsx b/src/renderer/components/CurateBox.tsx index 08d2cdf91..46b78ebdf 100644 --- a/src/renderer/components/CurateBox.tsx +++ b/src/renderer/components/CurateBox.tsx @@ -1,4 +1,4 @@ -import { sanitizeFilename } from '@back/util/sanitizeFilename'; +import { sanitizeFilename } from '@shared/utils/sanitizeFilename'; import { Tag } from '@database/entity/Tag'; import { TagCategory } from '@database/entity/TagCategory'; import * as remote from '@electron/remote'; diff --git a/src/back/util/sanitizeFilename.ts b/src/shared/utils/sanitizeFilename.ts similarity index 100% rename from src/back/util/sanitizeFilename.ts rename to src/shared/utils/sanitizeFilename.ts From a8f3b0049828fec061d5dc99f3fe075f17e4dc11 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 30 Jun 2022 12:02:44 -0400 Subject: [PATCH 32/62] Add exclusions back in the swcrc's --- .swcrc | 7 ++++++- swcrc.back.json | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.swcrc b/.swcrc index d9296a9b1..e23bc3c6f 100644 --- a/.swcrc +++ b/.swcrc @@ -1,4 +1,9 @@ { + "exclude": [ + "src/main/**", + "src/back/**", + "tests/**" + ], "jsc": { "parser": { "syntax": "typescript", @@ -12,4 +17,4 @@ "decoratorMetadata": true } } -} \ No newline at end of file +} diff --git a/swcrc.back.json b/swcrc.back.json index 8b535a1eb..78ae9b656 100644 --- a/swcrc.back.json +++ b/swcrc.back.json @@ -1,5 +1,9 @@ { - "exclude": ["node_modules/**"], + "exclude": [ + "node_modules/**", + "src/renderer/**", + "tests/**" + ], "module": { "type": "commonjs", "strict": true, From 1c817df82b61c2528cc75370df1858e08b607047 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Thu, 30 Jun 2022 12:06:47 -0400 Subject: [PATCH 33/62] style: make the linter happy --- src/shared/utils/misc.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/shared/utils/misc.ts b/src/shared/utils/misc.ts index 4e5a02eda..38da31446 100644 --- a/src/shared/utils/misc.ts +++ b/src/shared/utils/misc.ts @@ -1,9 +1,9 @@ export function chunkArray(array: T[], chunkSize: number): T[][] { - const chunks: T[][] = []; - - for (let i = 0; i < array.length; i += chunkSize) { - chunks.push(array.slice(i, i + chunkSize)); - } - - return chunks; - } \ No newline at end of file + const chunks: T[][] = []; + + for (let i = 0; i < array.length; i += chunkSize) { + chunks.push(array.slice(i, i + chunkSize)); + } + + return chunks; +} From 0fa974828f8ba8a195556303fb2113ad62ec2fbc Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Thu, 30 Jun 2022 20:32:48 +0100 Subject: [PATCH 34/62] fix: findTags not correct for empty name tests: More tests for Game, Playlist and Tag database funcs --- jest.config.ts | 2 + package-lock.json | 570 +++++++++++++++++++++++++++- package.json | 2 + src/back/game/GameManager.ts | 7 +- src/back/game/TagManager.ts | 19 +- tests/setup.ts | 7 + tests/unit/back/DbHelper.ts | 34 ++ tests/unit/back/GameManager.test.ts | 125 ++++++ tests/unit/back/TagManager.test.ts | 69 ++++ tests/unit/back/games.ts | 212 +++++++++++ 10 files changed, 1023 insertions(+), 24 deletions(-) create mode 100644 tests/setup.ts create mode 100644 tests/unit/back/DbHelper.ts create mode 100644 tests/unit/back/GameManager.test.ts create mode 100644 tests/unit/back/TagManager.test.ts create mode 100644 tests/unit/back/games.ts diff --git a/jest.config.ts b/jest.config.ts index 03ca46b9d..9848e91da 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -7,11 +7,13 @@ const config: Config.InitialOptions = { transform: { '^.+\\.tsx?$': 'ts-jest', }, + setupFiles: ['/tests/setup.ts'], moduleNameMapper: { '^@shared(.*)$': '/src/shared/$1', '^@main(.*)$': '/src/main/$1', '^@renderer(.*)$': '/src/renderer/$1', '^@back(.*)$': '/src/back/$1', + '^@database(.*)$': '/src/database/$1', '^@tests(.*)$': '/tests/$1' }, testPathIgnorePatterns: [ diff --git a/package-lock.json b/package-lock.json index 8be5417b0..b5491783b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "devDependencies": { "@swc/cli": "0.1.57", "@swc/core": "1.2.205", + "@types/better-sqlite3": "^7.5.0", "@types/electron-devtools-installer": "2.2.0", "@types/follow-redirects": "1.8.0", "@types/fs-extra": "8.1.0", @@ -68,6 +69,7 @@ "@types/ws": "6.0.3", "@typescript-eslint/eslint-plugin": "3.3.0", "@typescript-eslint/parser": "3.3.0", + "better-sqlite3": "^7.5.3", "chokidar": "^3.5.3", "coveralls": "3.1.0", "cross-env": "7.0.2", @@ -2363,6 +2365,15 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/better-sqlite3": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.5.0.tgz", + "integrity": "sha512-G9ZbMjydW2yj1AgiPlUtdgF3a1qNpLJLudc9ynJCeJByS3XFWpmT9LT+VSHrKHFbxb31CvtYwetLTOvG9zdxdg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -4144,6 +4155,17 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/better-sqlite3": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.5.3.tgz", + "integrity": "sha512-tNIrDsThpWT8j1mg+svI1pqCYROqNOWMbB2qXVg+TJqH9UR5XnbAHyRsLZoJagldGTTqJPj/sUPVOkW0GRpYqw==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -4157,12 +4179,89 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, + "devOptional": true, "dependencies": { "file-uri-to-path": "1.0.0" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "devOptional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "devOptional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bl/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -6099,7 +6198,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=4.0.0" } @@ -7746,6 +7845,15 @@ "node": ">=0.10.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "devOptional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -8283,8 +8391,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true + "devOptional": true }, "node_modules/filelist": { "version": "1.0.4", @@ -8548,6 +8655,12 @@ "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "devOptional": true + }, "node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -8746,6 +8859,12 @@ "assert-plus": "^1.0.0" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "devOptional": true + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -14421,6 +14540,12 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "devOptional": true + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -14480,6 +14605,12 @@ "node": ">=0.10.0" } }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "devOptional": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -14532,6 +14663,18 @@ "regexp-polyfill": "^1.0.1" } }, + "node_modules/node-abi": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz", + "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==", + "devOptional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-addon-api": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", @@ -15567,6 +15710,32 @@ "node": ">=0.10.0" } }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "devOptional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -15823,7 +15992,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, + "devOptional": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -16945,6 +17114,78 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-get/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "devOptional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/simple-get/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "devOptional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -17570,7 +17811,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -17778,6 +18019,83 @@ "node": ">= 10" } }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "devOptional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "devOptional": true + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "devOptional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "devOptional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tar-stream/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/tar/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -18647,7 +18965,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, + "devOptional": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -21988,6 +22306,15 @@ "@babel/types": "^7.3.0" } }, + "@types/better-sqlite3": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.5.0.tgz", + "integrity": "sha512-G9ZbMjydW2yj1AgiPlUtdgF3a1qNpLJLudc9ynJCeJByS3XFWpmT9LT+VSHrKHFbxb31CvtYwetLTOvG9zdxdg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -23445,6 +23772,16 @@ "tweetnacl": "^0.14.3" } }, + "better-sqlite3": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.5.3.tgz", + "integrity": "sha512-tNIrDsThpWT8j1mg+svI1pqCYROqNOWMbB2qXVg+TJqH9UR5XnbAHyRsLZoJagldGTTqJPj/sUPVOkW0GRpYqw==", + "devOptional": true, + "requires": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.0" + } + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -23455,12 +23792,60 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, + "devOptional": true, "requires": { "file-uri-to-path": "1.0.0" } }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "devOptional": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "devOptional": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "devOptional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "devOptional": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -24917,7 +25302,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true + "devOptional": true }, "deep-is": { "version": "0.1.3", @@ -26191,6 +26576,12 @@ } } }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "devOptional": true + }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -26624,8 +27015,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true + "devOptional": true }, "filelist": { "version": "1.0.4", @@ -26841,6 +27231,12 @@ "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "devOptional": true + }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -26993,6 +27389,12 @@ "assert-plus": "^1.0.0" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "devOptional": true + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -31192,6 +31594,12 @@ "minimist": "^1.2.5" } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "devOptional": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -31245,6 +31653,12 @@ "to-regex": "^3.0.1" } }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "devOptional": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -31291,6 +31705,15 @@ "regexp-polyfill": "^1.0.1" } }, + "node-abi": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz", + "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==", + "devOptional": true, + "requires": { + "semver": "^7.3.5" + } + }, "node-addon-api": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", @@ -32083,6 +32506,26 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "devOptional": true, + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -32280,7 +32723,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, + "devOptional": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -33155,6 +33598,40 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "devOptional": true + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "devOptional": true, + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "devOptional": true, + "requires": { + "mimic-response": "^3.1.0" + } + }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "devOptional": true + } + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -33655,7 +34132,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "devOptional": true }, "style-to-object": { "version": "0.3.0", @@ -33820,6 +34297,67 @@ } } }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "devOptional": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + }, + "dependencies": { + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "devOptional": true + } + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "devOptional": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "devOptional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "devOptional": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, "temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", @@ -34456,7 +34994,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, + "devOptional": true, "requires": { "safe-buffer": "^5.0.1" } diff --git a/package.json b/package.json index 7ae1b9faa..75c03a51d 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "devDependencies": { "@swc/cli": "0.1.57", "@swc/core": "1.2.205", + "@types/better-sqlite3": "7.5.0", "@types/electron-devtools-installer": "2.2.0", "@types/follow-redirects": "1.8.0", "@types/fs-extra": "8.1.0", @@ -91,6 +92,7 @@ "@types/ws": "6.0.3", "@typescript-eslint/eslint-plugin": "3.3.0", "@typescript-eslint/parser": "3.3.0", + "better-sqlite3": "^7.5.3", "chokidar": "^3.5.3", "coveralls": "3.1.0", "cross-env": "7.0.2", diff --git a/src/back/game/GameManager.ts b/src/back/game/GameManager.ts index 571fc484d..98a051938 100644 --- a/src/back/game/GameManager.ts +++ b/src/back/game/GameManager.ts @@ -321,7 +321,11 @@ export async function findPlaylistByName(playlistName: string, join?: boolean): where: { title: playlistName } - } : {}; + } : { + where: { + title: playlistName + } + }; const playlistRepository = getManager().getRepository(Playlist); return playlistRepository.findOne(opts); } @@ -346,6 +350,7 @@ export async function removePlaylist(playlistId: string): Promise { const playlistRepository = getManager().getRepository(Playlist); diff --git a/src/back/game/TagManager.ts b/src/back/game/TagManager.ts index 9f9c9d3bd..bf066f81b 100644 --- a/src/back/game/TagManager.ts +++ b/src/back/game/TagManager.ts @@ -52,16 +52,21 @@ export async function findTags(name?: string, flatFilters?: string[]): Promise { return { source: '', content: '', timestamp: 0, logLevel: 0 }; }, + debug: (a, b) => { return { source: '', content: '', timestamp: 0, logLevel: 0 }; }, + info: (a, b) => { return { source: '', content: '', timestamp: 0, logLevel: 0 }; }, + warn: (a, b) => { return { source: '', content: '', timestamp: 0, logLevel: 0 }; }, + error: (a, b) => { return { source: '', content: '', timestamp: 0, logLevel: 0 }; } +}; diff --git a/tests/unit/back/DbHelper.ts b/tests/unit/back/DbHelper.ts new file mode 100644 index 000000000..90d032c5b --- /dev/null +++ b/tests/unit/back/DbHelper.ts @@ -0,0 +1,34 @@ +import { Connection, createConnection } from 'typeorm'; +import * as Database from 'better-sqlite3'; + +export class DbHelper { + + private static _instance: DbHelper; + + public static get instance(): DbHelper { + if (!this._instance) { + this._instance = new DbHelper(); + } + return this._instance; + } + + private dbConnect!: Connection; + private testdb!: any; + + async setupTestDB() { + this.testdb = new Database(':memory:', { verbose: console.log }); + this.dbConnect = await createConnection({ + name: 'default', + type: 'better-sqlite3', + database: ':memory:', + entities: ['src/database/entity/**/*.ts'], + migrations: ['src/database/migration/**/*.ts'], + synchronize: true + }); + } + + teardownTestDB() { + this.dbConnect.close(); + this.testdb.close(); + } +} diff --git a/tests/unit/back/GameManager.test.ts b/tests/unit/back/GameManager.test.ts new file mode 100644 index 000000000..03a302699 --- /dev/null +++ b/tests/unit/back/GameManager.test.ts @@ -0,0 +1,125 @@ +import { DbHelper } from './DbHelper'; +import * as GameManager from '@back/game/GameManager'; +import { Game } from '@database/entity/Game'; +import { Playlist } from '@database/entity/Playlist'; +import { getManager } from 'typeorm'; + +describe('Games', () => { + beforeAll(async () => { + await DbHelper.instance.setupTestDB(); + }); + + afterAll(() => { + DbHelper.instance.teardownTestDB(); + }); + + it('save game' , async () => { + const game = createPlaceholderGame({ + id: '12345', + title: 'Test Game' + }); + const savedGame = await GameManager.save(game); + expect(savedGame.id).toEqual(game.id); + }); + + it('find game', async() => { + const game = await GameManager.findGame('12345'); + expect(game).toBeTruthy(); + if (game) { + expect(game.title).toEqual('Test Game'); + } + }); + + it('count games', async () => { + const count = await GameManager.countGames(); + expect(count).toEqual(1); + }); +}); + +describe('Playlists', () => { + const testGame = createPlaceholderGame({ + id: '12345', + title: 'Test Game' + }); + + beforeAll(async () => { + await DbHelper.instance.setupTestDB(); + await GameManager.save(testGame); + }); + + afterAll(() => { + DbHelper.instance.teardownTestDB(); + }); + + it('create playlist', async () => { + const playlist = new Playlist(); + playlist.id = '12345'; + playlist.author = 'generic'; + playlist.title = 'testPlaylist'; + playlist.description = ''; + playlist.library = 'arcade'; + playlist.extreme = false; + const savedPlaylist = await GameManager.updatePlaylist(playlist); + expect(savedPlaylist.title).toEqual('testPlaylist'); + }); + + it('find playlist', async () => { + const playlist = await GameManager.findPlaylist('12345'); + expect(playlist).toBeTruthy(); + if (playlist) { + expect(playlist.title).toEqual('testPlaylist'); + } + }); + + it('find playlist by name', async () => { + // const playlist = await GameManager.findPlaylistByName('testPlaylist'); + const playlist = await getManager().getRepository(Playlist).findOne({ where: { + title: 'testPlaylist' + }}); + expect(playlist).toBeTruthy(); + if (playlist) { + expect(playlist.id).toEqual('12345'); + } + }); + + it('save playlist game', async () => { + + }); +}); + +function createPlaceholderGame(data: Partial): Game { + const id = '12345'; + const game = new Game(); + Object.assign(game, { + id: id, + parentGameId: id, + title: 'Test Game', + alternateTitles: '', + series: '', + developer: '', + publisher: '', + platform: '', + dateAdded: new Date().toISOString(), + dateModified: new Date().toISOString(), + broken: false, + extreme: false, + playMode: '', + status: '', + notes: '', + tags: [], + source: '', + applicationPath: '', + launchCommand: '', + releaseDate: '', + version: '', + originalDescription: '', + language: '', + library: '', + orderTitle: '', + addApps: [], + placeholder: true, + activeDataOnDisk: false, + ...data + }); + return game; +} diff --git a/tests/unit/back/TagManager.test.ts b/tests/unit/back/TagManager.test.ts new file mode 100644 index 000000000..37b464c6f --- /dev/null +++ b/tests/unit/back/TagManager.test.ts @@ -0,0 +1,69 @@ +import { DbHelper } from './DbHelper'; +import * as TagManager from '@back/game/TagManager'; +import { Tag } from '@database/entity/Tag'; + +describe('Tags', () => { + beforeAll(async () => { + await DbHelper.instance.setupTestDB(); + }); + + afterAll(() => { + DbHelper.instance.teardownTestDB(); + }); + + it('create tag' , async () => { + const tag = await TagManager.createTag('test', undefined, undefined); + expect(tag).toBeTruthy(); + if (tag) { + expect(tag.categoryId).toEqual(1); + expect(tag.aliases[0].name).toEqual('test'); + } + }); + + it('find tag', async() => { + const tag = await TagManager.findTag('test'); + expect(tag).toBeTruthy(); + if (tag) { + expect(tag.categoryId).toEqual(1); + expect(tag.aliases[0].name).toEqual('test'); + } + }); + + it('created default category', async () => { + const tagCategories = await TagManager.findTagCategories(); + expect(tagCategories).toHaveLength(1); + expect(tagCategories[0].id).toEqual(1); + expect(tagCategories[0].name).toEqual('default'); + expect(tagCategories[0].color).toEqual('#FFFFFF'); + }); + + it('create category', async () => { + const newCategory = await TagManager.createTagCategory('testCategory', 'blue'); + expect(newCategory).toBeTruthy(); + if (newCategory) { + expect(newCategory.id).toBe(2); + expect(newCategory.name).toBe('testCategory'); + expect(newCategory.color).toEqual('blue'); + } + }); + + it('create tag with category', async () => { + const tag = await TagManager.createTag('test2', 'testCategory', undefined); + expect(tag).toBeTruthy(); + if (tag) { + expect(tag.categoryId).toEqual(2); + expect(tag.aliases[0].name).toEqual('test2'); + } + }); + + it('find tags', async () => { + const tags = await TagManager.findTags(); + expect(tags).toHaveLength(2); + }); + + it('find tags by partial name', async () => { + await TagManager.createTag('wontFindMe'); + const tags = await TagManager.findTags('test'); + expect(tags).toHaveLength(2); + }); +}); diff --git a/tests/unit/back/games.ts b/tests/unit/back/games.ts new file mode 100644 index 000000000..bd20863c4 --- /dev/null +++ b/tests/unit/back/games.ts @@ -0,0 +1,212 @@ +import { Game } from '@database/entity/Game'; + +export const gameArray: Game[] = [ + { + id: 'c6ca5ded-42f4-4251-9423-55700140b096', + parentGameId: '', + title: '"Alone"', + alternateTitles: '', + series: '', + developer: 'Natpat', + publisher: 'MoFunZone; Sketchy', + dateAdded: '2019-11-24T23:39:57.629Z', + dateModified: '2021-03-07T02:08:12.000Z', + platform: 'Flash', + broken: false, + extreme: false, + playMode: 'Single Player', + status: 'Playable', + notes: '', + tagsStr: 'Platformer; Puzzle; Pixel', + source: 'https://www.newgrounds.com/portal/view/578326', + applicationPath: 'FPSoftware\\Flash\\flashplayer_32_sa.exe', + launchCommand: 'http://uploads.ungrounded.net/578000/578326_Preloader.swf', + releaseDate: '2011-08-28', + version: '', + originalDescription: + 'Play as a penguin and a tortoise solving puzzles using a jetpack and a gun in this challenging, funky, colourful platformer!\nPlay alongside a beautiful soundtrack with 3 different songs, and funky graphics. Can you beat all 20 levels?\nI\'m so glad I\'m finally getting this out. Finally! :D Enjoy :)', + language: 'en', + library: 'arcade', + orderTitle: '"alone"', + activeDataId: undefined, + activeDataOnDisk: false, + tags: [], + addApps: [], + placeholder: false, + updateTagsStr: new Game().updateTagsStr, + }, + { + id: '6fabbb7a-f614-455c-a239-360b6b69ea24', + // This is not the real parent game id. It doesn't matter, deal with it. + parentGameId: 'c6ca5ded-42f4-4251-9423-55700140b096', + title: '"Game feel" demo', + alternateTitles: '', + series: '', + developer: 'Sebastien Benard', + publisher: 'Deepnight.net', + dateAdded: '2021-01-25T23:30:49.267Z', + dateModified: '2022-04-03T17:37:21.000Z', + platform: 'HTML5', + broken: false, + extreme: false, + playMode: 'Single Player', + status: 'Playable', + notes: '', + tagsStr: + 'Demonstration; Action; Platformer; Shooter; Pixel; Side-Scrolling', + source: 'http://deepnight.net/games/game-feel/', + applicationPath: 'FPSoftware\\Basilisk-Portable\\Basilisk-Portable.exe', + launchCommand: 'http://deepnight.net/games/game-feel/', + releaseDate: '2019-12-19', + version: '', + originalDescription: + 'This prototype is not exactly an actual game. It was developed to serve as a demonstration for a “Game feel” talk in 2019 at the ENJMIN school.\n\nIt shows the impact of small details on the overall quality of a game.\n\nYou will need a GAMEPAD to test it. You can enable or disable game features in this demo by pressing the START button.\n\nGAMEPAD is required to play\nA\njump\nB\ndash\nX\nshoot\nSTART\nenable/disable features\nSELECT\nrestart', + language: 'en', + library: 'arcade', + orderTitle: '', + activeDataId: 8656, + activeDataOnDisk: false, + tags: [], + addApps: [], + placeholder: false, + updateTagsStr: new Game().updateTagsStr, + }, + { + id: '4b1c582f-c953-48d4-b839-22897adc8406', + parentGameId: '', + title: '"Eight Planets and a Dwarf" Sudoku', + alternateTitles: '', + series: '', + developer: 'Julia Genyuk; Dave Fisher', + publisher: 'Windows to the Universe', + dateAdded: '2022-02-16T03:34:35.697Z', + dateModified: '2022-02-16T04:08:14.000Z', + platform: 'Flash', + broken: false, + extreme: false, + playMode: 'Single Player', + status: 'Playable', + notes: '', + tagsStr: 'Space; Sudoku', + source: 'https://www.windows2universe.org/games/sudoku/sudoku.html', + applicationPath: 'FPSoftware\\Flash\\flashplayer_32_sa.exe', + launchCommand: + 'http://www.windows2universe.org/games/sudoku/planets_sudoku.swf', + releaseDate: '', + version: '', + originalDescription: '', + language: 'en', + library: 'arcade', + orderTitle: '', + activeDataId: 96874, + activeDataOnDisk: true, + tags: [], + addApps: [], + placeholder: false, + updateTagsStr: new Game().updateTagsStr, + }, + { + id: 'f82c01e0-a30e-49e2-84b2-9b45c437eda6', + parentGameId: '', + title: '"Mind Realm"', + alternateTitles: '', + series: '', + developer: 'Wisdomchild', + publisher: 'WET GAMIN', + dateAdded: '2021-09-03T13:42:06.533Z', + dateModified: '2021-12-13T02:09:25.000Z', + platform: 'HTML5', + broken: false, + extreme: false, + playMode: 'Single Player', + status: 'Playable', + notes: '', + tagsStr: 'Puzzle; Score-Attack; Pixel; Arcade', + source: 'http://wetgamin.com/mindrealm.php', + applicationPath: 'FPSoftware\\Basilisk-Portable\\Basilisk-Portable.exe', + launchCommand: 'http://wetgamin.com/html5/mindrealm/index.html', + releaseDate: '', + version: '', + originalDescription: '', + language: 'en', + library: 'arcade', + orderTitle: '', + activeDataId: 44546, + activeDataOnDisk: true, + tags: [], + addApps: [], + placeholder: false, + updateTagsStr: new Game().updateTagsStr, + }, + { + id: 'b7dbd71b-d099-4fdf-9127-6e0a341b7f2d', + parentGameId: '', + title: '"Build a Tree" Dendrochronology Activity', + alternateTitles: '', + series: '', + developer: '', + publisher: 'Windows to the Universe', + dateAdded: '2022-02-18T04:20:19.301Z', + dateModified: '2022-02-18T04:31:29.000Z', + platform: 'Flash', + broken: false, + extreme: false, + playMode: 'Single Player', + status: 'Playable', + notes: '', + tagsStr: 'Creative; Educational; Object Creator; Toy', + source: + 'https://www.windows2universe.org/earth/climate/dendrochronology_build_tree.html', + applicationPath: 'FPSoftware\\Flash\\flashplayer_32_sa.exe', + launchCommand: + 'http://www.windows2universe.org/earth/climate/images/dendrochronology_build_tree.swf', + releaseDate: '', + version: '', + originalDescription: + 'The interactive diagram below demonstrates a very simple model of tree ring growth.\n\nSelect a temperature range (Normal, Cool, or Warm) and a precipitation amount (Normal, Dry, or Wet) for the coming year. Click the "Add Yearly Growth" button. The tree (which you are viewing a cross-section of the trunk of) grows one year\'s worth, adding a new ring.\n\nAdd some rings while varying the temperature and precipitation. Which of these factors has a stronger influence on the growth of the type of tree being modeled here?\n\nUse the "Reset" button to start over.\n\nThe "Show Specimen Tree" button displays a section of an "actual" tree specimen. Can you model the annual climate during each year of the specimen tree\'s life, matching your diagram with the specimen, to determine the climate history "written" in the rings of the specimen tree? (The "answer" is listed below, lower down on this page).', + language: 'en', + library: 'arcade', + orderTitle: '', + activeDataId: 97171, + activeDataOnDisk: false, + tags: [], + addApps: [], + placeholder: false, + updateTagsStr: new Game().updateTagsStr, + }, + { + id: 'a95d0ff7-3ee9-460f-a4f9-0e0c77764d13', + parentGameId: '', + title: '!BETA! little bullet hell', + alternateTitles: '', + series: '', + developer: 'leonidoss341', + publisher: 'Newgrounds', + dateAdded: '2021-08-04T06:09:06.114Z', + dateModified: '2021-08-04T06:09:44.000Z', + platform: 'Flash', + broken: false, + extreme: false, + playMode: 'Single Player', + status: 'Playable', + notes: '', + tagsStr: 'Action; Shooter', + source: 'https://www.newgrounds.com/portal/view/624363', + applicationPath: 'FPSoftware\\Flash\\flashplayer_32_sa.exe', + launchCommand: + 'http://uploads.ungrounded.net/624000/624363_touhou_project_tutorial.swf', + releaseDate: '2013-08-29', + version: 'Beta', + originalDescription: + 'Testing bullets. Just some fun\n\nAuthor Comments:\n\nWARNING! It\'s just beta, no need to say me that you wanna game, please tell me: everything work good or not? Also i want to know about graphic.\nMovement: keys or WASD, Shift-focus, Z-shooting.', + language: 'en', + library: 'arcade', + orderTitle: '', + activeDataId: 32195, + activeDataOnDisk: true, + tags: [], + addApps: [], + placeholder: false, + updateTagsStr: new Game().updateTagsStr, + }, +]; From 402bd81505fd367e333a43bd0c4ee0270b0b8825 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Thu, 30 Jun 2022 22:10:46 +0100 Subject: [PATCH 35/62] feat: Searching + Animated Spinner --- lang/en.json | 3 +- src/renderer/app.tsx | 2 +- src/renderer/components/Footer.tsx | 2 +- src/renderer/components/Spinner.tsx | 11 ++ src/renderer/components/pages/BrowsePage.tsx | 126 +++++++++++-------- src/renderer/store/main/reducer.ts | 1 + src/shared/lang.ts | 1 + static/window/styles/core.css | 12 ++ static/window/styles/fancy.css | 82 +++++++++++- 9 files changed, 186 insertions(+), 54 deletions(-) create mode 100644 src/renderer/components/Spinner.tsx diff --git a/lang/en.json b/lang/en.json index 39e903d52..c16a0bddc 100644 --- a/lang/en.json +++ b/lang/en.json @@ -428,7 +428,8 @@ "complete": "Complete", "exportMetaEditTitle": "Export Meta Edit", "exportMetaEditDesc": "Select all properties to export:", - "showImage": "Show Image" + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "View Thumbnail in Folder", diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index c89131b00..e515ca6da 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -653,7 +653,7 @@ export class App extends React.Component { randomGames: this.props.main.randomGames, rollRandomGames: this.rollRandomGames, updateView: this.updateView, - gamesTotal: view && view.total || 0, + gamesTotal: view && view.total, playlists: playlists, suggestions: this.props.main.suggestions, appPaths: this.props.main.appPaths, diff --git a/src/renderer/components/Footer.tsx b/src/renderer/components/Footer.tsx index 3cb33d688..cebe323f3 100644 --- a/src/renderer/components/Footer.tsx +++ b/src/renderer/components/Footer.tsx @@ -47,7 +47,7 @@ export class Footer extends React.Component { { currentLabel && strings.searchResults ? ( <>

    |

    -

    {`${strings.searchResults}: ${view && view.total || 0}`}

    +

    {`${strings.searchResults}: ${(view && view.total) ? view.total : this.context.misc.searching}`}

    ) : undefined }
    diff --git a/src/renderer/components/Spinner.tsx b/src/renderer/components/Spinner.tsx new file mode 100644 index 000000000..8f231740e --- /dev/null +++ b/src/renderer/components/Spinner.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; + +/** + * Source: https://loading.io/css/ + * License: CC0 +*/ +export function Spinner() { + return ( +
    + ); +} diff --git a/src/renderer/components/pages/BrowsePage.tsx b/src/renderer/components/pages/BrowsePage.tsx index 485b8c654..fa8a0e8a0 100644 --- a/src/renderer/components/pages/BrowsePage.tsx +++ b/src/renderer/components/pages/BrowsePage.tsx @@ -23,10 +23,12 @@ import { gameIdDataType, gameScaleSpan, getGamePath } from '../../Util'; import { LangContext } from '../../util/lang'; import { queueOne } from '../../util/queue'; import { uuid } from '../../util/uuid'; +import { FancyAnimation } from '../FancyAnimation'; import { GameGrid } from '../GameGrid'; import { GameList } from '../GameList'; import { InputElement } from '../InputField'; import { ResizableSidebar, SidebarResizeEvent } from '../ResizableSidebar'; +import { Spinner } from '../Spinner'; type Pick = { [P in K]: T[P]; }; type StateCallback1 = Pick; @@ -216,57 +218,81 @@ export class BrowsePage extends React.Component this.onExportPlaylist(strings, playlistId)} onContextMenu={this.onPlaylistContextMenuMemo(strings, this.state.isEditingPlaylist, this.props.selectedPlaylistId)} /> -
    - {(() => { - if (this.props.preferencesData.browsePageLayout === BrowsePageLayout.grid) { + {this.props.gamesTotal != undefined ? ( +
    + {(() => { + if (this.props.preferencesData.browsePageLayout === BrowsePageLayout.grid) { // (These are kind of "magic numbers" and the CSS styles are designed to fit with them) - const height: number = calcScale(350, this.props.preferencesData.browsePageGameScale); - const width: number = (height * 0.666) | 0; - return ( - - ); - } else { - const height: number = calcScale(30, this.props.preferencesData.browsePageGameScale); - return ( - - ); - } - })()} -
    + const height: number = calcScale(350, this.props.preferencesData.browsePageGameScale); + const width: number = (height * 0.666) | 0; + return ( + + ); + } else { + const height: number = calcScale(30, this.props.preferencesData.browsePageGameScale); + return ( + + ); + } + })()} +
    + ) : ( +
    +
    +
    + ( +
    + {strings.misc.searching} +
    + )} + fancyRender={() => ( + <> +
    + {strings.misc.searching} +
    + + + )} + /> +
    +
    +
    + )} Date: Thu, 30 Jun 2022 17:41:41 -0400 Subject: [PATCH 36/62] Fix arguments of playlist broadcast wrapper return function. --- src/back/extensions/ApiImplementation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/back/extensions/ApiImplementation.ts b/src/back/extensions/ApiImplementation.ts index 431f89e2b..ceafe0e14 100644 --- a/src/back/extensions/ApiImplementation.ts +++ b/src/back/extensions/ApiImplementation.ts @@ -111,7 +111,7 @@ export function createApiFactory(extId: string, extManifest: IExtensionManifest, function broadcastPlaylistWrapper(cb: (arg: T) => Promise): (args: T) => Promise; function broadcastPlaylistWrapper(cb: (arg: T, arg2: T2) => Promise): (arg: T, arg2: T2) => Promise; function broadcastPlaylistWrapper(cb: (...args: any[]) => Promise): (args: any[]) => Promise { - return async (args: any[]) => { + return async (...args: any[]) => { return cb(...args) .then(async (r) => { state.socketServer.broadcast(BackOut.PLAYLISTS_CHANGE, await GameManager.findPlaylists(state.preferences.browsePageShowExtreme)); From 9b3bfd874f263c766a721a56f5157d7f192a96d4 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Fri, 1 Jul 2022 11:29:10 +0100 Subject: [PATCH 37/62] feat: Fancy Searching Spinner --- src/renderer/components/pages/BrowsePage.tsx | 121 +++++++++++-------- 1 file changed, 71 insertions(+), 50 deletions(-) diff --git a/src/renderer/components/pages/BrowsePage.tsx b/src/renderer/components/pages/BrowsePage.tsx index d116e5e2b..bcad90c62 100644 --- a/src/renderer/components/pages/BrowsePage.tsx +++ b/src/renderer/components/pages/BrowsePage.tsx @@ -21,10 +21,12 @@ import { SearchQuery } from '../../store/search'; import { gameIdDataType, gameScaleSpan } from '../../Util'; import { LangContext } from '../../util/lang'; import { queueOne } from '../../util/queue'; +import { FancyAnimation } from '../FancyAnimation'; import { GameGrid } from '../GameGrid'; import { GameList } from '../GameList'; import { InputElement } from '../InputField'; import { ResizableSidebar, SidebarResizeEvent } from '../ResizableSidebar'; +import { Spinner } from '../Spinner'; type Pick = { [P in K]: T[P]; }; type StateCallback1 = Pick; @@ -216,57 +218,76 @@ export class BrowsePage extends React.Component this.onExportPlaylist(strings, playlistId)} onContextMenu={this.onPlaylistContextMenuMemo(strings, this.state.isEditingPlaylist, this.props.selectedPlaylistId)} /> -
    - {(() => { - if (this.props.preferencesData.browsePageLayout === BrowsePageLayout.grid) { + { this.props.gamesTotal ? ( +
    + {(() => { + if (this.props.preferencesData.browsePageLayout === BrowsePageLayout.grid) { // (These are kind of "magic numbers" and the CSS styles are designed to fit with them) - const height: number = calcScale(350, this.props.preferencesData.browsePageGameScale); - const width: number = (height * 0.666) | 0; - return ( - - ); - } else { - const height: number = calcScale(30, this.props.preferencesData.browsePageGameScale); - return ( - - ); - } - })()} -
    + const height: number = calcScale(350, this.props.preferencesData.browsePageGameScale); + const width: number = (height * 0.666) | 0; + return ( + + ); + } else { + const height: number = calcScale(30, this.props.preferencesData.browsePageGameScale); + return ( + + ); + } + })()} +
    + ) : ( +
    +
    +
    + {strings.misc.searching}
    + )} + fancyRender={( + <> +
    {strings.misc.searching}
    + + + )}/> +
    +
    +
    + )}
    ); } From b6ab2530d7c6244028354a870931c5ea2fcfcfc1 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Fri, 1 Jul 2022 11:48:42 +0100 Subject: [PATCH 38/62] fix: Exts deactivation won't crash exit procedure --- src/back/extensions/ExtensionService.ts | 16 ++++++++++------ src/back/responses.ts | 3 +++ src/back/util/misc.ts | 8 +++++++- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/back/extensions/ExtensionService.ts b/src/back/extensions/ExtensionService.ts index 9473c0c0f..dc83f7042 100644 --- a/src/back/extensions/ExtensionService.ts +++ b/src/back/extensions/ExtensionService.ts @@ -151,13 +151,17 @@ export class ExtensionService { const extData = this._extensionData[ext.id]; const entryPath = getExtensionEntry(ext); if (entryPath) { - const extModule: ExtensionModule = await import(entryPath); - if (extModule.deactivate) { - try { - await Promise.resolve(extModule.deactivate.apply(global)); - } catch (error) { - log.error('Extensions', `[${ext.manifest.displayName || ext.manifest.name}] Error in deactivation function.\n${error}'`); + try { + const extModule: ExtensionModule = await import(entryPath); + if (extModule.deactivate) { + try { + await Promise.resolve(extModule.deactivate.apply(global)); + } catch (error) { + log.error('Extensions', `[${ext.manifest.displayName || ext.manifest.name}] Error in deactivation function.\n${error}'`); + } } + } catch (error) { + log.error('Extensions', `[${ext.manifest.displayName || ext.manifest.name}] Error importing entry path.\n${error}'`); } } // Dispose of all subscriptions the extension made diff --git a/src/back/responses.ts b/src/back/responses.ts index 1f1d89af9..9de3d5235 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -1174,9 +1174,12 @@ export function registerRequestCallbacks(state: BackState): void { }); state.socketServer.register(BackIn.QUIT, async (event) => { + console.log('Exiting...'); // Unload all extensions before quitting await state.extensionsService.unloadAll(); + console.log(' - Extensions Unloaded'); state.socketServer.broadcast(BackOut.QUIT); + console.log(' - Quit Broadcast Sent'); exit(state); }); diff --git a/src/back/util/misc.ts b/src/back/util/misc.ts index d3216c453..390a31921 100644 --- a/src/back/util/misc.ts +++ b/src/back/util/misc.ts @@ -111,16 +111,19 @@ export async function exit(state: BackState): Promise { await service.kill(); } } + console.log(' - Managed Services Killed'); // Run stop commands for (let i = 0; i < state.serviceInfo.stop.length; i++) { execProcess(state, state.serviceInfo.stop[i], true); } + console.log(' - Service Info Stop Commands Run'); } state.languageWatcher.abort(); for (const watcher of state.themeState.watchers) { watcher.abort(); } + console.log(' - Watchers Aborted'); Promise.all([ // Close WebSocket server @@ -150,7 +153,10 @@ export async function exit(state: BackState): Promise { } } })(), - ]).then(() => { process.exit(); }); + ]).then(() => { + console.log(' - Cleanup Complete, Exiting Process...'); + process.exit(); + }); } } From 0dd2cc56267aec50b7e1d337d364e69fbc68a6a8 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Fri, 1 Jul 2022 12:25:44 +0100 Subject: [PATCH 39/62] fix: Extension string configs editable fix: ConfigBoxInput styling --- src/renderer/components/ConfigBoxInput.tsx | 4 +++- src/renderer/components/pages/ConfigPage.tsx | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/renderer/components/ConfigBoxInput.tsx b/src/renderer/components/ConfigBoxInput.tsx index 14163046c..2b3e5263a 100644 --- a/src/renderer/components/ConfigBoxInput.tsx +++ b/src/renderer/components/ConfigBoxInput.tsx @@ -6,7 +6,9 @@ export type ConfigBoxInputProps = ConfigBoxProps & InputFieldProps; export function ConfigBoxInput(props: ConfigBoxInputProps) { return ( - + ); diff --git a/src/renderer/components/pages/ConfigPage.tsx b/src/renderer/components/pages/ConfigPage.tsx index fee73f304..a222e6e0d 100644 --- a/src/renderer/components/pages/ConfigPage.tsx +++ b/src/renderer/components/pages/ConfigPage.tsx @@ -1062,6 +1062,7 @@ function renderExtConfigProp(key: string, prop: ExtConfigurationProp, value: any title={prop.title} description={prop.description} text={value} + editable={true} onChange={event => setExtConfigValue(key, event.target.value)} /> ); } From 0922d2851d4dd6449151df750eafe6c20456d9c8 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Fri, 1 Jul 2022 12:27:27 +0100 Subject: [PATCH 40/62] chore: Ignore .DS_Store mac files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3f7d0ba99..5b4d3d4d7 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ Data/flashpoint.sqlite /.coveralls.yml /coverage /tests/result + +# Mac +.DS_Store \ No newline at end of file From aac7739a7377fe22aec2599300c5de2a922d2cae Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Fri, 1 Jul 2022 13:23:41 +0100 Subject: [PATCH 41/62] feat: Coloured resizable divider on hover refactor: Widened resizable divider by 2px (6px) fix: Irrelevant prefs diffs won't update search --- src/renderer/app.tsx | 2 +- src/renderer/components/ResizableSidebar.tsx | 15 ++++++++++++--- src/renderer/components/pages/HomePage.tsx | 2 -- static/window/styles/core.css | 9 ++++++++- static/window/styles/fancy.css | 10 ++++++++-- 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index 98f45a9f1..3233853aa 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -539,7 +539,7 @@ export class App extends React.Component { view.query.orderBy !== this.props.preferencesData.gamesOrderBy || view.query.orderReverse !== this.props.preferencesData.gamesOrder || prevProps.main.playlists !== this.props.main.playlists || - view.tagFilters !== this.props.preferencesData.tagFilters || + JSON.stringify(view.tagFilters) !== JSON.stringify(this.props.preferencesData.tagFilters) || view.query.searchLimit !== this.props.preferencesData.searchLimit) { this.setViewQuery(library); } diff --git a/src/renderer/components/ResizableSidebar.tsx b/src/renderer/components/ResizableSidebar.tsx index dc4ef918d..05c18c750 100644 --- a/src/renderer/components/ResizableSidebar.tsx +++ b/src/renderer/components/ResizableSidebar.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { FancyAnimation } from './FancyAnimation'; type ResizableSidebarProps = { className?: string; @@ -80,9 +81,17 @@ export class ResizableSidebar extends React.Component + + )} + fancyRender={( +
    + )}/> ); } diff --git a/src/renderer/components/pages/HomePage.tsx b/src/renderer/components/pages/HomePage.tsx index 52a48c7d4..a76c6521a 100644 --- a/src/renderer/components/pages/HomePage.tsx +++ b/src/renderer/components/pages/HomePage.tsx @@ -348,8 +348,6 @@ export function HomePage(props: HomePageProps) { ); }, [strings, props.preferencesData.minimizedHomePageBoxes, toggleMinimizeBox]); - log.debug('Launcher', 'Selected - ' + (props.selectedGameId || 'None')); - const renderedRandomGames = React.useMemo(() => ( Date: Sun, 3 Jul 2022 17:14:14 +0100 Subject: [PATCH 42/62] feat: Startup helper for invalid prefs file feat: 'open' protocol for sidebar --- src/back/index.ts | 56 +++++++++-- src/main/Main.ts | 22 ++++- src/renderer/app.tsx | 110 +++++++++++++++------- src/renderer/store/main/enums.ts | 4 +- src/renderer/store/main/reducer.ts | 20 ++++ src/renderer/store/main/types.ts | 5 + src/shared/preferences/PreferencesFile.ts | 29 ++++-- 7 files changed, 194 insertions(+), 52 deletions(-) diff --git a/src/back/index.ts b/src/back/index.ts index 2afa30bc6..261bad74a 100644 --- a/src/back/index.ts +++ b/src/back/index.ts @@ -23,8 +23,9 @@ import { IBackProcessInfo, RecursivePartial } from '@shared/interfaces'; import { getDefaultLocalization, LangFileContent } from '@shared/lang'; import { ILogEntry, LogLevel } from '@shared/Log/interface'; import { PreferencesFile } from '@shared/preferences/PreferencesFile'; +import { defaultPreferencesData } from '@shared/preferences/util'; import { Theme } from '@shared/ThemeFile'; -import { createErrorProxy, removeFileExtension, stringifyArray } from '@shared/Util'; +import { createErrorProxy, deepCopy, removeFileExtension, stringifyArray } from '@shared/Util'; import * as child_process from 'child_process'; import { EventEmitter } from 'events'; import * as flashpoint from 'flashpoint-launcher'; @@ -276,15 +277,56 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { ? path.resolve(state.configFolder, state.config.flashpointPath) : state.config.flashpointPath; - // @TODO Figure out why async loading isn't always working? + const loadPrefs = async (): Promise => { + // @TODO Figure out why async loading isn't always working? + const prefsFilePath = path.join(state.config.flashpointPath, PREFERENCES_FILENAME); + try { + state.preferences = await PreferencesFile.readOrCreateFile(prefsFilePath); + } catch (e) { + console.log('Failed to load preferences, prompting for defaults'); + const res = await new Promise((resolve) => { + process.once('message', (msg) => { + resolve(Number(msg)); + }); + send({preferencesRefresh: true}); + }); + console.log('Response - ' + res); + + if (res === 1) { + throw 'User cancelled.'; + } + + // Check for custom default file + const overridePath = path.join(state.config.flashpointPath, '.preferences.defaults.json'); + console.log('Checking for prefs override at ' + overridePath); + try { + await fs.promises.copyFile(overridePath, prefsFilePath); + console.log('Copied default preferences (override)'); + // File copied, try loading again + return loadPrefs(); + } catch (err) { + console.log(err); + // Failed to copy overrides, use defaults + const defaultPrefs = deepCopy(defaultPreferencesData); + state.preferences = defaultPrefs; + try { + await PreferencesFile.saveFile(prefsFilePath, state.preferences); + console.log('Copied default preferences'); + } catch (err) { + send({quit: true, errorMessage: 'Failed to save default preferences file? Quitting...'}); + return; + } + } + } + }; + try { - state.preferences = await PreferencesFile.readOrCreateFile(path.join(state.config.flashpointPath, PREFERENCES_FILENAME)); - } catch (e) { - console.log(e); - // Fatal, quit. - send({quit: true, errorMessage: 'Invalid preferences.json! Is it 0 KB?'}); + await loadPrefs(); + } catch (err: any) { + send({quit: true, errorMessage: err.toString()}); return; } + try { const [extConf] = await (Promise.all([ ExtConfigFile.readOrCreateFile(path.join(state.config.flashpointPath, EXT_CONFIG_FILENAME)) diff --git a/src/main/Main.ts b/src/main/Main.ts index 457dc006e..66d8ccae0 100644 --- a/src/main/Main.ts +++ b/src/main/Main.ts @@ -154,8 +154,11 @@ export function main(init: Init): void { p = p.then(() => new Promise((resolve, reject) => { // Fork backend, init.rest will contain possible flashpoint:// message state.backProc = fork(path.join(__dirname, '../back/index.js'), [init.rest], { detached: true }); - // Wait for process to initialize - state.backProc.once('message', (message: any) => { + const initHandler = (message: any) => { + // Stop listening after a port, quit or other message arrives, keep going for preferences checks + if (state.backProc && !message.preferencesRefresh) { + state.backProc.removeListener('message', initHandler); + } if (message.port) { state.backHost.port = message.port as string; resolve(); @@ -164,10 +167,23 @@ export function main(init: Init): void { dialog.showErrorBox('Flashpoint Startup Error', message.errorMessage); } app.quit(); + } else if (message.preferencesRefresh) { + dialog.showMessageBox({ + title: 'Preferences File Invalid', + message: 'Preferences file failed to load. Use defaults?', + buttons: ['Use Default Preferences', 'Cancel'], + cancelId: 1 + }).then((res) => { + if (state.backProc) { + state.backProc.send(res.response); + } + }); } else { reject(new Error('Failed to start server in back process. Perhaps because it could not find an available port.')); } - }); + }; + // Wait for process to initialize + state.backProc.on('message', initHandler); // On windows you have to wait for app to be ready before you call app.getLocale() (so it will be sent later) let localeCode = 'en'; if (process.platform === 'win32' && !app.isReady()) { diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index 3233853aa..cc9285b12 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -135,9 +135,54 @@ export class App extends React.Component { // remove "flashpoint:" and "" elements parts.splice(0, 2); switch (parts[0]) { + case 'open': { + if (parts.length >= 2) { + // Open game in sidebar + window.Shared.back.request(BackIn.GET_GAME, parts[1]) + .then(game => { + if (game) { + this.props.setMainState({ + currentGame: game, + selectedGameId: game.id, + selectedPlaylistId: undefined, + currentPlaylist: undefined, + currentPlaylistEntry: undefined + }); + } else { console.log(`Failed to get game. Game is undefined (GameID: "${parts[1]}").`); } + }); + } + break; + } case 'run': { if (parts.length >= 2) { - window.Shared.back.send(BackIn.LAUNCH_GAME, parts[1]); + window.Shared.back.request(BackIn.GET_GAME, parts[1]) + .then(async (game) => { + if (game) { + // Open game in sidebar + this.props.setMainState({ + currentGame: game, + selectedGameId: game.id, + selectedPlaylistId: undefined, + currentPlaylist: undefined, + currentPlaylistEntry: undefined + }); + // Launch game + await this.onGameLaunch(game.id); + // Update game data (install state) + if (game && game.activeDataId) { + window.Shared.back.request(BackIn.GET_GAME_DATA, game.activeDataId) + .then((gameData) => { + if (gameData) { + log.debug('TEST', JSON.stringify(gameData, undefined, 2)); + this.props.dispatchMain({ + type: MainActionType.FORCE_UPDATE_GAME_DATA, + gameData + }); + } + }); + } + } else { console.log(`Failed to get game. Game is undefined (GameID: "${parts[1]}").`); } + }); } break; } @@ -688,6 +733,7 @@ export class App extends React.Component { // Reset the state related to the selected game this.props.setMainState({ currentGame: undefined, + selectedGameId: undefined, currentPlaylistEntry: undefined, isEditingGame: false }); @@ -982,7 +1028,7 @@ export class App extends React.Component { onGameContextMenu: this.onGameContextMenuMemo(this.props.main.playlists, this.props.main.lang, this.props.main.selectedPlaylistId), onSaveGame: this.onSaveGame, onDeleteGame: this.onDeleteGame, - onLaunchGame: this.onLaunchGame, + onLaunchGame: this.onGameLaunch, onQuickSearch: this.onQuickSearch, onOpenExportMetaEdit: this.onOpenExportMetaEdit, libraries: this.props.main.libraries, @@ -1056,33 +1102,35 @@ export class App extends React.Component { This website requires JavaScript to be enabled.
    - - ((prev, next) => extremeTags.includes(next.primaryAlias.name), false) : false} - gameRunning={routerProps.gameRunning} - currentPlaylistEntry={this.props.main.currentPlaylistEntry} - currentLibrary={routerProps.gameLibrary} - onGameLaunch={this.onGameLaunch} - onDeleteSelectedGame={this.onDeleteSelectedGame} - onRemoveSelectedGameFromPlaylist={this.onRemoveSelectedGameFromPlaylist} - onDeselectPlaylist={this.onRightSidebarDeselectPlaylist} - onEditPlaylistNotes={this.onEditPlaylistNotes} - isEditing={this.props.main.isEditingGame} - isNewGame={false} /* Deprecated */ - onEditGame={this.onEditGame} - onUpdateActiveGameData={this.onUpdateActiveGameData} - onEditClick={this.onStartEditClick} - onDiscardClick={this.onDiscardEditClick} - onSaveGame={this.onSaveEditClick} - tagCategories={this.props.tagCategories} - suggestions={this.props.main.suggestions} - onOpenExportMetaEdit={this.onOpenExportMetaEdit} /> - + { this.props.main.currentGame && ( + + ((prev, next) => extremeTags.includes(next.primaryAlias.name), false) : false} + gameRunning={routerProps.gameRunning} + currentPlaylistEntry={this.props.main.currentPlaylistEntry} + currentLibrary={routerProps.gameLibrary} + onGameLaunch={this.onGameLaunch} + onDeleteSelectedGame={this.onDeleteSelectedGame} + onRemoveSelectedGameFromPlaylist={this.onRemoveSelectedGameFromPlaylist} + onDeselectPlaylist={this.onRightSidebarDeselectPlaylist} + onEditPlaylistNotes={this.onEditPlaylistNotes} + isEditing={this.props.main.isEditingGame} + isNewGame={false} /* Deprecated */ + onEditGame={this.onEditGame} + onUpdateActiveGameData={this.onUpdateActiveGameData} + onEditClick={this.onStartEditClick} + onDiscardClick={this.onDiscardEditClick} + onSaveGame={this.onSaveEditClick} + tagCategories={this.props.tagCategories} + suggestions={this.props.main.suggestions} + onOpenExportMetaEdit={this.onOpenExportMetaEdit} /> + + )} {/* Footer */} @@ -1256,10 +1304,6 @@ export class App extends React.Component { }); } - onLaunchGame(gameId: string): void { - window.Shared.back.send(BackIn.LAUNCH_GAME, gameId); - } - onQuickSearch = (search: string): void => { // @TODO } diff --git a/src/renderer/store/main/enums.ts b/src/renderer/store/main/enums.ts index 1d6e0b90a..37a28e274 100644 --- a/src/renderer/store/main/enums.ts +++ b/src/renderer/store/main/enums.ts @@ -58,7 +58,9 @@ export enum MainActionType { /** Remove all queued random games (the currently displayed games are NOT removed). */ CLEAR_RANDOM_GAMES = '@@main/CLEAR_RANDOM_GAMES', /** Increments the Logo Version to force a cache clear */ - INCREMENT_LOGO_VERSION = '@@main/INCREMENT_LOGO_VERSION' + INCREMENT_LOGO_VERSION = '@@main/INCREMENT_LOGO_VERSION', + /** Forces the game data to update from the backend, if game still matches */ + FORCE_UPDATE_GAME_DATA = '@@main/FORCE_UPDATE_GAME_DATA' } export enum RequestState { diff --git a/src/renderer/store/main/reducer.ts b/src/renderer/store/main/reducer.ts index b7996cab5..0d95a798c 100644 --- a/src/renderer/store/main/reducer.ts +++ b/src/renderer/store/main/reducer.ts @@ -1,3 +1,4 @@ +import { Game } from '@database/entity/Game'; import { rebuildQuery } from '@renderer/Util'; import { createLangContainer } from '@shared/lang'; import { MainActionType, RequestState } from './enums'; @@ -377,6 +378,25 @@ export function mainStateReducer(state: MainState = createInitialState(), action logoVersion: state.logoVersion + 1 }; } + + case MainActionType.FORCE_UPDATE_GAME_DATA: { + const { gameData } = action; + if (state.currentGame) { + if (gameData.gameId === state.currentGame.id) { + const newGame: Game = new Game(); + Object.assign(newGame, state.currentGame); + newGame.activeDataOnDisk = gameData.presentOnDisk; + return { + ...state, + currentGameData: gameData, + currentGame: newGame + }; + } + } + return { + ...state + }; + } } } diff --git a/src/renderer/store/main/types.ts b/src/renderer/store/main/types.ts index 20b574374..7d9ad1cf5 100644 --- a/src/renderer/store/main/types.ts +++ b/src/renderer/store/main/types.ts @@ -1,4 +1,5 @@ import { Game } from '@database/entity/Game'; +import { GameData } from '@database/entity/GameData'; import { Playlist } from '@database/entity/Playlist'; import { PlaylistGame } from '@database/entity/PlaylistGame'; import { CreditsData } from '@renderer/credits/types'; @@ -126,6 +127,7 @@ export type MainState = { selectedGameId?: string; selectedPlaylistId?: string; currentGame?: Game; + currentGameData?: GameData; currentPlaylist?: Playlist; currentPlaylistEntry?: PlaylistGame; isEditingGame: boolean; @@ -229,4 +231,7 @@ export type MainAction = { type: MainActionType.CLEAR_RANDOM_GAMES; } | { type: MainActionType.INCREMENT_LOGO_VERSION; +} | { + type: MainActionType.FORCE_UPDATE_GAME_DATA; + gameData: GameData; } diff --git a/src/shared/preferences/PreferencesFile.ts b/src/shared/preferences/PreferencesFile.ts index 31ff31300..2b5e6f2e9 100644 --- a/src/shared/preferences/PreferencesFile.ts +++ b/src/shared/preferences/PreferencesFile.ts @@ -1,7 +1,8 @@ import * as fs from 'fs'; +import * as path from 'path'; +import { deepCopy, readJsonFile, readJsonFileSync, stringifyJsonDataFile } from '../Util'; import { AppPreferencesData } from './interfaces'; import { defaultPreferencesData, overwritePreferenceData } from './util'; -import { deepCopy, readJsonFile, readJsonFileSync, stringifyJsonDataFile } from '../Util'; /** Static class with methods for saving, loading and parsing the Preferences file */ export namespace PreferencesFile { @@ -27,10 +28,9 @@ export namespace PreferencesFile { * If the file does not exist, create a new one with the default values and return that instead. * @param onError Called for each error that occurs while parsing. */ - export async function readOrCreateFile(filePath: string, onError?: (error: string) => void): Promise { - let data: AppPreferencesData | undefined; + export async function readOrCreateFile(filePath: string, flashpointPath: string, onError?: (error: string) => void): Promise { // Try to get the data from the file - data = await readFile(filePath, onError) + const data = await readFile(filePath, onError) .catch((e) => { if (e.code !== 'ENOENT') { if (onError) { onError(e); } @@ -38,11 +38,24 @@ export namespace PreferencesFile { } return undefined; }); - // If that failed, set data to default and save it to a new file + // If doesn't exist, set data to default and save it to a new file if (!data) { - data = deepCopy(defaultPreferencesData); - await saveFile(filePath, data) - .catch(() => console.error('Failed to save default preferences file!')); + const overridePath = path.join(flashpointPath, '.preferences.defaults.json'); + console.log('Checking for prefs override at ' + overridePath); + try { + await fs.promises.copyFile(overridePath, filePath); + console.log('Copied default preferences (override)'); + // File copied, try loading again + return readOrCreateFile(filePath, flashpointPath, onError); + } catch (err) { + console.log(err); + // Failed to copy overrides, use defaults + const defaultPrefs = deepCopy(defaultPreferencesData); + await saveFile(filePath, defaultPrefs) + .catch(() => console.error('Failed to save default preferences file!')); + console.log('Copied default preferences'); + return defaultPrefs; + } } // Return return data; From 7857de426f88e32143d574132c5c32ccf19bcff1 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sun, 3 Jul 2022 19:26:06 -0400 Subject: [PATCH 43/62] fix: use temp-rename writing for preference saving --- package-lock.json | 276 ++++++++++++++++++++++ src/shared/Util.ts | 8 +- src/shared/preferences/PreferencesFile.ts | 22 +- 3 files changed, 303 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 905c548ef..343f10e1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2072,6 +2072,198 @@ "@swc/core-win32-x64-msvc": "1.2.205" } }, + "node_modules/@swc/core-android-arm-eabi": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.205.tgz", + "integrity": "sha512-HfiuVA1JDHMSRQ8nE1DcemUgZ1PKaPwit4i7q3xin0NVbVHY1xkJyQFuLVh3VxTvGKKkF3hi8GJMVQgOXWL6kg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-android-arm64": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.205.tgz", + "integrity": "sha512-sRGZBV2dOnmh8gWWFo9HVOHdKa33zIsF8/8oYEGtq+2/s96UlAKltO2AA7HH9RaO/fT1tzBZStp+fEMUhDk/FA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.205.tgz", + "integrity": "sha512-JwVDfKS7vp7zzOQXWNwwcF41h4r3DWEpK6DQjz18WJyS1VVOcpVQGyuE7kSPjcnG01ZxBL9JPwwT353i/8IwDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.205.tgz", + "integrity": "sha512-malz2I+w6xFF1QyTmPGt0Y0NEMbUcrvfr5gUfZDGjxMhPPlS7k6fXucuZxVr9VVaM+JGq1SidVODmZ84jb1qHg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-freebsd-x64": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.205.tgz", + "integrity": "sha512-/nZrG1z0T7h97AsOb/wOtYlnh4WEuNppv3XKQIMPj32YNQdMBVgpybVTVRIs1GQGZMd1/7jAy5BVQcwQjUbrLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.205.tgz", + "integrity": "sha512-mTA3vETMdBmpecUyI9waZYsp7FABhew4e81psspmFpDyfty0SLISWZDnvPAn0pSnb2fWhzKwDC5kdXHKUmLJuA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.205.tgz", + "integrity": "sha512-qGzFGryeQE+O5SFK7Nn2ESqCEnv00rnzhf11WZF9V71EZ15amIhmbcwHqvFpoRSDw8hZnqoGqfPRfoJbouptnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.205.tgz", + "integrity": "sha512-uLJoX9L/4Xg3sLMjAbIhzbTe5gD/MBA8VETBeEkLtgb7a0ys1kvn9xQ6qLw6A71amEPlI+VABnoTRdUEaBSV9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.205.tgz", + "integrity": "sha512-gQsjcYlkWKP1kceQIsoHGrOrG7ygW3ojNsSnYoZ5DG5PipRA4eeUfO9YIfrmoa29LiVNjmRPfUJa8O1UHDG5ew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.205.tgz", + "integrity": "sha512-LR5ukqBltQc++2eX3qEj/H8KtOt0V3CmtgXNOiNCUxvPDT8mYz/8MJhYOrofonND0RKfXyyPW7dRxg62ceTLSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.205.tgz", + "integrity": "sha512-NjcLWm4mOy78LAEt7pqFl+SLcCyqnSlUP729XRd1uRvKwt1Cwch5SQRdoaFqwf1DaEQy4H4iuGPynkfarlb1kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.205.tgz", + "integrity": "sha512-+6byrRxIXgZ0zmLL6ZeX1HBBrAqvCy8MR5Yz0SO26jR8OPZXJCgZXL9BTsZO+YEG4f32ZOlZh3nnHCl6Dcb4GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, "node_modules/@swc/core-win32-x64-msvc": { "version": "1.2.205", "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.205.tgz", @@ -21714,6 +21906,90 @@ "@swc/core-win32-x64-msvc": "1.2.205" } }, + "@swc/core-android-arm-eabi": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.205.tgz", + "integrity": "sha512-HfiuVA1JDHMSRQ8nE1DcemUgZ1PKaPwit4i7q3xin0NVbVHY1xkJyQFuLVh3VxTvGKKkF3hi8GJMVQgOXWL6kg==", + "dev": true, + "optional": true + }, + "@swc/core-android-arm64": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.205.tgz", + "integrity": "sha512-sRGZBV2dOnmh8gWWFo9HVOHdKa33zIsF8/8oYEGtq+2/s96UlAKltO2AA7HH9RaO/fT1tzBZStp+fEMUhDk/FA==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-arm64": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.205.tgz", + "integrity": "sha512-JwVDfKS7vp7zzOQXWNwwcF41h4r3DWEpK6DQjz18WJyS1VVOcpVQGyuE7kSPjcnG01ZxBL9JPwwT353i/8IwDg==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.205.tgz", + "integrity": "sha512-malz2I+w6xFF1QyTmPGt0Y0NEMbUcrvfr5gUfZDGjxMhPPlS7k6fXucuZxVr9VVaM+JGq1SidVODmZ84jb1qHg==", + "dev": true, + "optional": true + }, + "@swc/core-freebsd-x64": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.205.tgz", + "integrity": "sha512-/nZrG1z0T7h97AsOb/wOtYlnh4WEuNppv3XKQIMPj32YNQdMBVgpybVTVRIs1GQGZMd1/7jAy5BVQcwQjUbrLg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.205.tgz", + "integrity": "sha512-mTA3vETMdBmpecUyI9waZYsp7FABhew4e81psspmFpDyfty0SLISWZDnvPAn0pSnb2fWhzKwDC5kdXHKUmLJuA==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.205.tgz", + "integrity": "sha512-qGzFGryeQE+O5SFK7Nn2ESqCEnv00rnzhf11WZF9V71EZ15amIhmbcwHqvFpoRSDw8hZnqoGqfPRfoJbouptnA==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.205.tgz", + "integrity": "sha512-uLJoX9L/4Xg3sLMjAbIhzbTe5gD/MBA8VETBeEkLtgb7a0ys1kvn9xQ6qLw6A71amEPlI+VABnoTRdUEaBSV9Q==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.205.tgz", + "integrity": "sha512-gQsjcYlkWKP1kceQIsoHGrOrG7ygW3ojNsSnYoZ5DG5PipRA4eeUfO9YIfrmoa29LiVNjmRPfUJa8O1UHDG5ew==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.205.tgz", + "integrity": "sha512-LR5ukqBltQc++2eX3qEj/H8KtOt0V3CmtgXNOiNCUxvPDT8mYz/8MJhYOrofonND0RKfXyyPW7dRxg62ceTLSQ==", + "dev": true, + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.205.tgz", + "integrity": "sha512-NjcLWm4mOy78LAEt7pqFl+SLcCyqnSlUP729XRd1uRvKwt1Cwch5SQRdoaFqwf1DaEQy4H4iuGPynkfarlb1kQ==", + "dev": true, + "optional": true + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.2.205", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.205.tgz", + "integrity": "sha512-+6byrRxIXgZ0zmLL6ZeX1HBBrAqvCy8MR5Yz0SO26jR8OPZXJCgZXL9BTsZO+YEG4f32ZOlZh3nnHCl6Dcb4GA==", + "dev": true, + "optional": true + }, "@swc/core-win32-x64-msvc": { "version": "1.2.205", "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.205.tgz", diff --git a/src/shared/Util.ts b/src/shared/Util.ts index 9cb403023..bdeaa753d 100644 --- a/src/shared/Util.ts +++ b/src/shared/Util.ts @@ -1,11 +1,13 @@ import * as axiosImport from 'axios'; import { Tag, TagFilterGroup } from 'flashpoint-launcher'; import * as fs from 'fs'; +import * as os from 'os'; import * as path from 'path'; import { DownloadDetails } from './back/types'; import { AppConfigData } from './config/interfaces'; -import { parseVariableString } from './utils/VariableString'; import { throttle } from './utils/throttle'; +import { uuid } from './utils/uuid'; +import { parseVariableString } from './utils/VariableString'; const axios = axiosImport.default; @@ -451,3 +453,7 @@ export function generateTagFilterGroup(tags?: string[]): TagFilterGroup { childFilters: [] }; } + +export async function getTempFilename(ext: string = 'tmp') { + return path.join(await fs.promises.realpath(os.tmpdir()), uuid() + '.' + ext); +} diff --git a/src/shared/preferences/PreferencesFile.ts b/src/shared/preferences/PreferencesFile.ts index 2b5e6f2e9..9786c0a44 100644 --- a/src/shared/preferences/PreferencesFile.ts +++ b/src/shared/preferences/PreferencesFile.ts @@ -1,6 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; -import { deepCopy, readJsonFile, readJsonFileSync, stringifyJsonDataFile } from '../Util'; +import { deepCopy, getTempFilename, readJsonFile, readJsonFileSync, stringifyJsonDataFile } from '../Util'; import { AppPreferencesData } from './interfaces'; import { defaultPreferencesData, overwritePreferenceData } from './util'; @@ -68,6 +68,24 @@ export namespace PreferencesFile { export async function saveFile(filePath: string, data: AppPreferencesData): Promise { const json = stringifyJsonDataFile(data); - await fs.promises.writeFile(filePath, json); + if (json.length === 0) { + log.error('PreferencesFile', 'Serialized preferences string is empty, skipping write.'); + return; + } + const temp = await getTempFilename(); + await fs.promises.writeFile(temp, json); + // Check: was it written correctly? + let stat = await fs.promises.stat(temp); + let count = 0; + while (stat.size !== json.length) { + if (count > 3) { + log.error('PreferencesFile', 'Repeated failure to write preferences.'); + return fs.promises.unlink(temp); + } + await fs.promises.writeFile(temp, json); + stat = await fs.promises.stat(temp); + count++; + } + await fs.promises.rename(temp, filePath); } } From f0083dbb4806cc200777144205d473ca499a924f Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sun, 3 Jul 2022 19:30:57 -0400 Subject: [PATCH 44/62] lint: remove inferrable type. --- src/shared/Util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/Util.ts b/src/shared/Util.ts index bdeaa753d..cd2e8ecef 100644 --- a/src/shared/Util.ts +++ b/src/shared/Util.ts @@ -454,6 +454,6 @@ export function generateTagFilterGroup(tags?: string[]): TagFilterGroup { }; } -export async function getTempFilename(ext: string = 'tmp') { +export async function getTempFilename(ext = 'tmp') { return path.join(await fs.promises.realpath(os.tmpdir()), uuid() + '.' + ext); } From 10b27da0a0adc16f379bb98c419016aeeed11a71 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sun, 3 Jul 2022 19:48:45 -0400 Subject: [PATCH 45/62] fix: remove flash browser mode This removes support for the :flash: ApplicationPath from GameLauncher. --- src/back/GameLauncher.ts | 94 ++++++++++++---------------------------- 1 file changed, 27 insertions(+), 67 deletions(-) diff --git a/src/back/GameLauncher.ts b/src/back/GameLauncher.ts index e98772f18..9ac7c8a35 100644 --- a/src/back/GameLauncher.ts +++ b/src/back/GameLauncher.ts @@ -213,73 +213,33 @@ export namespace GameLauncher { } } // Continue with launching normally - switch (appPath) { - // Special case flash browser run. - // @TODO Move to extension - case ':flash:': { - const env = getEnvironment(opts.fpPath, opts.proxy); - if ('ELECTRON_RUN_AS_NODE' in env) { - delete env['ELECTRON_RUN_AS_NODE']; // If this flag is present, it will disable electron features from the process - } - const gameData = opts.game.activeDataId ? await GameDataManager.findOne(opts.game.activeDataId) : undefined; - const gameLaunchInfo: GameLaunchInfo = { - game: opts.game, - activeData: gameData, - launchInfo: { - gamePath: process.execPath, - gameArgs: [path.join(__dirname, '../main/index.js'), 'browser_mode=true', `browser_url=${path.join(__dirname, '../window/flash_index.html')}?data=${encodeURI(opts.game.launchCommand)}`], - useWine: false, - env, - cwd: getCwd(opts.isDev, opts.exePath), - noshell: true - } - }; - onWillEvent.fire(gameLaunchInfo) - .then(() => { - const managedProc = opts.runGame(gameLaunchInfo); - log.info(logSource, `Launch Game "${opts.game.title}" (PID: ${managedProc.getPid()}) [\n`+ - ` applicationPath: "${appPath}",\n`+ - ` launchCommand: "${opts.game.launchCommand}" ]`); - }) - .catch((error) => { - log.info('Game Launcher', `Game Launch Aborted: ${error}`); - alert(`Game Launch Aborted: ${error}`); - }); - const managedProc = opts.runGame(gameLaunchInfo); - log.info(logSource, `Launch Game "${opts.game.title}" (PID: ${managedProc.getPid()}) [\n`+ - ` applicationPath: "${appPath}",\n`+ - ` launchCommand: "${opts.game.launchCommand}" ]`); - } break; - default: { - const gamePath: string = path.isAbsolute(appPath) ? fixSlashes(appPath) : fixSlashes(path.join(opts.fpPath, appPath)); - const gameArgs: string[] = [...appArgs, opts.game.launchCommand]; - const useWine: boolean = process.platform != 'win32' && gamePath.endsWith('.exe'); - const env = getEnvironment(opts.fpPath, opts.proxy); - const gameData = opts.game.activeDataId ? await GameDataManager.findOne(opts.game.activeDataId) : undefined; - const gameLaunchInfo: GameLaunchInfo = { - game: opts.game, - activeData: gameData, - launchInfo: { - gamePath, - gameArgs, - useWine, - env, - } - }; - onWillEvent.fire(gameLaunchInfo) - .then(() => { - const command: string = createCommand(gameLaunchInfo.launchInfo); - const managedProc = opts.runGame(gameLaunchInfo); - log.info(logSource,`Launch Game "${opts.game.title}" (PID: ${managedProc.getPid()}) [\n`+ - ` applicationPath: "${opts.game.applicationPath}",\n`+ - ` launchCommand: "${opts.game.launchCommand}",\n`+ - ` command: "${command}" ]`); - }) - .catch((error) => { - log.info('Game Launcher', `Game Launch Aborted: ${error}`); - }); - } break; - } + const gamePath: string = path.isAbsolute(appPath) ? fixSlashes(appPath) : fixSlashes(path.join(opts.fpPath, appPath)); + const gameArgs: string[] = [...appArgs, opts.game.launchCommand]; + const useWine: boolean = process.platform != 'win32' && gamePath.endsWith('.exe'); + const env = getEnvironment(opts.fpPath, opts.proxy); + const gameData = opts.game.activeDataId ? await GameDataManager.findOne(opts.game.activeDataId) : undefined; + const gameLaunchInfo: GameLaunchInfo = { + game: opts.game, + activeData: gameData, + launchInfo: { + gamePath, + gameArgs, + useWine, + env, + } + }; + onWillEvent.fire(gameLaunchInfo) + .then(() => { + const command: string = createCommand(gameLaunchInfo.launchInfo); + const managedProc = opts.runGame(gameLaunchInfo); + log.info(logSource,`Launch Game "${opts.game.title}" (PID: ${managedProc.getPid()}) [\n`+ + ` applicationPath: "${opts.game.applicationPath}",\n`+ + ` launchCommand: "${opts.game.launchCommand}",\n`+ + ` command: "${command}" ]`); + }) + .catch((error) => { + log.info('Game Launcher', `Game Launch Aborted: ${error}`); + }); } /** From 382602d32047f9c5c13eac8678a04a4a1b6327d4 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sun, 3 Jul 2022 19:57:07 -0400 Subject: [PATCH 46/62] feat: pass fp path to launched games through env Remove the need for scripts to figure out the fp path themselves. Previously, scripts had to locate themselves, resulting in a lot of duplicate code between scripts. Look at the linux scripts, for example. --- src/back/GameLauncher.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/back/GameLauncher.ts b/src/back/GameLauncher.ts index e98772f18..dd73ae80e 100644 --- a/src/back/GameLauncher.ts +++ b/src/back/GameLauncher.ts @@ -336,6 +336,7 @@ export namespace GameLauncher { // level entire system when using Windows. // When using WINE on mac, the proxy variable is needed as well. return { + 'FP_PATH': fpPath, // Add proxy env vars if it's running on linux ...(((process.platform === 'linux' || process.platform === 'darwin') && proxy !== '') ? { http_proxy: `http://${proxy}/`, HTTP_PROXY: `http://${proxy}/` } : null), // Copy this processes environment variables From 59f42e9bf3a208cfd7ee69f293b63d8c84c358f4 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sun, 3 Jul 2022 19:59:47 -0400 Subject: [PATCH 47/62] lint: remove trailing space --- src/renderer/components/Spinner.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/Spinner.tsx b/src/renderer/components/Spinner.tsx index 8f231740e..02e4eaee8 100644 --- a/src/renderer/components/Spinner.tsx +++ b/src/renderer/components/Spinner.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; /** - * Source: https://loading.io/css/ + * Source: https://loading.io/css/ * License: CC0 */ export function Spinner() { From cf73f5a1ff9df3022a16960f8c1c2d3cbdfdba0e Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Tue, 5 Jul 2022 17:05:40 +0100 Subject: [PATCH 48/62] fix: Prefs save properly when file missing fix: Protocol works on launch --- src/back/index.ts | 2 +- src/main/Main.ts | 4 ++ src/main/MainWindowPreload.ts | 1 + src/renderer/app.tsx | 63 ++++++++++++++----------- src/renderer/components/RandomGames.tsx | 1 - src/shared/IPC.ts | 1 + src/shared/interfaces.ts | 3 ++ 7 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/back/index.ts b/src/back/index.ts index 261bad74a..433c985a2 100644 --- a/src/back/index.ts +++ b/src/back/index.ts @@ -281,7 +281,7 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { // @TODO Figure out why async loading isn't always working? const prefsFilePath = path.join(state.config.flashpointPath, PREFERENCES_FILENAME); try { - state.preferences = await PreferencesFile.readOrCreateFile(prefsFilePath); + state.preferences = await PreferencesFile.readOrCreateFile(prefsFilePath, state.config.flashpointPath); } catch (e) { console.log('Failed to load preferences, prompting for defaults'); const res = await new Promise((resolve) => { diff --git a/src/main/Main.ts b/src/main/Main.ts index 66d8ccae0..827d7ca05 100644 --- a/src/main/Main.ts +++ b/src/main/Main.ts @@ -12,6 +12,7 @@ import { randomBytes } from 'crypto'; import { app, BrowserWindow, dialog, ipcMain, IpcMainEvent, session, shell, WebContents } from 'electron'; import * as fs from 'fs-extra'; import * as path from 'path'; +import { argv } from 'process'; import * as WebSocket from 'ws'; import { Init } from './types'; import * as Util from './Util'; @@ -377,12 +378,15 @@ export function main(init: Init): void { } function onInit(event: IpcMainEvent) { + // Find the arg that is our custom protocol url and store it + const url = argv.find((arg) => arg.startsWith('flashpoint://')); const data: InitRendererData = { isBackRemote: !!init.args['connect-remote'], installed: !!state._installed, version: state._version, host: state.backHost.href, secret: state._secret, + url }; event.returnValue = data; } diff --git a/src/main/MainWindowPreload.ts b/src/main/MainWindowPreload.ts index 5a1c97038..4c92c1fff 100644 --- a/src/main/MainWindowPreload.ts +++ b/src/main/MainWindowPreload.ts @@ -114,6 +114,7 @@ const onInit = (async () => { window.Shared.version = data.version; window.Shared.isBackRemote = data.isBackRemote; window.Shared.backUrl = new URL(data.host); + window.Shared.url = data.url; // Connect to the back const socket = await SocketClient.connect(WebSocket, data.host, data.secret); window.Shared.back.url = data.host; diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index cc9285b12..f1444f55a 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -108,36 +108,16 @@ export class App extends React.Component { } }; })(); - // Listen for the window to move or resize (and update the preferences when it does) - ipcRenderer.on(WindowIPC.WINDOW_MOVE, debounce((sender, x: number, y: number, isMaximized: boolean) => { - if (!isMaximized) { - updatePreferencesData({ mainWindow: { x: x|0, y: y|0 } }); - } - }, 100)); - ipcRenderer.on(WindowIPC.WINDOW_RESIZE, debounce((sender, width: number, height: number, isMaximized: boolean) => { - if (!isMaximized) { - // Cap minimum size - if (width < 300) { - width = 300; - } - if (height < 300) { - height = 300; - } - updatePreferencesData({ mainWindow: { width: width|0, height: height|0 } }); - } - }, 100)); - ipcRenderer.on(WindowIPC.WINDOW_MAXIMIZE, (sender, isMaximized: boolean) => { - updatePreferencesData({ mainWindow: { maximized: isMaximized } }); - }); - ipcRenderer.on(WindowIPC.PROTOCOL, (sender, url: string) => { + const handleProtocol = (url: string) => { const parts = url.split('/'); + log.debug('Launcher', 'Handling Protocol - ' + url); if (parts.length > 2) { - // remove "flashpoint:" and "" elements + // remove "flashpoint:" and "" elements parts.splice(0, 2); switch (parts[0]) { case 'open': { if (parts.length >= 2) { - // Open game in sidebar + // Open game in sidebar window.Shared.back.request(BackIn.GET_GAME, parts[1]) .then(game => { if (game) { @@ -148,7 +128,7 @@ export class App extends React.Component { currentPlaylist: undefined, currentPlaylistEntry: undefined }); - } else { console.log(`Failed to get game. Game is undefined (GameID: "${parts[1]}").`); } + } else { log.error('Launcher', `Failed to get game. Game is undefined (GameID: "${parts[1]}").`); } }); } break; @@ -158,7 +138,7 @@ export class App extends React.Component { window.Shared.back.request(BackIn.GET_GAME, parts[1]) .then(async (game) => { if (game) { - // Open game in sidebar + // Open game in sidebar this.props.setMainState({ currentGame: game, selectedGameId: game.id, @@ -181,7 +161,7 @@ export class App extends React.Component { } }); } - } else { console.log(`Failed to get game. Game is undefined (GameID: "${parts[1]}").`); } + } else { log.error('Launcher', `Failed to get game. Game is undefined (GameID: "${parts[1]}").`); } }); } break; @@ -191,6 +171,30 @@ export class App extends React.Component { break; } } + }; + // Listen for the window to move or resize (and update the preferences when it does) + ipcRenderer.on(WindowIPC.WINDOW_MOVE, debounce((sender, x: number, y: number, isMaximized: boolean) => { + if (!isMaximized) { + updatePreferencesData({ mainWindow: { x: x|0, y: y|0 } }); + } + }, 100)); + ipcRenderer.on(WindowIPC.WINDOW_RESIZE, debounce((sender, width: number, height: number, isMaximized: boolean) => { + if (!isMaximized) { + // Cap minimum size + if (width < 300) { + width = 300; + } + if (height < 300) { + height = 300; + } + updatePreferencesData({ mainWindow: { width: width|0, height: height|0 } }); + } + }, 100)); + ipcRenderer.on(WindowIPC.WINDOW_MAXIMIZE, (sender, isMaximized: boolean) => { + updatePreferencesData({ mainWindow: { maximized: isMaximized } }); + }); + ipcRenderer.on(WindowIPC.PROTOCOL, (sender, url: string) => { + handleProtocol(url); }); window.Shared.back.request(BackIn.INIT_LISTEN) @@ -447,6 +451,10 @@ export class App extends React.Component { }); } })); + + if (window.Shared.url) { + handleProtocol(window.Shared.url); + } }); // Load Credits @@ -720,6 +728,7 @@ export class App extends React.Component { } onGameLaunch = async (gameId: string): Promise => { + log.debug('Launcher', 'Launching Game - ' + gameId); await window.Shared.back.request(BackIn.LAUNCH_GAME, gameId); } diff --git a/src/renderer/components/RandomGames.tsx b/src/renderer/components/RandomGames.tsx index 23191180a..7e982e9b1 100644 --- a/src/renderer/components/RandomGames.tsx +++ b/src/renderer/components/RandomGames.tsx @@ -42,7 +42,6 @@ export function RandomGames(props: RandomGamesProps) { const gameItems = React.useMemo(() => { /* Games is a long queue, only render front */ - log.debug('TEST', `Rendering Random Games - ${props.selectedGameId || 'None Selected'}`); return ( props.games.slice(0, 6).map(game => ( Date: Wed, 6 Jul 2022 18:57:35 +0100 Subject: [PATCH 49/62] refactor: Upgrade TypeORM --- package-lock.json | 830 ++++++++++--------- package.json | 5 +- src/back/GameLauncher.ts | 21 +- src/back/game/GameDataManager.ts | 25 +- src/back/game/GameManager.ts | 135 +-- src/back/game/SourceDataManager.ts | 31 +- src/back/game/SourceManager.ts | 26 +- src/back/game/TagManager.ts | 89 +- src/back/index.ts | 68 +- src/back/responses.ts | 20 +- src/back/types.ts | 2 - src/renderer/app.tsx | 8 +- src/renderer/components/Footer.tsx | 3 +- src/renderer/components/pages/BrowsePage.tsx | 11 +- src/renderer/router.tsx | 2 +- src/renderer/store/main/reducer.ts | 4 +- src/shared/back/types.ts | 24 +- typings/flashpoint-launcher.d.ts | 4 +- 18 files changed, 723 insertions(+), 585 deletions(-) diff --git a/package-lock.json b/package-lock.json index 343f10e1d..7f8880ebb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@fortawesome/free-solid-svg-icons": "5.15.4", "@fortawesome/react-fontawesome": "0.1.18", "axios": "0.21.4", + "better-sqlite3": "7.5.3", "connected-react-router": "6.9.2", "electron-updater": "4.3.1", "electron-util": "0.14.2", @@ -35,9 +36,8 @@ "redux-devtools-extension": "2.13.8", "reflect-metadata": "0.1.10", "remark-gfm": "2.0.0", - "sqlite3": "5.0.8", "tail": "2.0.3", - "typeorm": "0.2.37", + "typeorm": "0.3.7", "typesafe-actions": "4.4.2", "uuid": "3.3.2", "uuid-validate": "0.0.3", @@ -69,7 +69,6 @@ "@types/ws": "6.0.3", "@typescript-eslint/eslint-plugin": "3.3.0", "@typescript-eslint/parser": "3.3.0", - "better-sqlite3": "^7.5.3", "chokidar": "^3.5.3", "coveralls": "3.1.0", "cross-env": "7.0.2", @@ -631,7 +630,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -791,7 +790,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "optional": true + "optional": true, + "peer": true }, "node_modules/@icons/material": { "version": "0.2.4", @@ -1719,7 +1719,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6.0.0" } @@ -1737,13 +1737,13 @@ "version": "1.4.13", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", - "dev": true + "devOptional": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1873,6 +1873,8 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz", "integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==", + "optional": true, + "peer": true, "dependencies": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", @@ -1928,6 +1930,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", "optional": true, + "peer": true, "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" @@ -1938,6 +1941,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", "optional": true, + "peer": true, "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" @@ -1951,6 +1955,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "optional": true, + "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -2304,25 +2309,25 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true + "devOptional": true }, "node_modules/@types/babel__core": { "version": "7.1.19", @@ -2723,11 +2728,6 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, - "node_modules/@types/zen-observable": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.3.tgz", - "integrity": "sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.3.0.tgz", @@ -3036,7 +3036,9 @@ "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true, + "peer": true }, "node_modules/acorn": { "version": "7.4.0", @@ -3063,7 +3065,7 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.4.0" } @@ -3072,6 +3074,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "devOptional": true, "dependencies": { "debug": "4" }, @@ -3084,6 +3087,7 @@ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", "optional": true, + "peer": true, "dependencies": { "debug": "^4.1.0", "depd": "^1.1.2", @@ -3098,6 +3102,7 @@ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "optional": true, + "peer": true, "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -3239,6 +3244,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -3442,7 +3448,9 @@ "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "optional": true, + "peer": true }, "node_modules/archy": { "version": "1.0.0", @@ -3454,6 +3462,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "optional": true, + "peer": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -3466,6 +3476,8 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3492,12 +3504,16 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "optional": true, + "peer": true }, "node_modules/are-we-there-yet/node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -3506,7 +3522,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/argparse": { "version": "1.0.10", @@ -4141,7 +4157,6 @@ "version": "7.5.3", "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.5.3.tgz", "integrity": "sha512-tNIrDsThpWT8j1mg+svI1pqCYROqNOWMbB2qXVg+TJqH9UR5XnbAHyRsLZoJagldGTTqJPj/sUPVOkW0GRpYqw==", - "devOptional": true, "hasInstallScript": true, "dependencies": { "bindings": "^1.5.0", @@ -4161,7 +4176,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "devOptional": true, "dependencies": { "file-uri-to-path": "1.0.0" } @@ -4170,7 +4184,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "devOptional": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -4181,7 +4194,6 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "devOptional": true, "funding": [ { "type": "github", @@ -4205,7 +4217,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "devOptional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -4219,7 +4230,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "devOptional": true, "funding": [ { "type": "github", @@ -4239,7 +4249,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "devOptional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -4854,6 +4863,7 @@ "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", "optional": true, + "peer": true, "dependencies": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", @@ -4883,6 +4893,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "optional": true, + "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -5157,6 +5168,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true, + "peer": true, "engines": { "node": ">=10" } @@ -5220,6 +5233,7 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -5557,6 +5571,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -5613,6 +5628,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "devOptional": true, "bin": { "color-support": "bin.js" } @@ -5743,7 +5759,9 @@ "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "optional": true, + "peer": true }, "node_modules/convert-source-map": { "version": "1.7.0", @@ -5821,7 +5839,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cross-env": { "version": "7.0.2", @@ -5959,6 +5977,18 @@ "node": ">=0.10" } }, + "node_modules/date-fns": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -6019,7 +6049,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "devOptional": true, "engines": { "node": ">=4.0.0" } @@ -6149,13 +6178,16 @@ "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true, + "peer": true }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "optional": true, + "peer": true, "engines": { "node": ">= 0.6" } @@ -6354,11 +6386,11 @@ } }, "node_modules/dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", + "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/dotenv-expand": { @@ -6869,6 +6901,7 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -6878,6 +6911,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -6931,7 +6965,8 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "optional": true + "optional": true, + "peer": true }, "node_modules/errno": { "version": "0.1.7", @@ -7073,6 +7108,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -7643,7 +7679,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "devOptional": true, "engines": { "node": ">=6" } @@ -8146,14 +8181,6 @@ "pend": "~1.2.0" } }, - "node_modules/figlet": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz", - "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -8184,8 +8211,7 @@ "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "devOptional": true + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "node_modules/filelist": { "version": "1.0.4", @@ -8452,8 +8478,7 @@ "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "devOptional": true + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "node_modules/fs-extra": { "version": "8.1.0", @@ -8488,6 +8513,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -8543,6 +8570,8 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "optional": true, + "peer": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", @@ -8562,6 +8591,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -8570,6 +8601,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -8578,6 +8611,8 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -8591,6 +8626,8 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -8656,8 +8693,7 @@ "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "devOptional": true + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "node_modules/glob": { "version": "7.2.3", @@ -9221,17 +9257,6 @@ "node": ">= 0.4.0" } }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -9256,7 +9281,9 @@ "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true, + "peer": true }, "node_modules/has-value": { "version": "1.0.0", @@ -9403,6 +9430,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "devOptional": true, "dependencies": { "agent-base": "6", "debug": "4" @@ -9425,6 +9453,7 @@ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", "optional": true, + "peer": true, "dependencies": { "ms": "^2.0.0" } @@ -9536,6 +9565,7 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -9544,7 +9574,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "optional": true + "optional": true, + "peer": true }, "node_modules/inflight": { "version": "1.0.6", @@ -9563,8 +9594,7 @@ "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "devOptional": true + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inline-style-parser": { "version": "0.1.1", @@ -9746,7 +9776,8 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", - "optional": true + "optional": true, + "peer": true }, "node_modules/is-absolute": { "version": "1.0.0", @@ -9957,6 +9988,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "devOptional": true, "dependencies": { "number-is-nan": "^1.0.0" }, @@ -10014,7 +10046,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", - "optional": true + "optional": true, + "peer": true }, "node_modules/is-negated-glob": { "version": "1.0.0", @@ -13180,6 +13213,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "devOptional": true, "dependencies": { "semver": "^6.0.0" }, @@ -13194,6 +13228,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "devOptional": true, "bin": { "semver": "bin/semver.js" } @@ -13202,13 +13237,14 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/make-fetch-happen": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", "optional": true, + "peer": true, "dependencies": { "agentkeepalive": "^4.1.3", "cacache": "^15.2.0", @@ -13236,6 +13272,7 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "optional": true, + "peer": true, "engines": { "node": ">= 6" } @@ -13245,6 +13282,7 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "optional": true, + "peer": true, "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -14197,6 +14235,8 @@ "version": "3.1.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "optional": true, + "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -14209,6 +14249,7 @@ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -14221,6 +14262,7 @@ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", "optional": true, + "peer": true, "dependencies": { "minipass": "^3.1.0", "minipass-sized": "^1.0.3", @@ -14238,6 +14280,7 @@ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -14250,6 +14293,7 @@ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -14262,6 +14306,7 @@ "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -14273,6 +14318,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -14320,8 +14367,7 @@ "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "devOptional": true + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "node_modules/ms": { "version": "2.0.0", @@ -14385,8 +14431,7 @@ "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "devOptional": true + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -14399,6 +14444,7 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "optional": true, + "peer": true, "engines": { "node": ">= 0.6" } @@ -14444,7 +14490,6 @@ "version": "3.22.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz", "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==", - "devOptional": true, "dependencies": { "semver": "^7.3.5" }, @@ -14456,6 +14501,8 @@ "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "optional": true, + "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -14474,17 +14521,23 @@ "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "optional": true, + "peer": true }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "optional": true, + "peer": true }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "optional": true, + "peer": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -14495,6 +14548,7 @@ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", "optional": true, + "peer": true, "dependencies": { "env-paths": "^2.2.0", "glob": "^7.1.4", @@ -14519,6 +14573,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -14528,6 +14583,7 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", "optional": true, + "peer": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -14541,6 +14597,7 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "optional": true, + "peer": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", @@ -14560,6 +14617,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -14569,6 +14627,7 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "optional": true, + "peer": true, "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", @@ -14584,6 +14643,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -14611,13 +14671,15 @@ "url": "https://feross.org/support" } ], - "optional": true + "optional": true, + "peer": true }, "node_modules/node-gyp/node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "optional": true, + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -14627,6 +14689,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "optional": true, + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14641,6 +14704,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14653,6 +14717,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "optional": true, + "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -14679,6 +14744,8 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "peer": true, "dependencies": { "abbrev": "1" }, @@ -14782,6 +14849,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "optional": true, + "peer": true, "dependencies": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", @@ -14793,6 +14862,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -15110,6 +15180,7 @@ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "optional": true, + "peer": true, "dependencies": { "aggregate-error": "^3.0.0" }, @@ -15171,14 +15242,6 @@ "node": ">=6" } }, - "node_modules/parent-require": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", - "integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/parse-entities": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-3.0.0.tgz", @@ -15484,7 +15547,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "devOptional": true, "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", @@ -15590,13 +15652,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "optional": true + "optional": true, + "peer": true }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "optional": true, + "peer": true, "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" @@ -15762,7 +15826,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "devOptional": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -16506,6 +16569,7 @@ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "optional": true, + "peer": true, "engines": { "node": ">= 4" } @@ -16524,6 +16588,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "devOptional": true, "dependencies": { "glob": "^7.1.3" }, @@ -16748,7 +16813,8 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "devOptional": true }, "node_modules/set-immediate-shim": { "version": "1.0.1", @@ -16882,13 +16948,13 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "devOptional": true }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "devOptional": true, "funding": [ { "type": "github", @@ -16908,7 +16974,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "devOptional": true, "funding": [ { "type": "github", @@ -16933,7 +16998,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "devOptional": true, "dependencies": { "mimic-response": "^3.1.0" }, @@ -16948,7 +17012,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "devOptional": true, "engines": { "node": ">=10" }, @@ -16999,6 +17062,7 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "optional": true, + "peer": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -17158,6 +17222,7 @@ "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", "optional": true, + "peer": true, "dependencies": { "ip": "^1.1.5", "smart-buffer": "^4.2.0" @@ -17172,6 +17237,7 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.0.tgz", "integrity": "sha512-wWqJhjb32Q6GsrUqzuFkukxb/zzide5quXYcMVpIjxalDBBYy2nqKCFQ/9+Ie4dvOYSQdOk3hUlZSdzZOd3zMQ==", "optional": true, + "peer": true, "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", @@ -17304,6 +17370,8 @@ "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.8.tgz", "integrity": "sha512-f2ACsbSyb2D1qFFcqIXPfFscLtPVOWJr5GmUzYxf4W+0qelu5MWrR+FAQE1d5IUArEltBrzSDxDORG8P/IkqyQ==", "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "@mapbox/node-pre-gyp": "^1.0.0", "node-addon-api": "^4.2.0", @@ -17324,7 +17392,9 @@ "node_modules/sqlite3/node_modules/node-addon-api": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "optional": true, + "peer": true }, "node_modules/sshpk": { "version": "1.16.1", @@ -17356,6 +17426,7 @@ "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "optional": true, + "peer": true, "dependencies": { "minipass": "^3.1.1" }, @@ -17493,6 +17564,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "devOptional": true, "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -17549,6 +17621,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "devOptional": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -17581,7 +17654,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -17777,6 +17849,8 @@ "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "optional": true, + "peer": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -17793,7 +17867,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "devOptional": true, "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -17804,14 +17877,12 @@ "node_modules/tar-fs/node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "devOptional": true + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "node_modules/tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "devOptional": true, "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -17827,7 +17898,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "devOptional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -17841,7 +17911,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "devOptional": true, "funding": [ { "type": "github", @@ -17861,7 +17930,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "devOptional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -17870,6 +17938,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -18518,7 +18588,7 @@ "version": "10.8.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.0.tgz", "integrity": "sha512-/fNd5Qh+zTt8Vt1KbYZjRHCE9sI5i7nqfD/dzBBRDeVXZXS6kToW6R7tTU6Nd4XavFs0mAVCg29Q//ML7WsZYA==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -18561,7 +18631,7 @@ "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true, + "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -18573,7 +18643,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -18735,7 +18805,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "devOptional": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -18846,52 +18915,62 @@ } }, "node_modules/typeorm": { - "version": "0.2.37", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.37.tgz", - "integrity": "sha512-7rkW0yCgFC24I5T0f3S/twmLSuccPh1SQmxET/oDWn2sSDVzbyWdnItSdKy27CdJGTlKHYtUVeOcMYw5LRsXVw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.7.tgz", + "integrity": "sha512-MsPJeP6Zuwfe64c++l80+VRqpGEGxf0CkztIEnehQ+CMmQPSHjOnFbFxwBuZ2jiLqZTjLk2ZqQdVF0RmvxNF3Q==", "dependencies": { "@sqltools/formatter": "^1.2.2", "app-root-path": "^3.0.0", "buffer": "^6.0.3", "chalk": "^4.1.0", "cli-highlight": "^2.1.11", - "debug": "^4.3.1", - "dotenv": "^8.2.0", - "glob": "^7.1.6", - "js-yaml": "^4.0.0", + "date-fns": "^2.28.0", + "debug": "^4.3.3", + "dotenv": "^16.0.0", + "glob": "^7.2.0", + "js-yaml": "^4.1.0", "mkdirp": "^1.0.4", "reflect-metadata": "^0.1.13", "sha.js": "^2.4.11", - "tslib": "^2.1.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2", "xml2js": "^0.4.23", - "yargonaut": "^1.1.4", - "yargs": "^17.0.1", - "zen-observable-ts": "^1.0.0" + "yargs": "^17.3.1" }, "bin": { - "typeorm": "cli.js" + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">= 12.9.0" }, "funding": { "url": "https://opencollective.com/typeorm" }, "peerDependencies": { - "@sap/hana-client": "*", - "better-sqlite3": "*", - "hdb-pool": "*", - "ioredis": "*", + "@google-cloud/spanner": "^5.18.0", + "@sap/hana-client": "^2.12.25", + "better-sqlite3": "^7.1.2", + "hdb-pool": "^0.1.6", + "ioredis": "^5.0.4", "mongodb": "^3.6.0", - "mssql": "*", - "mysql2": "*", - "oracledb": "*", - "pg": "*", - "pg-native": "*", - "pg-query-stream": "*", - "redis": "*", - "sql.js": "*", - "sqlite3": "*", - "typeorm-aurora-data-api-driver": "*" + "mssql": "^7.3.0", + "mysql2": "^2.2.5", + "oracledb": "^5.1.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0" }, "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, "@sap/hana-client": { "optional": true }, @@ -18934,6 +19013,9 @@ "sqlite3": { "optional": true }, + "ts-node": { + "optional": true + }, "typeorm-aurora-data-api-driver": { "optional": true } @@ -19040,6 +19122,14 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, + "node_modules/typeorm/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/typesafe-actions": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/typesafe-actions/-/typesafe-actions-4.4.2.tgz", @@ -19052,7 +19142,7 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -19166,6 +19256,7 @@ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "optional": true, + "peer": true, "dependencies": { "unique-slug": "^2.0.0" } @@ -19175,6 +19266,7 @@ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "optional": true, + "peer": true, "dependencies": { "imurmurhash": "^0.1.4" } @@ -19538,7 +19630,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "9.0.0", @@ -19980,6 +20072,8 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "optional": true, + "peer": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -20264,47 +20358,6 @@ "node": ">= 14" } }, - "node_modules/yargonaut": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz", - "integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==", - "dependencies": { - "chalk": "^1.1.1", - "figlet": "^1.1.1", - "parent-require": "^1.0.0" - } - }, - "node_modules/yargonaut/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargonaut/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargonaut/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/yargs": { "version": "17.5.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", @@ -20391,25 +20444,11 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } }, - "node_modules/zen-observable": { - "version": "0.8.15", - "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", - "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" - }, - "node_modules/zen-observable-ts": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz", - "integrity": "sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==", - "dependencies": { - "@types/zen-observable": "0.8.3", - "zen-observable": "0.8.15" - } - }, "node_modules/zwitch": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.2.tgz", @@ -20827,7 +20866,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" } @@ -20943,7 +20982,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "optional": true + "optional": true, + "peer": true }, "@icons/material": { "version": "0.2.4", @@ -21647,7 +21687,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", - "dev": true + "devOptional": true }, "@jridgewell/set-array": { "version": "1.1.1", @@ -21659,13 +21699,13 @@ "version": "1.4.13", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", - "dev": true + "devOptional": true }, "@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -21759,6 +21799,8 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz", "integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==", + "optional": true, + "peer": true, "requires": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", @@ -21802,6 +21844,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", "optional": true, + "peer": true, "requires": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" @@ -21812,6 +21855,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", "optional": true, + "peer": true, "requires": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" @@ -21821,7 +21865,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "optional": true + "optional": true, + "peer": true } } }, @@ -22015,25 +22060,25 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true + "devOptional": true }, "@tsconfig/node12": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true + "devOptional": true }, "@tsconfig/node14": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true + "devOptional": true }, "@tsconfig/node16": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true + "devOptional": true }, "@types/babel__core": { "version": "7.1.19", @@ -22434,11 +22479,6 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, - "@types/zen-observable": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.3.tgz", - "integrity": "sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==" - }, "@typescript-eslint/eslint-plugin": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.3.0.tgz", @@ -22681,7 +22721,9 @@ "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true, + "peer": true }, "acorn": { "version": "7.4.0", @@ -22700,12 +22742,13 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true + "devOptional": true }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "devOptional": true, "requires": { "debug": "4" } @@ -22715,6 +22758,7 @@ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", "optional": true, + "peer": true, "requires": { "debug": "^4.1.0", "depd": "^1.1.2", @@ -22726,6 +22770,7 @@ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "optional": true, + "peer": true, "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -22831,7 +22876,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "devOptional": true }, "ansi-styles": { "version": "3.2.1", @@ -23000,7 +23046,9 @@ "aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "optional": true, + "peer": true }, "archy": { "version": "1.0.0", @@ -23012,6 +23060,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "optional": true, + "peer": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -23021,6 +23071,8 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -23030,12 +23082,16 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "optional": true, + "peer": true }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, + "peer": true, "requires": { "safe-buffer": "~5.2.0" } @@ -23046,7 +23102,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "argparse": { "version": "1.0.10", @@ -23528,7 +23584,6 @@ "version": "7.5.3", "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.5.3.tgz", "integrity": "sha512-tNIrDsThpWT8j1mg+svI1pqCYROqNOWMbB2qXVg+TJqH9UR5XnbAHyRsLZoJagldGTTqJPj/sUPVOkW0GRpYqw==", - "devOptional": true, "requires": { "bindings": "^1.5.0", "prebuild-install": "^7.1.0" @@ -23544,7 +23599,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "devOptional": true, "requires": { "file-uri-to-path": "1.0.0" } @@ -23553,7 +23607,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "devOptional": true, "requires": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -23564,7 +23617,6 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "devOptional": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -23574,7 +23626,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "devOptional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -23584,14 +23635,12 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "devOptional": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "devOptional": true, "requires": { "safe-buffer": "~5.2.0" } @@ -24055,6 +24104,7 @@ "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", "optional": true, + "peer": true, "requires": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", @@ -24080,7 +24130,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "optional": true + "optional": true, + "peer": true } } }, @@ -24268,7 +24319,9 @@ "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true, + "peer": true }, "chrome-trace-event": { "version": "1.0.3", @@ -24321,7 +24374,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "optional": true + "optional": true, + "peer": true }, "cli-boxes": { "version": "2.2.1", @@ -24578,7 +24632,8 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "devOptional": true }, "collect-v8-coverage": { "version": "1.0.1", @@ -24625,7 +24680,8 @@ "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "devOptional": true }, "colorette": { "version": "2.0.16", @@ -24725,7 +24781,9 @@ "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "optional": true, + "peer": true }, "convert-source-map": { "version": "1.7.0", @@ -24788,7 +24846,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "cross-env": { "version": "7.0.2", @@ -24891,6 +24949,11 @@ "assert-plus": "^1.0.0" } }, + "date-fns": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -24935,8 +24998,7 @@ "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "devOptional": true + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, "deep-is": { "version": "0.1.3", @@ -25037,13 +25099,16 @@ "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true, + "peer": true }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "optional": true + "optional": true, + "peer": true }, "detect-file": { "version": "1.0.0", @@ -25199,9 +25264,9 @@ } }, "dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", + "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==" }, "dotenv-expand": { "version": "5.1.0", @@ -25605,6 +25670,7 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "optional": true, + "peer": true, "requires": { "iconv-lite": "^0.6.2" }, @@ -25614,6 +25680,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "optional": true, + "peer": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } @@ -25654,7 +25721,8 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "optional": true + "optional": true, + "peer": true }, "errno": { "version": "0.1.7", @@ -25774,7 +25842,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "eslint": { "version": "7.2.0", @@ -26196,8 +26265,7 @@ "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "devOptional": true + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" }, "expand-tilde": { "version": "2.0.2", @@ -26605,11 +26673,6 @@ "pend": "~1.2.0" } }, - "figlet": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz", - "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==" - }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -26631,8 +26694,7 @@ "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "devOptional": true + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "filelist": { "version": "1.0.4", @@ -26851,8 +26913,7 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "devOptional": true + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "fs-extra": { "version": "8.1.0", @@ -26883,6 +26944,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "peer": true, "requires": { "minipass": "^3.0.0" } @@ -26924,6 +26987,8 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "optional": true, + "peer": true, "requires": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", @@ -26939,17 +27004,23 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "optional": true, + "peer": true }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "optional": true, + "peer": true }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "peer": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -26960,6 +27031,8 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "optional": true, + "peer": true, "requires": { "ansi-regex": "^5.0.1" } @@ -27009,8 +27082,7 @@ "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "devOptional": true + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "glob": { "version": "7.2.3", @@ -27471,14 +27543,6 @@ "function-bind": "^1.1.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -27494,7 +27558,9 @@ "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true, + "peer": true }, "has-value": { "version": "1.0.0", @@ -27615,6 +27681,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "devOptional": true, "requires": { "agent-base": "6", "debug": "4" @@ -27631,6 +27698,7 @@ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", "optional": true, + "peer": true, "requires": { "ms": "^2.0.0" } @@ -27703,13 +27771,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "optional": true + "optional": true, + "peer": true }, "infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "optional": true + "optional": true, + "peer": true }, "inflight": { "version": "1.0.6", @@ -27728,8 +27798,7 @@ "ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "devOptional": true + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "inline-style-parser": { "version": "0.1.1", @@ -27868,7 +27937,8 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", - "optional": true + "optional": true, + "peer": true }, "is-absolute": { "version": "1.0.0", @@ -28025,6 +28095,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "devOptional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -28063,7 +28134,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", - "optional": true + "optional": true, + "peer": true }, "is-negated-glob": { "version": "1.0.0", @@ -30444,6 +30516,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "devOptional": true, "requires": { "semver": "^6.0.0" }, @@ -30451,7 +30524,8 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "devOptional": true } } }, @@ -30459,13 +30533,14 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "make-fetch-happen": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", "optional": true, + "peer": true, "requires": { "agentkeepalive": "^4.1.3", "cacache": "^15.2.0", @@ -30489,13 +30564,15 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "optional": true + "optional": true, + "peer": true }, "http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "optional": true, + "peer": true, "requires": { "@tootallnate/once": "1", "agent-base": "6", @@ -31110,6 +31187,8 @@ "version": "3.1.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "optional": true, + "peer": true, "requires": { "yallist": "^4.0.0" } @@ -31119,6 +31198,7 @@ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "optional": true, + "peer": true, "requires": { "minipass": "^3.0.0" } @@ -31128,6 +31208,7 @@ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", "optional": true, + "peer": true, "requires": { "encoding": "^0.1.12", "minipass": "^3.1.0", @@ -31140,6 +31221,7 @@ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "optional": true, + "peer": true, "requires": { "minipass": "^3.0.0" } @@ -31149,6 +31231,7 @@ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "optional": true, + "peer": true, "requires": { "minipass": "^3.0.0" } @@ -31158,6 +31241,7 @@ "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "optional": true, + "peer": true, "requires": { "minipass": "^3.0.0" } @@ -31166,6 +31250,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "peer": true, "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -31203,8 +31289,7 @@ "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "devOptional": true + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "ms": { "version": "2.0.0", @@ -31262,8 +31347,7 @@ "napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "devOptional": true + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" }, "natural-compare": { "version": "1.4.0", @@ -31275,7 +31359,8 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "optional": true + "optional": true, + "peer": true }, "neo-async": { "version": "2.6.2", @@ -31315,7 +31400,6 @@ "version": "3.22.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz", "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==", - "devOptional": true, "requires": { "semver": "^7.3.5" } @@ -31324,6 +31408,8 @@ "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "optional": true, + "peer": true, "requires": { "whatwg-url": "^5.0.0" }, @@ -31331,17 +31417,23 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "optional": true, + "peer": true }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "optional": true, + "peer": true }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "optional": true, + "peer": true, "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -31354,6 +31446,7 @@ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", "optional": true, + "peer": true, "requires": { "env-paths": "^2.2.0", "glob": "^7.1.4", @@ -31371,13 +31464,15 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true + "optional": true, + "peer": true }, "are-we-there-yet": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", "optional": true, + "peer": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -31388,6 +31483,7 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "optional": true, + "peer": true, "requires": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", @@ -31403,13 +31499,15 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "optional": true + "optional": true, + "peer": true }, "npmlog": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "optional": true, + "peer": true, "requires": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", @@ -31422,6 +31520,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "optional": true, + "peer": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -31432,13 +31531,15 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "optional": true + "optional": true, + "peer": true }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "optional": true, + "peer": true, "requires": { "safe-buffer": "~5.2.0" } @@ -31448,6 +31549,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "optional": true, + "peer": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -31459,6 +31561,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "optional": true, + "peer": true, "requires": { "ansi-regex": "^5.0.1" } @@ -31468,6 +31571,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "optional": true, + "peer": true, "requires": { "isexe": "^2.0.0" } @@ -31490,6 +31594,8 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "peer": true, "requires": { "abbrev": "1" } @@ -31570,6 +31676,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "optional": true, + "peer": true, "requires": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", @@ -31580,7 +31688,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "devOptional": true }, "oauth-sign": { "version": "0.9.0", @@ -31816,6 +31925,7 @@ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "optional": true, + "peer": true, "requires": { "aggregate-error": "^3.0.0" } @@ -31861,11 +31971,6 @@ "callsites": "^3.0.0" } }, - "parent-require": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", - "integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=" - }, "parse-entities": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-3.0.0.tgz", @@ -32109,7 +32214,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "devOptional": true, "requires": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", @@ -32187,13 +32291,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "optional": true + "optional": true, + "peer": true }, "promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "optional": true, + "peer": true, "requires": { "err-code": "^2.0.2", "retry": "^0.12.0" @@ -32322,7 +32428,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "devOptional": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -32904,7 +33009,8 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "optional": true + "optional": true, + "peer": true }, "reusify": { "version": "1.0.4", @@ -32916,6 +33022,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "devOptional": true, "requires": { "glob": "^7.1.3" } @@ -33087,7 +33194,8 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "devOptional": true }, "set-immediate-shim": { "version": "1.0.1", @@ -33195,19 +33303,18 @@ "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "devOptional": true }, "simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "devOptional": true + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" }, "simple-get": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "devOptional": true, "requires": { "decompress-response": "^6.0.0", "once": "^1.3.1", @@ -33218,7 +33325,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "devOptional": true, "requires": { "mimic-response": "^3.1.0" } @@ -33226,8 +33332,7 @@ "mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "devOptional": true + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" } } }, @@ -33266,7 +33371,8 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "optional": true + "optional": true, + "peer": true }, "snapdragon": { "version": "0.8.2", @@ -33395,6 +33501,7 @@ "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", "optional": true, + "peer": true, "requires": { "ip": "^1.1.5", "smart-buffer": "^4.2.0" @@ -33405,6 +33512,7 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.0.tgz", "integrity": "sha512-wWqJhjb32Q6GsrUqzuFkukxb/zzide5quXYcMVpIjxalDBBYy2nqKCFQ/9+Ie4dvOYSQdOk3hUlZSdzZOd3zMQ==", "optional": true, + "peer": true, "requires": { "agent-base": "^6.0.2", "debug": "^4.3.3", @@ -33515,6 +33623,8 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.8.tgz", "integrity": "sha512-f2ACsbSyb2D1qFFcqIXPfFscLtPVOWJr5GmUzYxf4W+0qelu5MWrR+FAQE1d5IUArEltBrzSDxDORG8P/IkqyQ==", + "optional": true, + "peer": true, "requires": { "@mapbox/node-pre-gyp": "^1.0.0", "node-addon-api": "^4.2.0", @@ -33525,7 +33635,9 @@ "node-addon-api": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "optional": true, + "peer": true } } }, @@ -33551,6 +33663,7 @@ "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "optional": true, + "peer": true, "requires": { "minipass": "^3.1.1" } @@ -33664,6 +33777,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "devOptional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -33708,6 +33822,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "devOptional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -33730,8 +33845,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "devOptional": true + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "style-to-object": { "version": "0.3.0", @@ -33880,6 +33994,8 @@ "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "optional": true, + "peer": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -33892,7 +34008,9 @@ "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "peer": true } } }, @@ -33900,7 +34018,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "devOptional": true, "requires": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -33911,8 +34028,7 @@ "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "devOptional": true + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" } } }, @@ -33920,7 +34036,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "devOptional": true, "requires": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -33933,7 +34048,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "devOptional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -33943,14 +34057,12 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "devOptional": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "devOptional": true, "requires": { "safe-buffer": "~5.2.0" } @@ -34437,7 +34549,7 @@ "version": "10.8.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.0.tgz", "integrity": "sha512-/fNd5Qh+zTt8Vt1KbYZjRHCE9sI5i7nqfD/dzBBRDeVXZXS6kToW6R7tTU6Nd4XavFs0mAVCg29Q//ML7WsZYA==", - "dev": true, + "devOptional": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -34458,13 +34570,13 @@ "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true + "devOptional": true }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "devOptional": true } } }, @@ -34593,7 +34705,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "devOptional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -34679,27 +34790,27 @@ } }, "typeorm": { - "version": "0.2.37", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.37.tgz", - "integrity": "sha512-7rkW0yCgFC24I5T0f3S/twmLSuccPh1SQmxET/oDWn2sSDVzbyWdnItSdKy27CdJGTlKHYtUVeOcMYw5LRsXVw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.7.tgz", + "integrity": "sha512-MsPJeP6Zuwfe64c++l80+VRqpGEGxf0CkztIEnehQ+CMmQPSHjOnFbFxwBuZ2jiLqZTjLk2ZqQdVF0RmvxNF3Q==", "requires": { "@sqltools/formatter": "^1.2.2", "app-root-path": "^3.0.0", "buffer": "^6.0.3", "chalk": "^4.1.0", "cli-highlight": "^2.1.11", - "debug": "^4.3.1", - "dotenv": "^8.2.0", - "glob": "^7.1.6", - "js-yaml": "^4.0.0", + "date-fns": "^2.28.0", + "debug": "^4.3.3", + "dotenv": "^16.0.0", + "glob": "^7.2.0", + "js-yaml": "^4.1.0", "mkdirp": "^1.0.4", "reflect-metadata": "^0.1.13", "sha.js": "^2.4.11", - "tslib": "^2.1.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2", "xml2js": "^0.4.23", - "yargonaut": "^1.1.4", - "yargs": "^17.0.1", - "zen-observable-ts": "^1.0.0" + "yargs": "^17.3.1" }, "dependencies": { "ansi-styles": { @@ -34772,6 +34883,11 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, @@ -34784,7 +34900,7 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", - "dev": true + "devOptional": true }, "unc-path-regex": { "version": "0.1.2", @@ -34862,6 +34978,7 @@ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "optional": true, + "peer": true, "requires": { "unique-slug": "^2.0.0" } @@ -34871,6 +34988,7 @@ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "optional": true, + "peer": true, "requires": { "imurmurhash": "^0.1.4" } @@ -35150,7 +35268,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "v8-to-istanbul": { "version": "9.0.0", @@ -35478,6 +35596,8 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "optional": true, + "peer": true, "requires": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -35686,40 +35806,6 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.0.tgz", "integrity": "sha512-OuAINfTsoJrY5H7CBWnKZhX6nZciXBydrMtTHr1dC4nP40X5jyTIVlogZHxSlVZM8zSgXRfgZGsaHF4+pV+JRw==" }, - "yargonaut": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz", - "integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==", - "requires": { - "chalk": "^1.1.1", - "figlet": "^1.1.1", - "parent-require": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, "yargs": { "version": "17.5.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", @@ -35787,21 +35873,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "zen-observable": { - "version": "0.8.15", - "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", - "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" - }, - "zen-observable-ts": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz", - "integrity": "sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==", - "requires": { - "@types/zen-observable": "0.8.3", - "zen-observable": "0.8.15" - } + "devOptional": true }, "zwitch": { "version": "2.0.2", diff --git a/package.json b/package.json index 75c03a51d..9f09d1ad3 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@fortawesome/free-solid-svg-icons": "5.15.4", "@fortawesome/react-fontawesome": "0.1.18", "axios": "0.21.4", + "better-sqlite3": "7.5.3", "connected-react-router": "6.9.2", "electron-updater": "4.3.1", "electron-util": "0.14.2", @@ -58,9 +59,8 @@ "redux-devtools-extension": "2.13.8", "reflect-metadata": "0.1.10", "remark-gfm": "2.0.0", - "sqlite3": "5.0.8", "tail": "2.0.3", - "typeorm": "0.2.37", + "typeorm": "0.3.7", "typesafe-actions": "4.4.2", "uuid": "3.3.2", "uuid-validate": "0.0.3", @@ -92,7 +92,6 @@ "@types/ws": "6.0.3", "@typescript-eslint/eslint-plugin": "3.3.0", "@typescript-eslint/parser": "3.3.0", - "better-sqlite3": "^7.5.3", "chokidar": "^3.5.3", "coveralls": "3.1.0", "cross-env": "7.0.2", diff --git a/src/back/GameLauncher.ts b/src/back/GameLauncher.ts index ee0cb4bfe..bb4be5158 100644 --- a/src/back/GameLauncher.ts +++ b/src/back/GameLauncher.ts @@ -7,6 +7,7 @@ import { fixSlashes, padStart, stringifyArray } from '@shared/Util'; import { Coerce } from '@shared/utils/Coerce'; import { ChildProcess, exec } from 'child_process'; import { EventEmitter } from 'events'; +import * as GameManager from '@back/game/GameManager'; import { AppPathOverride, GameData, ManagedChildProcess } from 'flashpoint-launcher'; import * as path from 'path'; import { ApiEmitter } from './extensions/ApiEmitter'; @@ -28,7 +29,7 @@ export type LaunchGameOpts = LaunchBaseOpts & { export type GameLaunchInfo = { game: Game; - activeData?: GameData; + activeData: GameData | null; launchInfo: LaunchInfo; } @@ -117,7 +118,6 @@ export namespace GameLauncher { /** * Launch a game - * @param game Game to launch */ export async function launchGame(opts: LaunchGameOpts, onWillEvent: ApiEmitter): Promise { // Abort if placeholder (placeholders are not "actual" games) @@ -146,12 +146,14 @@ export namespace GameLauncher { } } } + log.debug('TEST', 'Run required add apps'); // Launch game let appPath: string = getApplicationPath(opts.game.applicationPath, opts.execMappings, opts.native); let appArgs: string[] = []; const appPathOverride = opts.appPathOverrides.filter(a => a.enabled).find(a => a.path === appPath); if (appPathOverride) { appPath = appPathOverride.override; } const availableApps = opts.providers.filter(p => p.provides.includes(appPath) || p.provides.includes(opts.game.applicationPath)); + log.debug('TEST', 'Checked for available apps'); // If any available provided applications, check if any work. for (const app of availableApps) { try { @@ -178,7 +180,7 @@ export namespace GameLauncher { const browserLaunchArgs = [path.join(__dirname, '../main/index.js'), 'browser_mode=true']; if (res.proxy) { browserLaunchArgs.push(`proxy=${res.proxy}`); } browserLaunchArgs.push(`browser_url=${(res.url)}`); - const gameData = opts.game.activeDataId ? await GameDataManager.findOne(opts.game.activeDataId) : undefined; + const gameData = opts.game.activeDataId ? await GameDataManager.findOne(opts.game.activeDataId) : null; const gameLaunchInfo: GameLaunchInfo = { game: opts.game, activeData: gameData, @@ -212,12 +214,21 @@ export namespace GameLauncher { log.error('Launcher', `Error running provider for game.\n${error}`); } } + log.debug('TEST', 'Trying launch'); // Continue with launching normally const gamePath: string = path.isAbsolute(appPath) ? fixSlashes(appPath) : fixSlashes(path.join(opts.fpPath, appPath)); const gameArgs: string[] = [...appArgs, opts.game.launchCommand]; + log.debug('TEST', 'Gotten game path and args'); const useWine: boolean = process.platform != 'win32' && gamePath.endsWith('.exe'); const env = getEnvironment(opts.fpPath, opts.proxy); - const gameData = opts.game.activeDataId ? await GameDataManager.findOne(opts.game.activeDataId) : undefined; + log.debug('TEST', 'Gotten environment'); + try { + await GameManager.findGame(opts.game.id); + } catch (err: any) { + log.error('TEST', 'Error Game - ' + err.toString()); + } + const gameData = opts.game.activeDataId ? await GameDataManager.findOne(opts.game.activeDataId) : null; + log.debug('TEST', 'Found game data'); const gameLaunchInfo: GameLaunchInfo = { game: opts.game, activeData: gameData, @@ -228,9 +239,11 @@ export namespace GameLauncher { env, } }; + log.debug('TEST', 'Gathered info'); onWillEvent.fire(gameLaunchInfo) .then(() => { const command: string = createCommand(gameLaunchInfo.launchInfo); + log.debug('TEST', 'All info gathered, running'); const managedProc = opts.runGame(gameLaunchInfo); log.info(logSource,`Launch Game "${opts.game.title}" (PID: ${managedProc.getPid()}) [\n`+ ` applicationPath: "${opts.game.applicationPath}",\n`+ diff --git a/src/back/game/GameDataManager.ts b/src/back/game/GameDataManager.ts index 376f6599f..0ef276911 100644 --- a/src/back/game/GameDataManager.ts +++ b/src/back/game/GameDataManager.ts @@ -1,15 +1,16 @@ +import { ApiEmitter } from '@back/extensions/ApiEmitter'; import { GameData } from '@database/entity/GameData'; import { SourceData } from '@database/entity/SourceData'; -import { downloadFile } from '@shared/Util'; import { DownloadDetails } from '@shared/back/types'; +import { downloadFile } from '@shared/Util'; import * as crypto from 'crypto'; +import * as flashpoint from 'flashpoint-launcher'; import * as fs from 'fs'; import * as path from 'path'; -import { getManager, In } from 'typeorm'; +import { In } from 'typeorm'; +import { AppDataSource } from '..'; import * as GameManager from './GameManager'; import * as SourceManager from './SourceManager'; -import { ApiEmitter } from '@back/extensions/ApiEmitter'; -import * as flashpoint from 'flashpoint-launcher'; export const onDidInstallGameData = new ApiEmitter(); export const onWillUninstallGameData = new ApiEmitter(); @@ -63,13 +64,13 @@ export async function downloadGameData(gameDataId: number, dataPacksFolderPath: } } -export function findOne(id: number): Promise { - const gameDataRepository = getManager().getRepository(GameData); - return gameDataRepository.findOne(id); +export function findOne(id: number): Promise { + const gameDataRepository = AppDataSource.getRepository(GameData); + return gameDataRepository.findOneBy({ id }); } export function findGameData(gameId: string): Promise { - const gameDataRepository = getManager().getRepository(GameData); + const gameDataRepository = AppDataSource.getRepository(GameData); return gameDataRepository.find({ where: { gameId @@ -78,19 +79,19 @@ export function findGameData(gameId: string): Promise { } export function findSourceDataForHashes(hashes: string[]): Promise { - const sourceDataRepository = getManager().getRepository(SourceData); - return sourceDataRepository.find({ + const sourceDataRepository = AppDataSource.getRepository(SourceData); + return sourceDataRepository.findBy({ sha256: In(hashes) }); } export function save(gameData: GameData): Promise { - const gameDataRepository = getManager().getRepository(GameData); + const gameDataRepository = AppDataSource.getRepository(GameData); return gameDataRepository.save(gameData); } export async function remove(gameDataId: number): Promise { - const gameDataRepository = getManager().getRepository(GameData); + const gameDataRepository = AppDataSource.getRepository(GameData); await gameDataRepository.delete({ id: gameDataId }); } diff --git a/src/back/game/GameManager.ts b/src/back/game/GameManager.ts index a0cd71cce..7b954d8eb 100644 --- a/src/back/game/GameManager.ts +++ b/src/back/game/GameManager.ts @@ -16,8 +16,9 @@ import { Coerce } from '@shared/utils/Coerce'; import * as fs from 'fs'; import * as path from 'path'; import * as TagManager from './TagManager'; -import { Brackets, FindOneOptions, getManager, SelectQueryBuilder } from 'typeorm'; +import { Brackets, FindOneOptions, SelectQueryBuilder } from 'typeorm'; import * as GameDataManager from './GameDataManager'; +import { AppDataSource } from '..'; const exactFields = [ 'broken', 'library', 'activeDataOnDisk' ]; enum flatGameFields { @@ -35,27 +36,33 @@ export const onDidUpdatePlaylistGame = new ApiEmitter<{oldGame: PlaylistGame, ne export const onDidRemovePlaylistGame = new ApiEmitter(); export async function countGames(): Promise { - const gameRepository = getManager().getRepository(Game); + const gameRepository = AppDataSource.getRepository(Game); return gameRepository.count(); } /** Find the game with the specified ID. */ -export async function findGame(id?: string, filter?: FindOneOptions): Promise { - if (id || filter) { - const gameRepository = getManager().getRepository(Game); - const game = await gameRepository.findOne(id, filter); - if (game) { - game.tags.sort(tagSort); - } - return game; +export async function findGame(id?: string, filter?: FindOneOptions): Promise { + if (!filter) { + filter = {}; + } + if (id) { + filter.where = { + id + }; + } + const gameRepository = AppDataSource.getRepository(Game); + const game = await gameRepository.findOne(filter); + if (game) { + game.tags.sort(tagSort); } + return game; } export async function findGameRow(gameId: string, filterOpts?: FilterGameOpts, orderBy?: GameOrderBy, direction?: GameOrderReverse, index?: PageTuple): Promise { if (orderBy) { validateSqlName(orderBy); } // const startTime = Date.now(); - const gameRepository = getManager().getRepository(Game); + const gameRepository = AppDataSource.getRepository(Game); const subQ = gameRepository.createQueryBuilder('game') .select(`game.id, row_number() over (order by game.${orderBy}) row_num`); @@ -68,7 +75,7 @@ export async function findGameRow(gameId: string, filterOpts?: FilterGameOpts, o } if (orderBy) { subQ.orderBy(`game.${orderBy}`, direction); } - const query = getManager().createQueryBuilder() + const query = AppDataSource.createQueryBuilder() .setParameters(subQ.getParameters()) .select('row_num') .from('(' + subQ.getQuery() + ')', 'g') @@ -80,7 +87,7 @@ export async function findGameRow(gameId: string, filterOpts?: FilterGameOpts, o } export async function findRandomGames(count: number, broken: boolean, excludedLibraries: string[], flatFilters: string[]): Promise { - const gameRepository = getManager().getRepository(Game); + const gameRepository = AppDataSource.getRepository(Game); const query = gameRepository.createQueryBuilder('game'); query.select('game.id, game.title, game.platform, game.developer, game.publisher, game.tagsStr'); if (!broken) { query.andWhere('broken = false'); } @@ -89,7 +96,7 @@ export async function findRandomGames(count: number, broken: boolean, excludedLi } if (flatFilters.length > 0) { const tagIdQuery = TagManager.getFilterIDsQuery(flatFilters); - const excludedGameIdQuery = getManager().createQueryBuilder() + const excludedGameIdQuery = AppDataSource.createQueryBuilder() .select('game_tag.gameId') .from('game_tags_tag', 'game_tag') .where(`game_tag.tagId IN (${tagIdQuery.getQuery()})`); @@ -117,7 +124,7 @@ export async function findGamePageKeyset(filterOpts: FilterGameOpts, orderBy: Ga subQ.select(`sub.${orderBy}, sub.title, sub.id, case row_number() over(order by sub.${orderBy} ${direction}, sub.title ${direction}, sub.id) % ${VIEW_PAGE_SIZE} when 0 then 1 else 0 end page_boundary`); subQ.orderBy(`sub.${orderBy} ${direction}, sub.title`, direction); - let query = getManager().createQueryBuilder() + let query = AppDataSource.createQueryBuilder() .select(`g.${orderBy}, g.title, g.id, row_number() over(order by g.${orderBy} ${direction}, g.title ${direction}) + 1 page_number`) .from('(' + subQ.getQuery() + ')', 'g') .where('g.page_boundary = 1') @@ -139,7 +146,7 @@ export async function findGamePageKeyset(filterOpts: FilterGameOpts, orderBy: Ga let total = -1; // startTime = Date.now(); const subGameQuery = await getGameQuery('sub', filterOpts, orderBy, direction, 0, searchLimit ? searchLimit : undefined, undefined); - query = getManager().createQueryBuilder() + query = AppDataSource.createQueryBuilder() .select('COUNT(*)') .from('(' + subGameQuery.getQuery() + ')', 'g') .setParameters(subGameQuery.getParameters()) @@ -171,7 +178,7 @@ export type FindGamesOpts = { } export async function findAllGames(): Promise { - const gameRepository = getManager().getRepository(Game); + const gameRepository = AppDataSource.getRepository(Game); return gameRepository.find(); } @@ -211,20 +218,23 @@ export async function findGames(opts: FindGamesOpts, shallow: } /** Find an add apps with the specified ID. */ -export async function findAddApp(id?: string, filter?: FindOneOptions): Promise { - if (id || filter) { - if (!filter) { - filter = { - relations: ['parentGame'] - }; - } - const addAppRepository = getManager().getRepository(AdditionalApp); - return addAppRepository.findOne(id, filter); +export async function findAddApp(id?: string, filter?: FindOneOptions): Promise { + if (!filter) { + filter = { + relations: ['parentGame'] + }; + } + if (id) { + filter.where = { + id + }; } + const addAppRepository = AppDataSource.getRepository(AdditionalApp); + return addAppRepository.findOne(filter); } export async function findPlatformAppPaths(platform: string): Promise { - const gameRepository = getManager().getRepository(Game); + const gameRepository = AppDataSource.getRepository(Game); const values = await gameRepository.createQueryBuilder('game') .select('game.applicationPath') .distinct() @@ -238,7 +248,7 @@ export async function findPlatformAppPaths(platform: string): Promise export async function findUniqueValues(entity: any, column: string): Promise { validateSqlName(column); - const repository = getManager().getRepository(entity); + const repository = AppDataSource.getRepository(entity); const values = await repository.createQueryBuilder('entity') .select(`entity.${column}`) .distinct() @@ -249,7 +259,7 @@ export async function findUniqueValues(entity: any, column: string): Promise { validateSqlName(column); - const repository = getManager().getRepository(entity); + const repository = AppDataSource.getRepository(entity); const values = await repository.createQueryBuilder('entity') .select(`entity.${column}`) .distinct() @@ -258,7 +268,7 @@ export async function findUniqueValuesInOrder(entity: any, column: string): Prom } export async function findPlatforms(library: string): Promise { - const gameRepository = getManager().getRepository(Game); + const gameRepository = AppDataSource.getRepository(Game); const libraries = await gameRepository.createQueryBuilder('game') .where('game.library = :library', {library: library}) .select('game.platform') @@ -270,7 +280,7 @@ export async function findPlatforms(library: string): Promise { export async function updateGames(games: Game[]): Promise { const chunks = chunkArray(games, 2000); for (const chunk of chunks) { - await getManager().transaction(async transEntityManager => { + await AppDataSource.transaction(async transEntityManager => { for (const game of chunk) { await transEntityManager.save(Game, game); } @@ -279,16 +289,16 @@ export async function updateGames(games: Game[]): Promise { } export async function save(game: Game): Promise { - const gameRepository = getManager().getRepository(Game); + const gameRepository = AppDataSource.getRepository(Game); log.debug('Launcher', 'Saving game...'); const savedGame = await gameRepository.save(game); if (savedGame) { onDidUpdateGame.fire({oldGame: game, newGame: savedGame}); } return savedGame; } -export async function removeGameAndAddApps(gameId: string, dataPacksFolderPath: string): Promise { - const gameRepository = getManager().getRepository(Game); - const addAppRepository = getManager().getRepository(AdditionalApp); +export async function removeGameAndAddApps(gameId: string, dataPacksFolderPath: string): Promise { + const gameRepository = AppDataSource.getRepository(Game); + const addAppRepository = AppDataSource.getRepository(AdditionalApp); const game = await findGame(gameId); if (game) { // Delete GameData @@ -309,13 +319,18 @@ export async function removeGameAndAddApps(gameId: string, dataPacksFolderPath: return game; } -export async function findPlaylist(playlistId: string, join?: boolean): Promise { +export async function findPlaylist(playlistId: string, join?: boolean): Promise { const opts: FindOneOptions = join ? { relations: ['games'] } : {}; - const playlistRepository = getManager().getRepository(Playlist); - return playlistRepository.findOne(playlistId, opts); + const playlistRepository = AppDataSource.getRepository(Playlist); + return playlistRepository.findOne({ + ...opts, + where: { + id: playlistId + } + }); } -export async function findPlaylistByName(playlistName: string, join?: boolean): Promise { +export async function findPlaylistByName(playlistName: string, join?: boolean): Promise { const opts: FindOneOptions = join ? { relations: ['games'], where: { @@ -326,13 +341,13 @@ export async function findPlaylistByName(playlistName: string, join?: boolean): title: playlistName } }; - const playlistRepository = getManager().getRepository(Playlist); + const playlistRepository = AppDataSource.getRepository(Playlist); return playlistRepository.findOne(opts); } /** Find playlists given a filter. @TODO filter */ export async function findPlaylists(showExtreme: boolean): Promise { - const playlistRepository = getManager().getRepository(Playlist); + const playlistRepository = AppDataSource.getRepository(Playlist); if (showExtreme) { return await playlistRepository.find(); } else { @@ -342,8 +357,8 @@ export async function findPlaylists(showExtreme: boolean): Promise { /** Removes a playlist */ export async function removePlaylist(playlistId: string): Promise { - const playlistRepository = getManager().getRepository(Playlist); - const playlistGameRepository = getManager().getRepository(PlaylistGame); + const playlistRepository = AppDataSource.getRepository(Playlist); + const playlistGameRepository = AppDataSource.getRepository(PlaylistGame); const playlist = await findPlaylist(playlistId); if (playlist) { await playlistGameRepository.delete({ playlistId: playlist.id }); @@ -353,36 +368,32 @@ export async function removePlaylist(playlistId: string): Promise { - const playlistRepository = getManager().getRepository(Playlist); + const playlistRepository = AppDataSource.getRepository(Playlist); const savedPlaylist = await playlistRepository.save(playlist); if (savedPlaylist) { onDidUpdatePlaylist.fire({oldPlaylist: playlist, newPlaylist: savedPlaylist}); } return savedPlaylist; } /** Finds a Playlist Game */ -export async function findPlaylistGame(playlistId: string, gameId: string): Promise { - const playlistGameRepository = getManager().getRepository(PlaylistGame); - return await playlistGameRepository.findOne({ - where: { - gameId: gameId, - playlistId: playlistId - } - }); +export async function findPlaylistGame(playlistId: string, gameId: string): Promise { + const playlistGameRepository = AppDataSource.getRepository(PlaylistGame); + return await playlistGameRepository.findOneBy({ gameId, playlistId }); } /** Removes a Playlist Game */ -export async function removePlaylistGame(playlistId: string, gameId: string): Promise { - const playlistGameRepository = getManager().getRepository(PlaylistGame); +export async function removePlaylistGame(playlistId: string, gameId: string): Promise { + const playlistGameRepository = AppDataSource.getRepository(PlaylistGame); const playlistGame = await findPlaylistGame(playlistId, gameId); if (playlistGame) { onDidRemovePlaylistGame.fire(playlistGame); return playlistGameRepository.remove(playlistGame); } + return null; } /** Adds a new Playlist Game (to the end of the playlist). */ export async function addPlaylistGame(playlistId: string, gameId: string): Promise { - const repository = getManager().getRepository(PlaylistGame); + const repository = AppDataSource.getRepository(PlaylistGame); const duplicate = await repository.createQueryBuilder() .where('playlistId = :playlistId', { playlistId }) @@ -407,7 +418,7 @@ export async function addPlaylistGame(playlistId: string, gameId: string): Promi /** Updates a Playlist Game */ export async function updatePlaylistGame(playlistGame: PlaylistGame): Promise { - const playlistGameRepository = getManager().getRepository(PlaylistGame); + const playlistGameRepository = AppDataSource.getRepository(PlaylistGame); const savedPlaylistGame = await playlistGameRepository.save(playlistGame); if (savedPlaylistGame) { onDidUpdatePlaylistGame.fire({oldGame: playlistGame, newGame: savedPlaylistGame }); } return savedPlaylistGame; @@ -415,7 +426,7 @@ export async function updatePlaylistGame(playlistGame: PlaylistGame): Promise { - return getManager().transaction(async transEntityManager => { + return AppDataSource.transaction(async transEntityManager => { for (const game of playlistGames) { await transEntityManager.save(PlaylistGame, game); } @@ -423,7 +434,7 @@ export async function updatePlaylistGames(playlistGames: PlaylistGame[]): Promis } export async function findGamesWithTag(tag: Tag): Promise { - const gameIds = (await getManager().createQueryBuilder() + const gameIds = (await AppDataSource.createQueryBuilder() .select('game_tag.gameId as gameId') .distinct() .from('game_tags_tag', 'game_tag') @@ -434,7 +445,7 @@ export async function findGamesWithTag(tag: Tag): Promise { } async function chunkedFindByIds(gameIds: string[]): Promise { - const gameRepository = getManager().getRepository(Game); + const gameRepository = AppDataSource.getRepository(Game); const chunks = chunkArray(gameIds, 100); let gamesFound: Game[] = []; @@ -537,7 +548,7 @@ function doWhereField(alias: string, query: SelectQueryBuilder, field: str async function applyTagFilters(aliases: string[], alias: string, query: SelectQueryBuilder, whereCount: number, whitelist: boolean) { validateSqlName(alias); - const tagAliasRepository = getManager().getRepository(TagAlias); + const tagAliasRepository = AppDataSource.getRepository(TagAlias); const comparator = whitelist ? 'IN' : 'NOT IN'; const aliasKey = `${whitelist ? 'whitelist_' : 'blacklist_'}${whereCount}`; @@ -546,7 +557,7 @@ async function applyTagFilters(aliases: string[], alias: string, query: SelectQu .select('tag_alias.tagId') .distinct(); - const subQuery = getManager().createQueryBuilder() + const subQuery = AppDataSource.createQueryBuilder() .select('game_tag.gameId') .distinct() .from('game_tags_tag', 'game_tag') @@ -566,7 +577,7 @@ async function getGameQuery( let whereCount = 0; - const query = getManager().getRepository(Game).createQueryBuilder(alias); + const query = AppDataSource.getRepository(Game).createQueryBuilder(alias); // Use Page Index if available (ignored for Playlists) if ((!filterOpts || !filterOpts.playlistId) && index) { diff --git a/src/back/game/SourceDataManager.ts b/src/back/game/SourceDataManager.ts index d892ca28a..b942b7f8e 100644 --- a/src/back/game/SourceDataManager.ts +++ b/src/back/game/SourceDataManager.ts @@ -1,9 +1,9 @@ -import { chunkArray } from '@shared/utils/misc'; import { SourceData } from '@database/entity/SourceData'; -import { getManager } from 'typeorm'; +import { chunkArray } from '@shared/utils/misc'; +import { AppDataSource } from '..'; export function findBySource(sourceId: number): Promise { - const sourceDataRepository = getManager().getRepository(SourceData); + const sourceDataRepository = AppDataSource.getRepository(SourceData); return sourceDataRepository.find({ where: { sourceId @@ -11,28 +11,23 @@ export function findBySource(sourceId: number): Promise { }); } -export function findSourceHash(sourceId: number, hash: string): Promise { - const sourceDataRepository = getManager().getRepository(SourceData); - return sourceDataRepository.findOne({ - where: { - sourceId, - sha256: hash - } - }); +export function findSourceHash(sourceId: number, hash: string): Promise { + const sourceDataRepository = AppDataSource.getRepository(SourceData); + return sourceDataRepository.findOneBy({ sourceId, sha256: hash }); } -export function findOne(sourceDataId: number): Promise { - const sourceDataRepository = getManager().getRepository(SourceData); - return sourceDataRepository.findOne(sourceDataId); +export function findOne(sourceDataId: number): Promise { + const sourceDataRepository = AppDataSource.getRepository(SourceData); + return sourceDataRepository.findOneBy({ id: sourceDataId }); } export function save(sourceData: SourceData): Promise { - const sourceDataRepository = getManager().getRepository(SourceData); + const sourceDataRepository = AppDataSource.getRepository(SourceData); return sourceDataRepository.save(sourceData); } export function countBySource(sourceId: number): Promise { - const sourceDataRepository = getManager().getRepository(SourceData); + const sourceDataRepository = AppDataSource.getRepository(SourceData); return sourceDataRepository.count({ where: { sourceId @@ -41,14 +36,14 @@ export function countBySource(sourceId: number): Promise { } export async function clearSource(sourceId: number): Promise { - const sourceDataRepository = getManager().getRepository(SourceData); + const sourceDataRepository = AppDataSource.getRepository(SourceData); await sourceDataRepository.delete({ sourceId }); } export async function updateData(sourceData: SourceData[]): Promise { const chunks = chunkArray(sourceData, 2000); for (const chunk of chunks) { - await getManager().transaction(async transEntityManager => { + await AppDataSource.transaction(async transEntityManager => { for (const sd of chunk) { await transEntityManager.save(SourceData, sd); } diff --git a/src/back/game/SourceManager.ts b/src/back/game/SourceManager.ts index 45375a51d..a181e0eba 100644 --- a/src/back/game/SourceManager.ts +++ b/src/back/game/SourceManager.ts @@ -8,35 +8,31 @@ import axios from 'axios'; import * as fs from 'fs'; import * as path from 'path'; import * as readline from 'readline'; -import { getManager } from 'typeorm'; +import { AppDataSource } from '..'; export function find(): Promise { - const sourceRepository = getManager().getRepository(Source); + const sourceRepository = AppDataSource.getRepository(Source); return sourceRepository.find(); } -export function findOne(sourceId: number): Promise { - const sourceRepository = getManager().getRepository(Source); - return sourceRepository.findOne(sourceId); +export function findOne(sourceId: number): Promise { + const sourceRepository = AppDataSource.getRepository(Source); + return sourceRepository.findOneBy({ id: sourceId }); } -export function findBySourceFileUrl(sourceFileUrl: string): Promise { - const sourceRepository = getManager().getRepository(Source); - return sourceRepository.findOne({ - where: { - sourceFileUrl - } - }); +export function findBySourceFileUrl(sourceFileUrl: string): Promise { + const sourceRepository = AppDataSource.getRepository(Source); + return sourceRepository.findOneBy({ sourceFileUrl }); } export function save(source: Source): Promise { - const sourceRepository = getManager().getRepository(Source); + const sourceRepository = AppDataSource.getRepository(Source); return sourceRepository.save(source); } export async function remove(sourceId: number): Promise { - const sourceDataRepository = getManager().getRepository(SourceData); - const sourceRepository = getManager().getRepository(Source); + const sourceDataRepository = AppDataSource.getRepository(SourceData); + const sourceRepository = AppDataSource.getRepository(Source); await sourceDataRepository.delete({ sourceId }); await sourceRepository.delete({ id: sourceId }); } diff --git a/src/back/game/TagManager.ts b/src/back/game/TagManager.ts index e2785dd1f..7b3b57431 100644 --- a/src/back/game/TagManager.ts +++ b/src/back/game/TagManager.ts @@ -1,23 +1,24 @@ import { SocketServer } from '@back/SocketServer'; import { ShowMessageBoxFunc } from '@back/types'; -import { chunkArray } from '@shared/utils/misc'; import { Tag } from '@database/entity/Tag'; import { TagAlias } from '@database/entity/TagAlias'; import { TagCategory } from '@database/entity/TagCategory'; import { BackOut, MergeTagData, TagSuggestion } from '@shared/back/types'; -import { getManager, Not, SelectQueryBuilder } from 'typeorm'; +import { chunkArray } from '@shared/utils/misc'; +import { IsNull, Not, SelectQueryBuilder } from 'typeorm'; +import { AppDataSource } from '..'; import * as GameManager from './GameManager'; export async function findTagCategories(): Promise { - return getManager().getRepository(TagCategory).find(); + return AppDataSource.getRepository(TagCategory).find(); } export async function deleteTag(tagId: number, openDialog: ShowMessageBoxFunc, skipWarn?: boolean): Promise { - const tagRepository = getManager().getRepository(Tag); - const tagAliasRepository = getManager().getRepository(TagAlias); + const tagRepository = AppDataSource.getRepository(Tag); + const tagAliasRepository = AppDataSource.getRepository(TagAlias); if (!skipWarn) { - const gameCount = (await getManager().createQueryBuilder() + const gameCount = (await AppDataSource.createQueryBuilder() .select('COUNT(*)') .from('game_tags_tag', 'game_tag') .where('game_tag.tagId = :id', { id: tagId }) @@ -38,18 +39,18 @@ export async function deleteTag(tagId: number, openDialog: ShowMessageBoxFunc, s } export async function saveTag(tag: Tag): Promise { - const tagRepository = getManager().getRepository(Tag); + const tagRepository = AppDataSource.getRepository(Tag); return tagRepository.save(tag); } export async function saveTagAlias(tagAlias: TagAlias): Promise { - const tagAliasRepository = getManager().getRepository(TagAlias); + const tagAliasRepository = AppDataSource.getRepository(TagAlias); return tagAliasRepository.save(tagAlias); } export async function findTags(name?: string, flatFilters?: string[]): Promise { - const tagRepository = getManager().getRepository(Tag); - const tagAliasRepostiory = getManager().getRepository(TagAlias); + const tagRepository = AppDataSource.getRepository(Tag); + const tagAliasRepostiory = AppDataSource.getRepository(TagAlias); const filterQuery = flatFilters ? getFilterIDsQuery(flatFilters) : undefined; @@ -77,7 +78,7 @@ export async function findTags(name?: string, flatFilters?: string[]): Promise { +export async function mergeTags(mergeData: MergeTagData, openDialog: ShowMessageBoxFunc): Promise { const mergeSorc = await findTag(mergeData.toMerge); const mergeDest = await findTag(mergeData.mergeInto); if (mergeDest && mergeSorc) { @@ -89,7 +90,7 @@ export async function mergeTags(mergeData: MergeTagData, openDialog: ShowMessage buttons: [ 'Yes', 'No', 'Cancel' ] }); if (res !== 0) { - return undefined; + return null; } // Move names first if (mergeData.makeAlias) { @@ -128,16 +129,16 @@ export async function mergeTags(mergeData: MergeTagData, openDialog: ShowMessage } export async function cleanupTagAliases() { - const tagAliasRepostiory = getManager().getRepository(TagAlias); + const tagAliasRepostiory = AppDataSource.getRepository(TagAlias); const q = tagAliasRepostiory.createQueryBuilder('tag_alias') .delete() .where('tag_alias.tagId IS NULL'); return q.execute(); } -export async function findTag(name: string): Promise { - const tagRepository = getManager().getRepository(Tag); - const tagAliasRepostiory = getManager().getRepository(TagAlias); +export async function findTag(name: string): Promise { + const tagRepository = AppDataSource.getRepository(Tag); + const tagAliasRepostiory = AppDataSource.getRepository(TagAlias); const alias = await tagAliasRepostiory.findOne({ where: [ @@ -152,11 +153,13 @@ export async function findTag(name: string): Promise { ] }); } + + return null; } export async function findTagSuggestions(name: string, flatTagFilter: string[] = [], flatCatFilter: string[] = []): Promise { - const tagAliasRepostiory = getManager().getRepository(TagAlias); - const tagCategoryRepository = getManager().getRepository(TagCategory); + const tagAliasRepostiory = AppDataSource.getRepository(TagAlias); + const tagCategoryRepository = AppDataSource.getRepository(TagCategory); const tagCategories = (await Promise.all(flatCatFilter.map(async (cat) => tagCategoryRepository.findOne({ where: { name: cat }})))).filter(t => t !== undefined) as TagCategory[]; const flatCatIds = tagCategories.map(tg => tg.id); const filterQuery = flatTagFilter.length > 0 ? getFilterIDsQuery(flatTagFilter) : undefined; @@ -172,7 +175,7 @@ export async function findTagSuggestions(name: string, flatTagFilter: string[] = } subQuery = subQuery.limit(25); - const tagAliases = await getManager().createQueryBuilder() + const tagAliases = await AppDataSource.createQueryBuilder() .select('sugg.id, sugg.categoryId, sugg.name, COUNT(game_tag.gameId) as gameCount, primary_alias.name as primaryName') .from(`(${ subQuery.getQuery() })`, 'sugg') .leftJoin(TagAlias, 'primary_alias', 'sugg.primaryAliasId = primary_alias.id') @@ -197,9 +200,9 @@ export async function findTagSuggestions(name: string, flatTagFilter: string[] = } export async function findGameTags(gameId: string): Promise { - const tagRepository = getManager().getRepository(Tag); + const tagRepository = AppDataSource.getRepository(Tag); - const subQuery = getManager().createQueryBuilder() + const subQuery = AppDataSource.createQueryBuilder() .select('game_tag.tagId') .from('game_tags_tag', 'game_tag') .where('game_tag.gameId = :gameId', { gameId: gameId }); @@ -214,11 +217,11 @@ export async function findGameTags(gameId: string): Promise { return tags; } -export async function createTag(name: string, categoryName?: string, aliases?: string[]): Promise { - const tagRepository = getManager().getRepository(Tag); - const tagAliasRepostiory = getManager().getRepository(TagAlias); - const tagCategoryRepository = getManager().getRepository(TagCategory); - let category: TagCategory | undefined = undefined; +export async function createTag(name: string, categoryName?: string, aliases?: string[]): Promise { + const tagRepository = AppDataSource.getRepository(Tag); + const tagAliasRepostiory = AppDataSource.getRepository(TagAlias); + const tagCategoryRepository = AppDataSource.getRepository(TagCategory); + let category: TagCategory | null = null; // If category is defined, find/make it if (categoryName) { @@ -263,10 +266,12 @@ export async function createTag(name: string, categoryName?: string, aliases?: s savedTag.aliases = [await tagAliasRepostiory.save(tagAlias), ...tagAliases]; return savedTag; } + + return null; } -export async function createTagCategory(name: string, color: string): Promise { - const tagCategoryRepository = getManager().getRepository(TagCategory); +export async function createTagCategory(name: string, color: string): Promise { + const tagCategoryRepository = AppDataSource.getRepository(TagCategory); const category = tagCategoryRepository.create({ name: name, @@ -279,30 +284,30 @@ export async function createTagCategory(name: string, color: string): Promise { - const tagCategoryRepository = getManager().getRepository(TagCategory); + const tagCategoryRepository = AppDataSource.getRepository(TagCategory); const newCat = await tagCategoryRepository.save(tagCategory); return newCat; } -export async function getTagCategoryById(categoryId: number): Promise { - const tagCategoryRepository = getManager().getRepository(TagCategory); - return tagCategoryRepository.findOne(categoryId); +export async function getTagCategoryById(categoryId: number): Promise { + const tagCategoryRepository = AppDataSource.getRepository(TagCategory); + return tagCategoryRepository.findOneBy({ id: categoryId }); } -export async function getTagById(tagId: number): Promise { - const tagRepository = getManager().getRepository(Tag); - return tagRepository.findOne(tagId); +export async function getTagById(tagId: number): Promise { + const tagRepository = AppDataSource.getRepository(Tag); + return tagRepository.findOneBy({ id: tagId}); } export async function fixPrimaryAliases(): Promise { - const tagRepository = getManager().getRepository(Tag); + const tagRepository = AppDataSource.getRepository(Tag); let fixed = 0; - const tags = await tagRepository.find({ where: [{ primaryAliasId: null }] }); + const tags = await tagRepository.find({ where: [{ primaryAliasId: IsNull() }] }); const tagChunks = chunkArray(tags, 2000); for (const chunk of tagChunks) { - await getManager().transaction(async transEntityManager => { + await AppDataSource.transaction(async transEntityManager => { for (const tag of chunk) { if (tag.aliases.length > 0) { tag.primaryAliasId = tag.aliases[0].id; @@ -317,8 +322,8 @@ export async function fixPrimaryAliases(): Promise { } export async function deleteTagCategory(tagCategoryId: number, openDialog: ShowMessageBoxFunc): Promise { - const tagCategoryRepository = getManager().getRepository(TagCategory); - const tagRepository = getManager().getRepository(Tag); + const tagCategoryRepository = AppDataSource.getRepository(TagCategory); + const tagRepository = AppDataSource.getRepository(Tag); const attachedTags = await tagRepository.find({ where: [ @@ -365,13 +370,13 @@ export async function deleteTagCategory(tagCategoryId: number, openDialog: ShowM } export async function sendTagCategories(socketServer: SocketServer) { - const tagCategoryRepository = getManager().getRepository(TagCategory); + const tagCategoryRepository = AppDataSource.getRepository(TagCategory); const cats = await tagCategoryRepository.find(); socketServer.broadcast(BackOut.TAG_CATEGORIES_CHANGE, cats); } export function getFilterIDsQuery(flatFilters: string[]): SelectQueryBuilder { - const tagAliasRepostiory = getManager().getRepository(TagAlias); + const tagAliasRepostiory = AppDataSource.getRepository(TagAlias); return tagAliasRepostiory.createQueryBuilder('tag_alias') .select('tag_alias.tagId') .where('tag_alias.name IN (:...flatFilters)', { flatFilters }); diff --git a/src/back/index.ts b/src/back/index.ts index 433c985a2..6ec5683b4 100644 --- a/src/back/index.ts +++ b/src/back/index.ts @@ -36,9 +36,8 @@ import * as mime from 'mime'; import * as path from 'path'; import 'reflect-metadata'; // Required for the DB Models to function -import 'sqlite3'; import { Tail } from 'tail'; -import { ConnectionOptions, createConnection } from 'typeorm'; +import { DataSource, DataSourceOptions } from 'typeorm'; import { ConfigFile } from './ConfigFile'; import { CONFIG_FILENAME, EXT_CONFIG_FILENAME, PREFERENCES_FILENAME, SERVICES_SOURCE } from './constants'; import { loadExecMappingsFile } from './Execs'; @@ -61,6 +60,15 @@ import { LogFile } from './util/LogFile'; import { logFactory } from './util/logging'; import { createContainer, exit, runService } from './util/misc'; +const dataSourceOptions: DataSourceOptions = { + type: 'better-sqlite3', + database: ':memory:', + entities: [Game, AdditionalApp, Playlist, PlaylistGame, Tag, TagAlias, TagCategory, GameData, Source, SourceData], + migrations: [Initial1593172736527, AddExtremeToPlaylist1599706152407, GameData1611753257950, SourceDataUrlPath1612434225789, SourceFileURL1612435692266, + SourceFileCount1612436426353, GameTagsStr1613571078561, GameDataParams1619885915109] +}; +export const AppDataSource: DataSource = new DataSource(dataSourceOptions); + const DEFAULT_LOGO_PATH = 'window/images/Logos/404.png'; // Make sure the process.send function is available @@ -167,7 +175,6 @@ const state: BackState = { themes: new Map(), }, extensionsService: createErrorProxy('extensionsService'), - connection: undefined, prefsQueue: new EventQueue(), }; @@ -235,6 +242,8 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { if (state.isInit) { return; } state.isInit = true; + console.log('Back - Initializing...'); + const content: BackInitArgs = JSON.parse(message); state.isDev = content.isDev; state.verbose = content.verbose; @@ -272,6 +281,8 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { return; } + console.log('Back - Loaded Config'); + // If we're on mac and the flashpoint path is relative, resolve it relative to the configFolder path. state.config.flashpointPath = process.platform == 'darwin' && state.config.flashpointPath[0] != '/' ? path.resolve(state.configFolder, state.config.flashpointPath) @@ -327,6 +338,8 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { return; } + console.log('Back - Loaded Preferences'); + try { const [extConf] = await (Promise.all([ ExtConfigFile.readOrCreateFile(path.join(state.config.flashpointPath, EXT_CONFIG_FILENAME)) @@ -337,6 +350,8 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { // Non-fatal, don't quit. } + console.log('Back - Loaded Extension Config'); + // Create Game Data Directory and clean up temp files const fullDataPacksFolderPath = path.join(state.config.flashpointPath, state.preferences.dataPacksFolderPath); try { @@ -368,21 +383,30 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { } // Setup DB - if (!state.connection) { - const options: ConnectionOptions = { - type: 'sqlite', - database: path.join(state.config.flashpointPath, 'Data', 'flashpoint.sqlite'), - entities: [Game, AdditionalApp, Playlist, PlaylistGame, Tag, TagAlias, TagCategory, GameData, Source, SourceData], - migrations: [Initial1593172736527, AddExtremeToPlaylist1599706152407, GameData1611753257950, SourceDataUrlPath1612434225789, SourceFileURL1612435692266, - SourceFileCount1612436426353, GameTagsStr1613571078561, GameDataParams1619885915109] - }; - state.connection = await createConnection(options); + if (!AppDataSource.isInitialized) { + const databasePath = path.resolve(state.config.flashpointPath, 'Data', 'flashpoint.sqlite'); + console.log('Back - Using Database at ' + databasePath); + // Spin up another source just to run migrations + const migrationSource = new DataSource({ + ...dataSourceOptions, + type: 'better-sqlite3', + database: databasePath + }); + await migrationSource.initialize(); + await migrationSource.query('PRAGMA foreign_keys=off;'); + await migrationSource.runMigrations(); + await migrationSource.showMigrations(); + await migrationSource.destroy(); + // Initialize real database + AppDataSource.setOptions({ database: databasePath }); + await AppDataSource.initialize(); // TypeORM forces on but breaks Playlist Game links to unimported games - await state.connection.query('PRAGMA foreign_keys=off;'); - await state.connection.runMigrations(); + await AppDataSource.query('PRAGMA foreign_keys=off;'); log.info('Launcher', 'Database connection established'); } + console.log('Back - Initialized Database'); + // Init extensions const addExtLogFactory = (extId: string) => (entry: ILogEntry) => { state.extensionsService.logExtension(extId, entry); @@ -428,6 +452,7 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { }); }); + console.log('Back - Initialized Extensions'); // Init services try { @@ -472,6 +497,8 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { } } + console.log('Back - Initialized Services'); + // Init language state.languageWatcher.on('ready', () => { // Add event listeners @@ -536,6 +563,8 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { } }); + console.log('Back - Initialized Languages'); + // Init themes const dataThemeFolder = path.join(state.config.flashpointPath, state.preferences.themeFolderPath); try { @@ -566,6 +595,8 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { } } + console.log('Back - Initialized Themes'); + // Init Logo Sets const dataLogoSetsFolder = path.join(state.config.flashpointPath, state.preferences.logoSetsFolderPath); try { @@ -628,6 +659,8 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { } } + console.log('Back - Initialized Logo Sets'); + // Load Exec Mappings loadExecMappingsFile(path.join(state.config.flashpointPath, state.preferences.jsonFolderPath), content => log.info('Launcher', content)) .then(data => { @@ -641,6 +674,8 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { state.initEmitter.emit(BackInit.EXEC); }); + console.log('Back - Loaded Exec Mappings'); + const hostname = content.acceptRemote ? undefined : 'localhost'; // Find the first available port in the range @@ -685,11 +720,16 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { // Exit if it failed to open the server if (state.socketServer.port < 0) { + console.log('Back - Failed to open File Server, Exiting...'); setImmediate(exit); + return; } + console.log('Back - Opened File Server'); + // Respond send({port: state.socketServer.port}, () => { + console.log('Back - Ready'); state.apiEmitters.onDidInit.fire(); }); diff --git a/src/back/responses.ts b/src/back/responses.ts index 9de3d5235..3a514c3ac 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -214,7 +214,7 @@ export function registerRequestCallbacks(state: BackState): void { const addApp = await GameManager.findAddApp(id); if (addApp) { // If it has GameData, make sure it's present - let gameData: GameData | undefined; + let gameData: GameData | null; if (addApp.parentGame.activeDataId) { gameData = await GameDataManager.findOne(addApp.parentGame.activeDataId); if (gameData && !gameData.presentOnDisk) { @@ -275,9 +275,11 @@ export function registerRequestCallbacks(state: BackState): void { runService(state, 'server', 'Server', state.config.flashpointPath, {}, configServer); } } + log.debug('TEST', 'Server change done'); // If it has GameData, make sure it's present - let gameData: GameData | undefined; + let gameData: GameData | null; if (game.activeDataId) { + log.debug('TEST', 'Found active game data'); gameData = await GameDataManager.findOne(game.activeDataId); if (gameData && !gameData.presentOnDisk) { // Download GameData @@ -304,6 +306,7 @@ export function registerRequestCallbacks(state: BackState): void { } } } + log.debug('TEST', 'Running game'); // Launch game await GameLauncher.launchGame({ game, @@ -352,7 +355,7 @@ export function registerRequestCallbacks(state: BackState): void { return { game, - library: game && game.library, + library: game ? game.library : undefined, gamesTotal: await GameManager.countGames(), }; }); @@ -404,7 +407,8 @@ export function registerRequestCallbacks(state: BackState): void { } return { - library: result && result.library, + game: null, + library: result ? result.library : undefined, gamesTotal: await GameManager.countGames(), }; }); @@ -588,7 +592,7 @@ export function registerRequestCallbacks(state: BackState): void { }); state.socketServer.register(BackIn.UNINSTALL_GAME_DATA, async (event, id) => { - const gameData = await GameDataManager.findOne(id); + const gameData: GameData | null = await GameDataManager.findOne(id); if (gameData && gameData.path && gameData.presentOnDisk) { await GameDataManager.onWillUninstallGameData.fire(gameData); // Delete Game Data @@ -611,6 +615,7 @@ export function registerRequestCallbacks(state: BackState): void { return GameManager.save(game); } } + return null; }); state.socketServer.register(BackIn.ADD_SOURCE_BY_URL, async (event, url) => { @@ -644,7 +649,9 @@ export function registerRequestCallbacks(state: BackState): void { state.socketServer.register(BackIn.BROWSE_VIEW_KEYSET, async (event, library, query) => { query.filter = adjustGameFilter(query.filter); + const startTime = Date.now(); const result = await GameManager.findGamePageKeyset(query.filter, query.orderBy, query.orderReverse, query.searchLimit); + log.debug('Launcher', 'Search Time: ' + (Date.now() - startTime) + 'ms'); return { keyset: result.keyset, total: result.total, @@ -653,15 +660,12 @@ export function registerRequestCallbacks(state: BackState): void { state.socketServer.register(BackIn.BROWSE_VIEW_PAGE, async (event, data) => { data.query.filter = adjustGameFilter(data.query.filter); - const startTime = new Date(); const results = await GameManager.findGames({ ranges: data.ranges, filter: data.query.filter, orderBy: data.query.orderBy, direction: data.query.orderReverse, }, !!data.shallow); - const timeTaken = (new Date()).getTime() - startTime.getTime(); - log.debug('Launcher', `FindGames Time: ${timeTaken}ms`); return { ranges: results, diff --git a/src/back/types.ts b/src/back/types.ts index 5b6d91111..ad0cc9769 100644 --- a/src/back/types.ts +++ b/src/back/types.ts @@ -12,7 +12,6 @@ import { MessageBoxOptions, OpenDialogOptions, OpenExternalOptions, SaveDialogOp import { EventEmitter } from 'events'; import * as flashpoint from 'flashpoint-launcher'; import { IncomingMessage, Server, ServerResponse } from 'http'; -import { Connection } from 'typeorm'; import * as WebSocket from 'ws'; import { ApiEmitter } from './extensions/ApiEmitter'; import { ExtensionService } from './extensions/ExtensionService'; @@ -70,7 +69,6 @@ export type BackState = { readonly apiEmitters: ApiEmittersState, readonly registry: Registry; extensionsService: ExtensionService; - connection: Connection | undefined; prefsQueue: EventQueue; logsWindowProc?: ManagedChildProcess; } diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index f1444f55a..ab1a1d812 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -769,7 +769,7 @@ export class App extends React.Component { } const game = await this.onSaveGame(this.props.main.currentGame, this.props.main.currentPlaylistEntry); this.props.setMainState({ - currentGame: game, + currentGame: game == null ? undefined : game, isEditingGame: false }); // this.focusGameGridOrList(); @@ -851,7 +851,7 @@ export class App extends React.Component { updateCurrentGame = queueOne(async (gameId?: string, playlistId?: string): Promise => { // Find the selected game in the selected playlist if (gameId) { - let gamePlaylistEntry: PlaylistGame | undefined; + let gamePlaylistEntry: PlaylistGame | null; if (playlistId) { gamePlaylistEntry = await window.Shared.back.request(BackIn.GET_PLAYLIST_GAME, playlistId, gameId); @@ -863,7 +863,7 @@ export class App extends React.Component { if (game) { this.props.setMainState({ currentGame: game, - currentPlaylistEntry: gamePlaylistEntry + currentPlaylistEntry: gamePlaylistEntry == null ? undefined : gamePlaylistEntry }); } else { console.log(`Failed to get game. Game is undefined (GameID: "${gameId}").`); } }); @@ -1293,7 +1293,7 @@ export class App extends React.Component { this.props.setMainState(state as any); // (This is very annoying to make typesafe) } - onSaveGame = async (game: Game, playlistEntry?: PlaylistGame): Promise => { + onSaveGame = async (game: Game, playlistEntry?: PlaylistGame): Promise => { const data = await window.Shared.back.request(BackIn.SAVE_GAME, game); if (playlistEntry) { window.Shared.back.send(BackIn.SAVE_PLAYLIST_GAME, playlistEntry); diff --git a/src/renderer/components/Footer.tsx b/src/renderer/components/Footer.tsx index cebe323f3..a3595b96a 100644 --- a/src/renderer/components/Footer.tsx +++ b/src/renderer/components/Footer.tsx @@ -36,6 +36,7 @@ export class Footer extends React.Component { const libraryPath = getBrowseSubPath(this.props.location.pathname); const currentLabel = libraryPath && getLibraryItemTitle(libraryPath, this.props.main.lang.libraries); const view = this.props.main.views[libraryPath]; + const gamesTotal = (view && view.total != undefined) ? view.total : -1; return (
    @@ -47,7 +48,7 @@ export class Footer extends React.Component { { currentLabel && strings.searchResults ? ( <>

    |

    -

    {`${strings.searchResults}: ${(view && view.total) ? view.total : this.context.misc.searching}`}

    +

    {`${strings.searchResults}: ${gamesTotal > -1 ? gamesTotal : this.context.misc.searching}`}

    ) : undefined }
    diff --git a/src/renderer/components/pages/BrowsePage.tsx b/src/renderer/components/pages/BrowsePage.tsx index bcad90c62..8a2fa46cc 100644 --- a/src/renderer/components/pages/BrowsePage.tsx +++ b/src/renderer/components/pages/BrowsePage.tsx @@ -37,7 +37,7 @@ type OwnProps = { playlists: Playlist[]; suggestions: Partial; playlistIconCache: Record; - onSaveGame: (game: Game, playlistEntry?: PlaylistGame) => Promise; + onSaveGame: (game: Game, playlistEntry?: PlaylistGame) => Promise; onDeleteGame: (gameId: string) => void; onQuickSearch: (search: string) => void; onOpenExportMetaEdit: (gameId: string) => void; @@ -180,6 +180,7 @@ export class BrowsePage extends React.Component !t.enabled && t.extreme).reduce((prev, cur) => prev.concat(cur.tags), []); // Render return ( @@ -218,7 +219,7 @@ export class BrowsePage extends React.Component this.onExportPlaylist(strings, playlistId)} onContextMenu={this.onPlaylistContextMenuMemo(strings, this.state.isEditingPlaylist, this.props.selectedPlaylistId)} /> - { this.props.gamesTotal ? ( + { (gamesTotalNum > -1) ? (
    @@ -507,7 +508,7 @@ export class BrowsePage extends React.Component => { // Find the selected game in the selected playlist if (gameId) { - let gamePlaylistEntry: PlaylistGame | undefined; + let gamePlaylistEntry: PlaylistGame | null; if (playlistId) { gamePlaylistEntry = await window.Shared.back.request(BackIn.GET_PLAYLIST_GAME, playlistId, gameId); @@ -519,7 +520,7 @@ export class BrowsePage extends React.Component; platforms: Record; platformsFlat: string[]; - onSaveGame: (game: Game, playlistEntry?: PlaylistGame) => Promise; + onSaveGame: (game: Game, playlistEntry?: PlaylistGame) => Promise; onDeleteGame: (gameId: string) => void; onLaunchGame: (gameId: string) => void; onQuickSearch: (search: string) => void; diff --git a/src/renderer/store/main/reducer.ts b/src/renderer/store/main/reducer.ts index 0d95a798c..bc192fae9 100644 --- a/src/renderer/store/main/reducer.ts +++ b/src/renderer/store/main/reducer.ts @@ -130,7 +130,9 @@ export function mainStateReducer(state: MainState = createInitialState(), action // metaState: RequestState.RECEIVED, // Dirty games - isDirty: true, + isDirty: action.total === 0 ? false : true, + games: action.total === 0 ? [] : view.games, + lastCount: action.total === 0 ? 0 : view.lastCount, pageState: {}, // Update total (for the first response only) total: (view.total === undefined) diff --git a/src/shared/back/types.ts b/src/shared/back/types.ts index f8b9ad090..2e1a15043 100644 --- a/src/shared/back/types.ts +++ b/src/shared/back/types.ts @@ -183,12 +183,12 @@ export type BackInTemplate = SocketTemplate InitEventData; [BackIn.GET_SUGGESTIONS]: () => GetSuggestionsResponseData; [BackIn.GET_GAMES_GAME_DATA]: (gameId: string) => GameData[]; - [BackIn.GET_GAME_DATA]: (gameDataId: number) => GameData | undefined; + [BackIn.GET_GAME_DATA]: (gameDataId: number) => GameData | null; [BackIn.DELETE_GAME_DATA]: (gameDataId: number) => void; [BackIn.GET_SOURCES]: () => Source[]; [BackIn.GET_SOURCE_DATA]: (hashes: string[]) => SourceData[]; [BackIn.DOWNLOAD_GAME_DATA]: (gameDataId: number) => void; - [BackIn.UNINSTALL_GAME_DATA]: (id: number) => Game | undefined; + [BackIn.UNINSTALL_GAME_DATA]: (id: number) => Game | null; [BackIn.IMPORT_GAME_DATA]: (gameId: string, path: string) => GameData; [BackIn.SAVE_GAME_DATAS]: (gameData: GameData[]) => void; [BackIn.GET_GAMES_TOTAL]: () => number; @@ -196,7 +196,7 @@ export type BackInTemplate = SocketTemplate ExecMapping[]; [BackIn.SAVE_GAME]: (data: Game) => BrowseChangeData; [BackIn.SAVE_GAMES]: (data: Game[]) => void; - [BackIn.GET_GAME]: (id: string) => Game | undefined; + [BackIn.GET_GAME]: (id: string) => Game | null; [BackIn.GET_ALL_GAMES]: () => Game[]; [BackIn.RANDOM_GAMES]: (data: RandomGamesData) => ViewGame[]; [BackIn.LAUNCH_GAME]: (id: string) => void; @@ -216,10 +216,10 @@ export type BackInTemplate = SocketTemplate Playlist; [BackIn.DELETE_PLAYLIST]: (playlistId: string) => Playlist; [BackIn.DELETE_ALL_PLAYLISTS]: () => void; - [BackIn.GET_PLAYLIST_GAME]: (playlistId: string, gameId: string) => PlaylistGame | undefined; + [BackIn.GET_PLAYLIST_GAME]: (playlistId: string, gameId: string) => PlaylistGame | null; [BackIn.ADD_PLAYLIST_GAME]: (playlistId: string, gameId: string) => void; [BackIn.SAVE_PLAYLIST_GAME]: (data: PlaylistGame) => PlaylistGame; - [BackIn.DELETE_PLAYLIST_GAME]: (playlistId: string, gameId: string) => PlaylistGame | undefined; + [BackIn.DELETE_PLAYLIST_GAME]: (playlistId: string, gameId: string) => PlaylistGame | null; [BackIn.SAVE_LEGACY_PLATFORM]: (platform: Legacy_GamePlatform) => void; [BackIn.IMPORT_CURATION]: (data: ImportCurationData) => ImportCurationResponseData; [BackIn.LAUNCH_CURATION]: (data: LaunchCurationData) => void; @@ -229,9 +229,9 @@ export type BackInTemplate = SocketTemplate Tag; [BackIn.GET_TAG_SUGGESTIONS]: (data: string, tagFilters: TagFilterGroup[]) => TagSuggestion[]; - [BackIn.GET_TAG_BY_ID]: (data: number) => Tag | undefined; + [BackIn.GET_TAG_BY_ID]: (data: number) => Tag | null; [BackIn.GET_TAGS]: (data: string, tagFilters?: TagFilterGroup[]) => Tag[]; - [BackIn.GET_TAG]: (data: string) => Tag | undefined; + [BackIn.GET_TAG]: (data: string) => Tag | null; [BackIn.SAVE_TAG]: (data: Tag) => Tag; [BackIn.SAVE_TAG_ALIAS]: (data: TagAlias) => TagAlias; [BackIn.DELETE_TAG]: (data: number) => TagDeleteResponse; @@ -244,7 +244,7 @@ export type BackInTemplate = SocketTemplate TagCategory; - [BackIn.GET_TAG_CATEGORY_BY_ID]: (data: number) => TagCategory | undefined; + [BackIn.GET_TAG_CATEGORY_BY_ID]: (data: number) => TagCategory | null; [BackIn.DELETE_TAG_CATEGORY]: (data: number) => boolean; // Sources @@ -302,14 +302,14 @@ export type BackOutTemplate = SocketTemplate void; [BackOut.IMPORT_CURATION_RESPONSE]: () => void; [BackOut.GET_TAG_SUGGESTIONS]: (data: TagSuggestion[]) => void; - [BackOut.GET_TAG_BY_ID]: (SAVE_TAGdata: Tag | undefined) => Tag | undefined; + [BackOut.GET_TAG_BY_ID]: (SAVE_TAGdata: Tag | null) => Tag | undefined; [BackOut.GET_TAGS]: (data: Tag[]) => void; - [BackOut.GET_TAG]: (data: Tag | undefined) => void; + [BackOut.GET_TAG]: (data: Tag | null) => void; [BackOut.SAVE_TAG]: (data: Tag) => void; [BackOut.MERGE_TAGS]: (newTag: Tag) => void; [BackOut.EXPORT_TAGS]: (data: number) => void; [BackOut.IMPORT_TAGS]: (data: number) => void; - [BackOut.GET_TAG_CATEGORY_BY_ID]: (data: TagCategory | undefined) => void; + [BackOut.GET_TAG_CATEGORY_BY_ID]: (data: TagCategory | null) => void; [BackOut.SAVE_TAG_CATEGORY]: (data: TagCategory) => void; [BackOut.DELETE_TAG_CATEGORY]: (data: boolean) => void; [BackOut.TAG_CATEGORIES_CHANGE]: (cats: TagCategory[]) => void; @@ -483,7 +483,7 @@ export type ViewGame = { } export type BrowseChangeData = { - game?: Game; + game: Game | null; library?: string; gamesTotal: number; } diff --git a/typings/flashpoint-launcher.d.ts b/typings/flashpoint-launcher.d.ts index 44987ffc9..8df3183da 100644 --- a/typings/flashpoint-launcher.d.ts +++ b/typings/flashpoint-launcher.d.ts @@ -1,4 +1,4 @@ -// Type definitions for non-npm package flashpoint-launcher 10.1 +// Type definitions for non-npm package flashpoint-launcher 10.2 // Project: Flashpoint Launcher https://github.com/FlashpointProject/launcher // Definitions by: Colin Berry // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped @@ -1027,7 +1027,7 @@ declare module 'flashpoint-launcher' { /** Info type passed to onWillLaunch events */ type GameLaunchInfo = { game: Game; - activeData?: GameData; + activeData: GameData | null; launchInfo: LaunchInfo; }; From 2d77c68a397e3b4befa7c1e61f309c9c7b9bc58d Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Thu, 7 Jul 2022 20:49:35 +0100 Subject: [PATCH 50/62] fix: API typings --- typings/flashpoint-launcher.d.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/typings/flashpoint-launcher.d.ts b/typings/flashpoint-launcher.d.ts index 8df3183da..194445be1 100644 --- a/typings/flashpoint-launcher.d.ts +++ b/typings/flashpoint-launcher.d.ts @@ -104,13 +104,13 @@ declare module 'flashpoint-launcher' { * @param playlistId ID of the Playlist * @param join Whether to include Playlist Games in the result */ - function findPlaylist(playlistId: string, join?: boolean): Promise; + function findPlaylist(playlistId: string, join?: boolean): Promise; /** * Finds a playlist given its name * @param playlistName Name of the Playlist * @param join Whether to include Playlist Games in the result */ - function findPlaylistByName(playlistName: string, join?: boolean): Promise; + function findPlaylistByName(playlistName: string, join?: boolean): Promise; /** Find all Playlists in the database (Playlist Games not returned) */ function findPlaylists(showExtreme: boolean): Promise; /** @@ -131,13 +131,13 @@ declare module 'flashpoint-launcher' { * @param playlistId Playlist to search * @param gameId Game to find */ - function findPlaylistGame(playlistId: string, gameId: string): Promise; + function findPlaylistGame(playlistId: string, gameId: string): Promise; /** * Removes a Playlist Game entry from a Playlist * @param playlistId Playlist to search * @param gameId Game to remove */ - function removePlaylistGame(playlistId: string, gameId: string): Promise; + function removePlaylistGame(playlistId: string, gameId: string): Promise; /** * Update / Create a Playlist Game entry * @param playlistGame Playlist Game entry to save @@ -162,7 +162,7 @@ declare module 'flashpoint-launcher' { * Finds a Game given its ID * @param id ID of the Game */ - function findGame(id: string): Promise; + function findGame(id: string): Promise; /** * Finds a selection of Games given filter options * @param opts Filter options @@ -188,7 +188,7 @@ declare module 'flashpoint-launcher' { * Removes a Game and all its AddApps * @param gameId ID of Game to remove */ - function removeGameAndAddApps(gameId: string): Promise; + function removeGameAndAddApps(gameId: string): Promise; // Misc /** @@ -233,7 +233,7 @@ declare module 'flashpoint-launcher' { /** Collection of Game Data related API functions */ namespace gameData { - function findOne(id: number): Promise; + function findOne(id: number): Promise; function findGameData(gameId: string): Promise; function findSourceDataForHashes(hashes: string[]): Promise; function save(gameData: GameData): Promise; @@ -243,7 +243,7 @@ declare module 'flashpoint-launcher' { } namespace sources { - function findOne(sourceId: number): Promise; + function findOne(sourceId: number): Promise; } /** Collection of Tag related API functions */ @@ -253,12 +253,12 @@ declare module 'flashpoint-launcher' { * Finds a tag given it's ID * @param tagId ID of the Tag */ - function getTagById(tagId: number): Promise; + function getTagById(tagId: number): Promise; /** * Finds a tag given an alias name * @param name Name of the Tag (any matching alias) */ - function findTag(name: string): Promise; + function findTag(name: string): Promise; /** * Finds a list of tags that begins with a name (if given) * @param name Partial name that a tag starts with @@ -270,7 +270,7 @@ declare module 'flashpoint-launcher' { * @param categoryName Name of the category to use, Default if none given * @param aliases List of extra aliases to register */ - function createTag(name: string, categoryName?: string, aliases?: string[]): Promise; + function createTag(name: string, categoryName?: string, aliases?: string[]): Promise; /** * Updates a Tag * @param tag Tag data to save @@ -293,7 +293,7 @@ declare module 'flashpoint-launcher' { * Find a Tag Category by it's ID. (Useful when a Tag doesn't populate it) * @param categoryId ID of the Tag Category */ - function getTagCategoryById(categoryId: number): Promise; + function getTagCategoryById(categoryId: number): Promise; /** * Find all Tag Categories */ @@ -303,7 +303,7 @@ declare module 'flashpoint-launcher' { * @param name Name of the Tag Category * @param color Color to give the Tag Category */ - function createTagCategory(name: string, color: string): Promise; + function createTagCategory(name: string, color: string): Promise; /** * Update a Tag Category * @param tagCategory Tag Category data to save @@ -327,7 +327,7 @@ declare module 'flashpoint-launcher' { * Merges 2 tags into a single tag. * @param mergeData Required data to merge. */ - function mergeTags(mergeData: MergeTagData): Promise; + function mergeTags(mergeData: MergeTagData): Promise; } /** Collection of Status related API functions */ From cd8a28a5b3b736b7bc08dc6faeebcd296483904d Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sun, 10 Jul 2022 12:16:37 +0100 Subject: [PATCH 51/62] refactor: Revert to ttsc for backend building --- gulpfile.js | 4 ++-- package-lock.json | 40 ++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ src/back/MetaEdit.ts | 2 +- tsconfig.json | 8 +++++++- 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 40de5749a..52c2ad359 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -97,7 +97,7 @@ const publishInfo = [ /* ------ Watch ------ */ gulp.task('watch-back', (done) => { - execute('npx swc -w --no-swcrc --config-file swcrc.back.json -d build src', done); + execute('npx ttsc --project tsconfig.backend.json --pretty --watch', done); }); gulp.task('watch-renderer', (done) => { @@ -113,7 +113,7 @@ gulp.task('watch-static', () => { /* ------ Build ------ */ gulp.task('build-back', (done) => { - execute('npx swc --no-swcrc --config-file swcrc.back.json -d build src', done); + execute('npx ttsc --project tsconfig.backend.json --pretty', done); }); gulp.task('build-renderer', (done) => { diff --git a/package-lock.json b/package-lock.json index 7f8880ebb..289ce65c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,9 +84,11 @@ "ts-jest": "28.0.3", "ts-loader": "9.3.0", "ts-node": "10.8.0", + "ts-transform-paths": "^2.0.3", "tsconfig-paths-webpack-plugin": "3.2.0", "tslint": "5.18.0", "tswc": "^1.1.1", + "ttypescript": "^1.5.13", "typedoc": "0.22.15", "typescript": "4.6.2", "webpack": "5.72.1", @@ -18648,6 +18650,12 @@ "node": ">=0.3.1" } }, + "node_modules/ts-transform-paths": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/ts-transform-paths/-/ts-transform-paths-2.0.3.tgz", + "integrity": "sha512-eMUcIe/CCLUl90j9xqyj0RXC0t7h2E/ETbGBG3E8xFLyIMD9kTixUfZM78K1sHu0OBTFZTo6J7aUOafB8Mx+Dg==", + "dev": true + }, "node_modules/tsconfig-paths": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", @@ -18792,6 +18800,23 @@ "@swc/core": ">= 1.2.58" } }, + "node_modules/ttypescript": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.13.tgz", + "integrity": "sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==", + "dev": true, + "dependencies": { + "resolve": ">=1.9.0" + }, + "bin": { + "ttsc": "bin/tsc", + "ttsserver": "bin/tsserver" + }, + "peerDependencies": { + "ts-node": ">=8.0.2", + "typescript": ">=3.2.2" + } + }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -34580,6 +34605,12 @@ } } }, + "ts-transform-paths": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/ts-transform-paths/-/ts-transform-paths-2.0.3.tgz", + "integrity": "sha512-eMUcIe/CCLUl90j9xqyj0RXC0t7h2E/ETbGBG3E8xFLyIMD9kTixUfZM78K1sHu0OBTFZTo6J7aUOafB8Mx+Dg==", + "dev": true + }, "tsconfig-paths": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", @@ -34695,6 +34726,15 @@ "tsconfig-to-swcconfig": "^2.0.1" } }, + "ttypescript": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.13.tgz", + "integrity": "sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==", + "dev": true, + "requires": { + "resolve": ">=1.9.0" + } + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", diff --git a/package.json b/package.json index 9f09d1ad3..71212ea9f 100644 --- a/package.json +++ b/package.json @@ -107,9 +107,11 @@ "ts-jest": "28.0.3", "ts-loader": "9.3.0", "ts-node": "10.8.0", + "ts-transform-paths": "^2.0.3", "tsconfig-paths-webpack-plugin": "3.2.0", "tslint": "5.18.0", "tswc": "^1.1.1", + "ttypescript": "^1.5.13", "typedoc": "0.22.15", "typescript": "4.6.2", "webpack": "5.72.1", diff --git a/src/back/MetaEdit.ts b/src/back/MetaEdit.ts index f07e4440f..a79cc65d4 100644 --- a/src/back/MetaEdit.ts +++ b/src/back/MetaEdit.ts @@ -172,7 +172,7 @@ export async function importAllMetaEdits(fullMetaEditsFolderPath: string, openDi const game = await GameManager.findGame(id); if (game) { - games[id] = await GameManager.findGame(id); + games[id] = (await GameManager.findGame(id)) || undefined; } else { // Game not found const combined = combinedMetas[id]; if (!combined) { throw new Error(`Failed to check for collisions. "combined meta" is missing (id: "${id}") (bug)`); } diff --git a/tsconfig.json b/tsconfig.json index bb663ad60..b0afdeff8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,13 @@ "@renderer/*": [ "./src/renderer/*" ], "@database/*": [ "./src/database/*" ], "@tests/*": [ "./tests/*" ], - } + }, + "plugins": [ + { + "name": "empty", + "transform": "ts-transform-paths", + } + ] }, "exclude": [ "node_modules", From 6c0af99bcc64ecb3a8bd751445c95a92e3860c6f Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sun, 10 Jul 2022 12:26:36 +0100 Subject: [PATCH 52/62] chore: Bump Electron minor version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 71212ea9f..4afe52174 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "chokidar": "^3.5.3", "coveralls": "3.1.0", "cross-env": "7.0.2", - "electron": "17.1.2", + "electron": "17.2.0", "electron-builder": "22.14.13", "electron-devtools-installer": "3.1.1", "eslint": "7.2.0", From 3b79ea51ae4f53636de9c0f2e9c7f515cc0d23f3 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Tue, 12 Jul 2022 18:30:54 +0100 Subject: [PATCH 53/62] fix: Preferences should use NodeJS crypto --- src/back/util/misc.ts | 5 +++++ src/shared/Util.ts | 6 ------ src/shared/preferences/PreferencesFile.ts | 3 ++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/back/util/misc.ts b/src/back/util/misc.ts index 390a31921..d87df44b5 100644 --- a/src/back/util/misc.ts +++ b/src/back/util/misc.ts @@ -15,6 +15,7 @@ import { Legacy_IAdditionalApplicationInfo, Legacy_IGameInfo } from '@shared/leg import { deepCopy, recursiveReplace, stringifyArray } from '@shared/Util'; import * as child_process from 'child_process'; import * as fs from 'fs'; +import * as os from 'os'; import * as path from 'path'; import { promisify } from 'util'; import { uuid } from './uuid'; @@ -332,3 +333,7 @@ export function isBrowserOpts(val: any): val is BrowserApplicationOpts { export function getCwd(isDev: boolean, exePath: string) { return isDev ? process.cwd() : process.platform == 'darwin' ? path.resolve(path.dirname(exePath), '..') : path.dirname(exePath); } + +export async function getTempFilename(ext = 'tmp') { + return path.join(await fs.promises.realpath(os.tmpdir()), uuid() + '.' + ext); +} diff --git a/src/shared/Util.ts b/src/shared/Util.ts index cd2e8ecef..8ee92f4d9 100644 --- a/src/shared/Util.ts +++ b/src/shared/Util.ts @@ -1,12 +1,10 @@ import * as axiosImport from 'axios'; import { Tag, TagFilterGroup } from 'flashpoint-launcher'; import * as fs from 'fs'; -import * as os from 'os'; import * as path from 'path'; import { DownloadDetails } from './back/types'; import { AppConfigData } from './config/interfaces'; import { throttle } from './utils/throttle'; -import { uuid } from './utils/uuid'; import { parseVariableString } from './utils/VariableString'; const axios = axiosImport.default; @@ -453,7 +451,3 @@ export function generateTagFilterGroup(tags?: string[]): TagFilterGroup { childFilters: [] }; } - -export async function getTempFilename(ext = 'tmp') { - return path.join(await fs.promises.realpath(os.tmpdir()), uuid() + '.' + ext); -} diff --git a/src/shared/preferences/PreferencesFile.ts b/src/shared/preferences/PreferencesFile.ts index 9786c0a44..ab9968980 100644 --- a/src/shared/preferences/PreferencesFile.ts +++ b/src/shared/preferences/PreferencesFile.ts @@ -1,6 +1,7 @@ +import { getTempFilename } from '@back/util/misc'; import * as fs from 'fs'; import * as path from 'path'; -import { deepCopy, getTempFilename, readJsonFile, readJsonFileSync, stringifyJsonDataFile } from '../Util'; +import { deepCopy, readJsonFile, readJsonFileSync, stringifyJsonDataFile } from '../Util'; import { AppPreferencesData } from './interfaces'; import { defaultPreferencesData, overwritePreferenceData } from './util'; From 0813fb156acf532f11d00b03925de58d89a7c4d4 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Thu, 21 Jul 2022 14:14:20 +0100 Subject: [PATCH 54/62] feat: Basic diagnostics fetching on logs page --- lang/en.json | 3 +- src/back/responses.ts | 61 +++++++++++++++++++++- src/renderer/components/pages/LogsPage.tsx | 25 ++++++++- src/shared/back/types.ts | 2 + src/shared/lang.ts | 1 + 5 files changed, 88 insertions(+), 4 deletions(-) diff --git a/lang/en.json b/lang/en.json index c16a0bddc..8a7091e14 100644 --- a/lang/en.json +++ b/lang/en.json @@ -114,7 +114,8 @@ "copy404Urls": "Copy 404 URLs", "uploadLog": "Upload Log", "copiedToClipboard": "Copied to Clipboard", - "openLogsWindow": "Open Logs Window" + "openLogsWindow": "Open Logs Window", + "copyDiagnostics": "Copy Diagnostics" }, "app": { "home": "Home", diff --git a/src/back/responses.ts b/src/back/responses.ts index 3a514c3ac..adae40e91 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -18,10 +18,12 @@ import { MetaEditFile, MetaEditMeta } from '@shared/MetaEdit'; import { PreferencesFile } from '@shared/preferences/PreferencesFile'; import { defaultPreferencesData, overwritePreferenceData } from '@shared/preferences/util'; import { deepCopy } from '@shared/Util'; +import { sanitizeFilename } from '@shared/utils/sanitizeFilename'; import { formatString } from '@shared/utils/StringFormatter'; import * as axiosImport from 'axios'; import * as fs from 'fs'; import { ensureDir } from 'fs-extra'; +import * as os from 'os'; import * as path from 'path'; import * as url from 'url'; import * as util from 'util'; @@ -40,7 +42,6 @@ import { importAllMetaEdits } from './MetaEdit'; import { BackState, BareTag, TagsFile } from './types'; import { pathToBluezip } from './util/Bluezip'; import { copyError, createAddAppFromLegacy, createContainer, createGameFromLegacy, createPlaylistFromJson, exit, getCwd, pathExists, procToService, removeService, runService } from './util/misc'; -import { sanitizeFilename } from '@shared/utils/sanitizeFilename'; import { uuid } from './util/uuid'; const axios = axiosImport.default; @@ -1177,6 +1178,64 @@ export function registerRequestCallbacks(state: BackState): void { return getUrl; }); + state.socketServer.register(BackIn.FETCH_DIAGNOSTICS, async (event) => { + type Diagnostics = { + services: Array<{ + id: string; + name: string; + state: ProcessState; + }>; + generics: string[]; + } + // services + const diagnostics: Diagnostics = { + services: Array.from(state.services.values()).map(s => { + return { + id: s.id, + name: s.name, + state: s.getState() + }; + }), + generics: [] + }; + + // generics + + if (!fs.existsSync(path.join(state.config.flashpointPath, 'Legacy', 'router.php'))) { + diagnostics.generics.push('router.php is missing. Possible cause: Anti-Virus software has deleted it.'); + } + if (state.log.findIndex(e => e.content.includes('Server exited with code 3221225781')) !== -1) { + diagnostics.generics.push('Server exited with code 3221225781. Possible cause: .NET Framework or Visual C++ 2015 x86 Redists are not installed.'); + } + + // print + + let message = ''; + message = message + 'Operating System: ' + os.version() + '\n\n'; + for (const service of diagnostics.services) { + message = message + `${ProcessState[service.state]}:\t${service.name}\n`; + } + message = message + '\n'; + for (const service of diagnostics.services.filter(s => s.state === ProcessState.STOPPED)) { + const serviceLogs = state.log.filter(e => e.source === service.name); + if (serviceLogs.length > 0) { + message = message + `${service.name} recent logs:\n`; + for (const log of serviceLogs.slice(0, serviceLogs.length > 10 ? 10 : undefined)) { + message = message + `\t${log.content}\n`; + } + } + } + message = message + '\n'; + if (diagnostics.generics.length > 0) { + message = message + 'Warnings:\n'; + } + for (const generic of diagnostics.generics) { + message = message + `\t${generic}\n`; + } + + return '```' + message + '```'; + }); + state.socketServer.register(BackIn.QUIT, async (event) => { console.log('Exiting...'); // Unload all extensions before quitting diff --git a/src/renderer/components/pages/LogsPage.tsx b/src/renderer/components/pages/LogsPage.tsx index c934543e7..a4d0c6e42 100644 --- a/src/renderer/components/pages/LogsPage.tsx +++ b/src/renderer/components/pages/LogsPage.tsx @@ -44,6 +44,8 @@ export type LogsPageState = { uploaded: boolean; /** Whether an upload is in progress */ uploading: boolean; + /** Whether diagnostics has been fetched */ + fetchedDiagnostics: boolean; } export interface LogsPage { @@ -58,7 +60,8 @@ export class LogsPage extends React.Component { super(props); this.state = { uploaded: false, - uploading: false + uploading: false, + fetchedDiagnostics: false }; } @@ -146,7 +149,17 @@ export class LogsPage extends React.Component {
    )} - + {/* Copy Diagnostics Button */} +
    +
    + +
    +
    {/* Upload Logs Button */}
    @@ -235,6 +248,14 @@ export class LogsPage extends React.Component { window.Shared.back.send(BackIn.OPEN_LOGS_WINDOW); } + onCopyDiagnosticsClick = async (): Promise => { + window.Shared.back.request(BackIn.FETCH_DIAGNOSTICS) + .then((diagnostics) => { + this.setState({ fetchedDiagnostics: true }); + clipboard.writeText(diagnostics); + }); + } + onUploadClick = async (): Promise => { this.setState({ uploading: true }); const strings = this.context; diff --git a/src/shared/back/types.ts b/src/shared/back/types.ts index 2e1a15043..4e2e192cd 100644 --- a/src/shared/back/types.ts +++ b/src/shared/back/types.ts @@ -127,6 +127,7 @@ export enum BackIn { OPEN_LOGS_WINDOW, UPLOAD_LOG, SET_EXT_CONFIG_VALUE, + FETCH_DIAGNOSTICS, } export enum BackOut { @@ -276,6 +277,7 @@ export type BackInTemplate = SocketTemplate void; [BackIn.UPLOAD_LOG]: () => string | undefined; [BackIn.SET_EXT_CONFIG_VALUE]: (key: string, value: any) => void; + [BackIn.FETCH_DIAGNOSTICS]: () => string; }> export type BackOutTemplate = SocketTemplate Date: Thu, 21 Jul 2022 20:45:33 +0100 Subject: [PATCH 55/62] feat: Improve diagnostics output --- src/back/responses.ts | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/back/responses.ts b/src/back/responses.ts index adae40e91..d72ee9f60 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -17,10 +17,11 @@ import { LogLevel } from '@shared/Log/interface'; import { MetaEditFile, MetaEditMeta } from '@shared/MetaEdit'; import { PreferencesFile } from '@shared/preferences/PreferencesFile'; import { defaultPreferencesData, overwritePreferenceData } from '@shared/preferences/util'; -import { deepCopy } from '@shared/Util'; +import { deepCopy, padEnd } from '@shared/Util'; import { sanitizeFilename } from '@shared/utils/sanitizeFilename'; import { formatString } from '@shared/utils/StringFormatter'; import * as axiosImport from 'axios'; +import { execSync } from 'child_process'; import * as fs from 'fs'; import { ensureDir } from 'fs-extra'; import * as os from 'os'; @@ -1211,12 +1212,31 @@ export function registerRequestCallbacks(state: BackState): void { // print let message = ''; - message = message + 'Operating System: ' + os.version() + '\n\n'; + const maxLen = 'Operating System: '.length; + message = message + 'Operating System: ' + os.version() + '\n'; + message = message + padEnd('Architecture:', maxLen) + os.arch() + '\n'; + try { + const output = execSync('powershell.exe -Command "Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntivirusProduct | Format-Wide -Property displayName"').toString().trim(); + if (output.toLowerCase().includes('avast') || output.toLowerCase().includes('avg')) { + diagnostics.generics.push('AVG or Avast Anti-Virus is installed. This may cause problems with Flashpoint.'); + } + message = message + padEnd('Anti-Virus:', maxLen) + output + '\n'; + } catch (err) { + message = message + 'Anti-Virus:\tUnknown\n'; + } + message = message + '\n'; for (const service of diagnostics.services) { message = message + `${ProcessState[service.state]}:\t${service.name}\n`; } + if (diagnostics.generics.length > 0) { + message = message + '\n'; + message = message + 'Warnings:\n'; + } + for (const generic of diagnostics.generics) { + message = message + `\t${generic}\n`; + } message = message + '\n'; - for (const service of diagnostics.services.filter(s => s.state === ProcessState.STOPPED)) { + for (const service of diagnostics.services) { const serviceLogs = state.log.filter(e => e.source === service.name); if (serviceLogs.length > 0) { message = message + `${service.name} recent logs:\n`; @@ -1225,13 +1245,6 @@ export function registerRequestCallbacks(state: BackState): void { } } } - message = message + '\n'; - if (diagnostics.generics.length > 0) { - message = message + 'Warnings:\n'; - } - for (const generic of diagnostics.generics) { - message = message + `\t${generic}\n`; - } return '```' + message + '```'; }); From 22934ac948572f3684d604e374e280bd215d1a4b Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Thu, 21 Jul 2022 20:50:02 +0100 Subject: [PATCH 56/62] refactor: Move Copy Diagnostics Button --- src/renderer/components/pages/LogsPage.tsx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/renderer/components/pages/LogsPage.tsx b/src/renderer/components/pages/LogsPage.tsx index a4d0c6e42..512b21bdf 100644 --- a/src/renderer/components/pages/LogsPage.tsx +++ b/src/renderer/components/pages/LogsPage.tsx @@ -137,6 +137,17 @@ export class LogsPage extends React.Component {
    + {/* Copy Diagnostics Button */} +
    +
    + +
    +
    {/* Create Logs Window Button */} { !this.props.isLogsWindow && (
    @@ -149,17 +160,6 @@ export class LogsPage extends React.Component {
    )} - {/* Copy Diagnostics Button */} -
    -
    - -
    -
    {/* Upload Logs Button */}
    From 7f9575abfef61e1c08247e858a1bd4a987dbe99f Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Fri, 22 Jul 2022 22:01:30 +0100 Subject: [PATCH 57/62] chore: Update lang --- lang/cs-CZ.json | 85 ++++--- lang/da-DK.json | 55 +++-- lang/de-DE.json | 33 ++- lang/eo-UY.json | 35 ++- lang/es-ES.json | 51 +++-- lang/et-EE.json | 39 +++- lang/fi-FI.json | 73 +++--- lang/fil-PH.json | 109 +++++---- lang/fr-FR.json | 487 ++++++++++++++++++++-------------------- lang/hu-HU.json | 33 ++- lang/it-IT.json | 565 ++++++++++++++++++++++++----------------------- lang/ja-JP.json | 39 +++- lang/mk-MK.json | 35 ++- lang/nl-NL.json | 133 ++++++----- lang/pl-PL.json | 127 ++++++----- lang/pt-BR.json | 35 ++- lang/pt-PT.json | 33 ++- lang/ro-RO.json | 145 ++++++------ lang/ru-RU.json | 179 ++++++++------- lang/sr-CS.json | 33 ++- lang/sr-SP.json | 37 +++- lang/sv-SE.json | 137 +++++++----- lang/tr-TR.json | 73 +++--- lang/uk-UA.json | 137 +++++++----- lang/vi-VN.json | 111 ++++++---- lang/zh-CN.json | 35 ++- lang/zh-TW.json | 33 ++- 27 files changed, 1700 insertions(+), 1187 deletions(-) diff --git a/lang/cs-CZ.json b/lang/cs-CZ.json index 443cab63f..d7487b772 100644 --- a/lang/cs-CZ.json +++ b/lang/cs-CZ.json @@ -2,22 +2,31 @@ "name": "Čeština", "config": { "configHeader": "Nastavení", - "configDesc": "(Pro aplikaci některých změn musíte stisknout tlačítko 'Uložit a restartovat')", - "preferencesHeader": "Předvolby", - "extremeGames": "Obsah pro dospělé", - "extremeGamesDesc": "Povolí zobrazování her a štítků patřících do filtrační skupiny \"Pro dospělé\".", + "configDesc": "(Pro aplikaci některých změn musíte stisknout tlačítko \"Uložit a restartovat\")", + "preferencesHeader": "Přizpůsobení", + "extremeGames": "Zobrazit filtry obsahu pro dospělé", + "extremeGamesDesc": "Umožňuje přepínání, tvorbu a úpravy filtrů štítků označujících obsah nevhodný pro děti.", + "hideExtremeScreenshots": "Skrývat snímky obrazovky pro dospělé", + "hideExtremeScreenshotsDesc": "Skrývá snímky obrazovky u her s označením \"Pro Dospělé\", může být odkryto kliknutím na rozostřený obrázek.", + "fancyAnimations": "Hezčí Animace", + "fancyAnimationsDesc": "Povolí ve spouštěči hezčí animace.", + "searchLimit": "Limit výsledků vyhledávání", + "searchLimitDesc": "Limituje počet výsledků navrácených jakýmkoliv vyhledáváním", + "searchLimitUnlimited": "Neomezené", + "searchLimitValue": "{0} Výsledků", "enableEditing": "Povolit upravování", - "enableEditingDesc": "Povolit úpravy her a dalších aplikací. Také zobrazuje záložky související s úpravami.", + "enableEditingDesc": "Povolit úpravy her a přídavných aplikací. Také zobrazí záložky související s úpravami.", "onDemandImages": "Obrázky na vyžádání", "onDemandImagesDesc": "Stahovat a ukládat chybějící screenshoty a náhledy podle potřeby.", "currentLanguage": "Jazyk", "currentLanguageDesc": "Jazyk, který bude program primárně používat.", "fallbackLanguage": "Záložní jazyk", - "fallbackLanguageDesc": "Jazyk, který se použije, pokud aktuálně nastavený jazyk není kompletně přeložen.", + "fallbackLanguageDesc": "Jazyk, který se použije, pokud text není dostupný v preferovaném jazyku.", "auto": "Automaticky ({0})", "none": "Žádný", + "contentFiltersHeader": "Filtry obsahu", "flashpointHeader": "Flashpoint", - "flashpointPath": "Cesta k Flashpoint", + "flashpointPath": "Cesta k Flashpointu", "flashpointPathDesc": "Cesta ke složce Flashpoint (může být relativní)", "browse": "Procházet", "libraries": "Knihovny", @@ -36,7 +45,7 @@ "appPathOverridesDesc": "Přepíše cestu aplikace vlevo cestou vpravo při spouštění her.", "visualsHeader": "Vzhled", "useCustomTitleBar": "Použít upravenou horní lištu", - "useCustomTitleBarDesc": "Použije upravený vzhled horní lišty na základě motivu.", + "useCustomTitleBarDesc": "Upraví vzhled horní lišty na základě zvoleného motivu.", "theme": "Motiv", "noTheme": "Žádný motiv", "themeDesc": "ID motivu, který se má použít.", @@ -45,7 +54,7 @@ "logoSetDesc": "ID sady log, která má být použita.", "advancedHeader": "Pokročilé", "showDeveloperTab": "Zobrazit záložku pro vývojáře", - "showDeveloperTabDesc": "Zobrazí záložku 'Vývojář'. S největší pravděpodobností užitečné pouze pro vývojáře a kurátory.", + "showDeveloperTabDesc": "Zobrazí záložku \"Vývojář\". Toto je obvykle užitečné pouze pro vývojáře a kurátory.", "server": "Server", "serverDesc": "Server proces ke spuštění při zapínání launcheru.", "metadataServerHost": "Metadata Server Host", @@ -64,26 +73,27 @@ "updateHeader": "Aktualizace spouštěče", "currentVersion": "Současná verze", "nextVersion": "Další verze", - "updateAvailable": "Aktualizace je dostupná", + "updateAvailable": "Je dostupná aktualizace", "upToDate": "Aktuální.", "downloadingUpdate": "Stahuji aktualizaci...", - "quickStartHeader": "Rychlý Start", - "hallOfFameInfo": "Zajímá vás jen to nejlepší? Mrkněte na {0}!", - "hallOfFame": "Síň slávy", - "allGamesInfo": "Hledáte, co byste si zahráli? Podívejte se na {0}.", - "allGames": "Katalog her", - "allAnimationsInfo": "Chcete jen něco zhlédnout? Podívejte se na {0}.", - "allAnimations": "Katalog animací", + "quickStartHeader": "Rychlý start", + "hallOfFameInfo": "Zajímá vás jen to nejlepší? Mrkněte do {0}!", + "hallOfFame": "Síně slávy", + "allGamesInfo": "Hledáte, co byste si zahráli? Podívejte se do {0}.", + "allGames": "Katalogu her", + "allAnimationsInfo": "Chcete se jen na něco dívat? Podívejte se do {0}.", + "allAnimations": "Katalogu animací", "configInfo": "Chcete něco změnit? Běžte do {0}.", "config": "Nastavení", - "helpInfo": "Potřebujete pomoc? {0}.", + "helpInfo": "Potřebujete pomoci? {0}.", "help": "Nahlédněte do nápovědy", "upgradesHeader": "Vylepšení", + "updateFeedHeader": "Novinky", "installComplete": "Instalace dokončena", "alreadyInstalled": "Již nainstalováno", "download": "Stáhnout", "update": "Aktualizovat", - "checkingUpgradeState": "Kontrola stavu aktualizace...", + "checkingUpgradeState": "Kontrola stavu vylepšení...", "extrasHeader": "Další", "favoritesPlaylist": "Seznam oblíbených", "tagList": "Seznam štítků", @@ -103,7 +113,8 @@ "clearLog": "Vymazat logy", "copy404Urls": "Zkopírovat 404 URL adresy", "uploadLog": "Nahrát Log", - "copiedToClipboard": "Zkopírováno do Schránky" + "copiedToClipboard": "Zkopírováno do Schránky", + "openLogsWindow": "Otevřít okno s logy" }, "app": { "home": "Domů", @@ -146,20 +157,20 @@ "checkGameIdsDesc": "Vypíše všechny hry, které mají neplatné nebo duplicitní ID", "checkGameTitles": "Zkontrolovat názvy her", "checkGameTitlesDesc": "Vypíše všechny hry, které mají neplatný nebo duplicitní název v rámci stejné platformy", - "checkGameFields": "Zkontrolovat herní pole", - "checkGameFieldsDesc": "Vypíše všechny hry, které mají prázdné pole (pokud jsou povinné)", + "checkGameFields": "Zkontrolovat povinná metadata", + "checkGameFieldsDesc": "Vypíše všechny hry, které mají některé z povinných polí prázdné", "checkPlaylists": "Zkontrolovat seznamy", "checkPlaylistsDesc": "Vypíše všechny seznamy, které mají neplatné nebo duplicitní ID, anebo obsahují hry s neplatným, duplicitním nebo chybějícím ID", "checkGameFileLocation": "Zkontrolovat umístění herních souborů", - "checkGameFileLocationDesc": "Vypíše všechny hry, které mají chybou cestu ve spouštěcím příkazu (týká se 'Otevřít umístění souboru', nikoliv spouštění hry)", + "checkGameFileLocationDesc": "Vypíše všechny hry, které mají ve spouštěcím příkazu chybou cestu (vztahuje se k \"Otevřít umístění souboru\", nikoliv ke spouštění hry)", "checkMissingExecMappings": "Zkontrolovat Chybějící Mappingy Exekutiv", "checkMissingExecMappingsDesc": "Vypíše všechny unikátní exekutivy pro win32, linux a darwin kterým chybí mapping", "renameImagesTitleToId": "Přejmenovat obrázky (název => ID)", - "renameImagesTitleToIdDesc": "Najde všechny obrázky, které mají ve jméně souboru název hry a přejmenuje je na ID", + "renameImagesTitleToIdDesc": "Najde všechny herní obrázky, používající ve jméně souboru název hry, ke které jsou přidruženy a přejmenuje je, aby místo toho používaly ID hry", "renameImagesIdToTitle": "Přejmenovat obrázky (ID => název)", - "renameImagesIdToTitleDesc": "Najde všechny obrázky, které mají ve jméně souboru ID a přejmenuje je na název hry", + "renameImagesIdToTitleDesc": "Najde všechny herní obrázky, používající ve jméně souboru ID hry, ke které jsou přidruženy a přejmenuje je, aby místo toho používaly název hry", "createMissingFolders": "Vytvořit chybějící složky", - "createMissingFoldersDesc": "Najde všechny složky, které chybí v definované Flashpoint struktuře složek a vytvoří je", + "createMissingFoldersDesc": "Najde všechny složky, které chybí ve struktuře složek Flashpointu a vytvoří je", "importLegacyPlatforms": "Importovat Legacy Platformy", "importLegacyPlatformsDesc": "Importuje Legacy Platformy z `Data/Platforms/` - POZOR: Přepíše hry s konflikty", "importLegacyPlaylists": "Importovat Legacy Seznamy", @@ -184,11 +195,11 @@ "servicesMissing": "Nebyly nalezeny žádné služby.", "running": "Spuštěno", "stopped": "Zastaveno", - "killing": "Násilné ukončení", + "killing": "Ukončování", "start": "Spustit", "startDesc": "Spustit službu", "stop": "Zastavit", - "stopDesc": "Zastavit službu", + "stopDesc": "Zastavit spuštěnou službu", "restart": "Restartovat", "restartDesc": "Restartovat službu", "details": "Detaily", @@ -197,7 +208,7 @@ "about": { "aboutHeader": "Podrobnosti", "flashpoint": "BlueMaxima's Flashpoint", - "flashpointDesc": "Bluemaxima's Flashpoint je projekt, jehož cílem je zachování her a animací vytvořených na starších webových platformách jako je např. Flash, Shockwave, Java Applet, ale i novějších, jako je HTML5 a Unity.", + "flashpointDesc": "Bluemaxima's Flashpoint je projekt, jehož cílem je zachování her a animací vytvořených na starších webových platformách jako například Flash, Shockwave, Java Applet, ale i novějších, jako je HTML5 a Unity. Zároveň cílí na umožnění snadného procházení a spouštění všech těchto výtvorů.", "website": "Webová stránka", "flashpointLauncher": "Flashpoint Launcher", "flashpointLauncherDesc": "Desktopová aplikace s otevřeným zdrojovým kódem, která slouží pro procházení, spuštění a správu her a animací z projektu Flashpoint.", @@ -266,7 +277,7 @@ "thumbnail": "Miniatura", "screenshot": "Náhled", "dropImageHere": "Obrázek vložíte přetažením", - "noGameSelected": "Nebyla vybrána žádná hra", + "noGameSelected": "Žádný/á/é {0} nevybrán/a/o", "clickToSelectGame": "Kliknutím na hru provedete její výběr.", "deleteAdditionalApplication": "Smazat podružnou aplikaci", "deleteGameAndAdditionalApps": "Smazat hru (a všechny podružné aplikace)", @@ -288,7 +299,10 @@ "saveAndRestart": "Uložit a restartovat", "thereAreNoGames": "Nebyly nalezeny žádné hry.", "noGameMatchedDesc": "Nebyly nalezeny žádné výsledky hledání.", - "noGameMatchedSearch": "Zkuste méně omezit výběr hledání." + "noGameMatchedSearch": "Zkuste méně omezit výběr hledání.", + "mountParameters": "Zavádějící parametry", + "noMountParameters": "Žádné zavádějící parametry", + "showExtremeScreenshot": "Zobrazovat snímky obrazovky pro dospělé" }, "tags": { "name": "Název", @@ -413,7 +427,9 @@ "installingFiles": "Instaluji soubory...", "complete": "Dokončeno", "exportMetaEditTitle": "Exportovat úpravy metadat", - "exportMetaEditDesc": "Vyberte všechny vlastnosti k exportu:" + "exportMetaEditDesc": "Vyberte všechny vlastnosti k exportu:", + "showImage": "Zaostřit obrázek", + "searching": "Vyhledávám..." }, "menu": { "viewThumbnailInFolder": "Otevřít složku s umístěním miniatury", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Hry", + "arcadeSingular": "Hra", "arcadePlural": "Všechny Hry", "theatre": "Animace", + "theatreSingular": "Animace", "theatrePlural": "Všechny Animace", "auditorium": "Newgrounds Auditorium", + "auditoriumSingular": "Animace", "auditoriumPlural": "Všechny Newgrounds Animace" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Loga a snímky obrazovky", "screenshotsDesc": "Přidá loga do mřížkového zobrazení a snímky obrazovky pro všechny hry." } -} \ No newline at end of file +} diff --git a/lang/da-DK.json b/lang/da-DK.json index 064678827..9e652d1b8 100644 --- a/lang/da-DK.json +++ b/lang/da-DK.json @@ -4,8 +4,16 @@ "configHeader": "Konfiguration", "configDesc": "(Du skal trykke 'Gem & Genstart' for at nogen af ændringerne tager effekt)", "preferencesHeader": "Preferencer", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "Tænd Redigering", "enableEditingDesc": "Aktiver redigering af spil og yderligere programmer. Viser også faner relateret til redigering.", "onDemandImages": "Ved Efterspørgselsbilleder", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Hvilket sprog til at bruge på et ukomplet sprog.", "auto": "Auto ({0})", "none": "Ingen", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Flashpoint Sti", "flashpointPathDesc": "Stien til Flashpoint folderen (kan være relativt)", @@ -79,6 +88,7 @@ "helpInfo": "Brug for help? {0}.", "help": "Læs manualen", "upgradesHeader": "Opgraderinger", + "updateFeedHeader": "News Feed", "installComplete": "Installation er færdig", "alreadyInstalled": "Allerede Installeret", "download": "Download", @@ -103,7 +113,8 @@ "clearLog": "Ryd log-filen", "copy404Urls": "Kopiér 404 URL'er", "uploadLog": "Upload Log", - "copiedToClipboard": "Kopieret til udklipsholder" + "copiedToClipboard": "Kopieret til udklipsholder", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Hjem", @@ -266,7 +277,7 @@ "thumbnail": "Thumbnail", "screenshot": "Skærmbillede", "dropImageHere": "Sæt et billede her for at tilføje det", - "noGameSelected": "Ingen Spil Valgt", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Klik på et spil for at vælge it.", "deleteAdditionalApplication": "Slet Additionel Applikation", "deleteGameAndAdditionalApps": "Slet Spil (og Additionelle Applikationer)", @@ -288,7 +299,10 @@ "saveAndRestart": "Gem & Genstart", "thereAreNoGames": "Der er ingen spil.", "noGameMatchedDesc": "Ingen game titel har matchet din søgning.", - "noGameMatchedSearch": "Prøv at søge på noget mindre restriktivt." + "noGameMatchedSearch": "Prøv at søge på noget mindre restriktivt.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Navn", @@ -318,16 +332,16 @@ "importAllDesc": "Importer alle kurationer som er indlæst lige nu", "deleteAll": "Slet Alle", "deleteAllDesc": "Slet alle kurationer, der i øjeblikket er indlæst", - "saveImportedCurations": "Gem Importerede Curations", + "saveImportedCurations": "Gem Importerede Kurationer", "keepArchiveKey": "Behold Kuration Arkiv UUID", "symlinkCurationContent": "Symlink Curation Content Folder (Admin prompts på Windows, Kræves for MAD4FP)", "useTagFilters": "Use Tag Filters in Suggestions", - "openCurationsFolder": "Curations Mappe", - "openCurationsFolderDesc": "Åbn Curations mappen i explorer", - "openExportsFolder": "Eksporterede Kurser", - "openExportsFolderDesc": "Åbn standard Eksporter mappe der indeholder dine eksporterede curations.", - "openImportedFolder": "Importerede Kurser", - "openImportedFolderDesc": "Åbn standardmappen Importerede kurver, der indeholder dine importerede kurver.", + "openCurationsFolder": "Kurationer Mappe", + "openCurationsFolderDesc": "Åbn Kurationer mappen i explorer", + "openExportsFolder": "Eksporterede Kurationer", + "openExportsFolderDesc": "Åbn standard Eksporter mappe der indeholder dine eksporterede kurationer.", + "openImportedFolder": "Importerede Kurationer", + "openImportedFolderDesc": "Åbn standardmappen Importerede kurationer, der indeholder dine importerede kurationer.", "newCuration": "Ny Kuration", "newCurationDesc": "Opretter en ny kuration med en unik mappe", "loadMeta": "Indlæs Meta", @@ -338,7 +352,7 @@ "loadFolderDesc": "Indlæls en eller mere af kuration foldere", "scanNewCurationFolders": "Scan For New Curations", "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", - "noCurations": "Ingen Kurser", + "noCurations": "Ingen Kurationer", "id": "Kuration Mappe", "heading": "Overskrift", "noHeading": "Ingen Overskrift", @@ -413,7 +427,9 @@ "installingFiles": "Installerer filer...", "complete": "Færdig", "exportMetaEditTitle": "Eksporter Meta Rediger", - "exportMetaEditDesc": "Vælg alle egenskaber at eksportere:" + "exportMetaEditDesc": "Vælg alle egenskaber at eksportere:", + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Vis Thumbnail i Folderen", @@ -469,17 +485,17 @@ "overwriteFileMessage": "En fil med samme navn findes allerede. Vil du overskrive den?", "overwriteFileDetail": "Fil sti:", "deleteTagFilterGroup": "Delete this Tag Filter Group?", - "deleteCuration": "Delete this Curation?", + "deleteCuration": "Slet denne kuration?", "importCuration": "Importér denne Curation?", "deleteGameImage": "Slet dette spil billede?", "deletePlaylist": "Slet denne Spilleliste?", "importAllCurations": "Import all Curations?", - "deleteAllCurations": "Delete all Curations?", + "deleteAllCurations": "Slet alle kuratione?", "removePlaylistGame": "Fjern dette spil fra spillelisten?", "deleteGame": "Slet dette spil? Dette kan ikke fortrydes.", "deleteGameData": "Slet dette spildata? Dette kan ikke fortrydes.", "deleteAddApp": "Slet denne Tilføj app?", - "deleteTagCategory": "Delete this Tag Category?", + "deleteTagCategory": "Slet dette Tag Kategori?", "deleteTag": "Slet dette Tag?", "deleteTagAlias": "Slet dette Tag Alias?", "deleteSource": "Slet denne kilde?", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Spil", + "arcadeSingular": "Game", "arcadePlural": "Alle Spil", "theatre": "Animationer", + "theatreSingular": "Animation", "theatrePlural": "Alle Animationer", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Alle NG Animationer" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logoer Og Skærmfotos", "screenshotsDesc": "Tilføjer logoer for gitter visning og skærmbilleder for alle spil." } -} \ No newline at end of file +} diff --git a/lang/de-DE.json b/lang/de-DE.json index 8b7d783df..7ebb661a3 100644 --- a/lang/de-DE.json +++ b/lang/de-DE.json @@ -4,8 +4,16 @@ "configHeader": "Konfiguration", "configDesc": "(Einige Änderungen erfordern einen Neustart)", "preferencesHeader": "Einstellungen", - "extremeGames": "Extreme Inhalte", - "extremeGamesDesc": "Erlaube Tags der \"Extrem\" Tag-Filtergruppen und entsprechende Spiele anzuzeigen.", + "extremeGames": "\"Extrem\"-Filter anzeigen", + "extremeGamesDesc": "Ermöglicht das Einschalten, Erstellen und Verändern von Extrem-Tag-Filtern, um Inhalte zu steuern, die für Kinder ungeeignet sind.", + "hideExtremeScreenshots": "Extreme Screenshots verstecken", + "hideExtremeScreenshotsDesc": "Versteckt Screenshots von als Extrem markierten Inhalten. Mit einem Klick auf das Bildfeld können sie wieder angezeigt werden.", + "fancyAnimations": "Schicke Animationen", + "fancyAnimationsDesc": "Aktiviere schicke Animationen im Launcher.", + "searchLimit": "Suchbegrenzung", + "searchLimitDesc": "Begrenze die Anzahl an angezeigten Suchergebnissen", + "searchLimitUnlimited": "Unbegrenzt", + "searchLimitValue": "{0} Ergebnis(se)", "enableEditing": "Bearbeiten Erlauben", "enableEditingDesc": "Aktiviere Bearbeitung von Spielen und zusätzlichen Anwendungen. Zeigt auch Tabs im Zusammenhang mit der Bearbeitung.", "onDemandImages": "Bilder auf Nachfrage", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Welche Sprache benutzt wird, wenn in der ersten Sprache ein Wort nicht übersetzt ist.", "auto": "Automatisch ({0})", "none": "Keine", + "contentFiltersHeader": "Inhaltsfilter", "flashpointHeader": "Flashpoint", "flashpointPath": "Dateipfad Flashpoint", "flashpointPathDesc": "Der Dateipfad zum Flashpoint-Ordner (kann relativ angegeben werden)", @@ -79,6 +88,7 @@ "helpInfo": "Brauchst du Hilfe? {0}.", "help": "Benutzerhandbuch lesen", "upgradesHeader": "Upgrades", + "updateFeedHeader": "Nachrichtenfeed", "installComplete": "Installation abgeschlossen", "alreadyInstalled": "Bereits installiert", "download": "Herunterladen", @@ -103,7 +113,8 @@ "clearLog": "Protokoll löschen", "copy404Urls": "404-URLs kopieren", "uploadLog": "Log hochladen", - "copiedToClipboard": "In Zwischenablage kopiert" + "copiedToClipboard": "In Zwischenablage kopiert", + "openLogsWindow": "Öffne Logs Fenster" }, "app": { "home": "Start", @@ -266,7 +277,7 @@ "thumbnail": "Thumbnail", "screenshot": "Screenshot", "dropImageHere": "Bild zum Einfügen hierher ziehen", - "noGameSelected": "Kein Spiel ausgewählt", + "noGameSelected": "Kein {0} ausgewählt", "clickToSelectGame": "Klicke auf ein Spiel, um es auszuwählen.", "deleteAdditionalApplication": "Zusätzliche Anwendung löschen", "deleteGameAndAdditionalApps": "Spiel löschen (und zusätzliche Anwendungen)", @@ -288,7 +299,10 @@ "saveAndRestart": "Speichern & neu starten", "thereAreNoGames": "Es gibt keine Spiele.", "noGameMatchedDesc": "Kein Spieltitel entspricht deiner Suche.", - "noGameMatchedSearch": "Versuche, nach etwas weniger Restriktivem zu suchen." + "noGameMatchedSearch": "Versuche, nach etwas weniger Restriktivem zu suchen.", + "mountParameters": "Mount-Parameter", + "noMountParameters": "Keine Mount-Parameter", + "showExtremeScreenshot": "Extremen Screenshot anzeigen" }, "tags": { "name": "Name", @@ -413,7 +427,9 @@ "installingFiles": "Dateien werden installiert...", "complete": "Abgeschlossen", "exportMetaEditTitle": "Meta-Edit exportieren", - "exportMetaEditDesc": "Alle zu exportierenden Eigenschaften auswählen:" + "exportMetaEditDesc": "Alle zu exportierenden Eigenschaften auswählen:", + "showImage": "Bild anzeigen", + "searching": "Suche..." }, "menu": { "viewThumbnailInFolder": "Miniaturansicht im Ordner anzeigen", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Spiele", + "arcadeSingular": "Spiel", "arcadePlural": "Alle Spiele", "theatre": "Animationen", + "theatreSingular": "Animation", "theatrePlural": "Alle Animationen", "auditorium": "NG-Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Alle NG-Animationen" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logos & Screenshots", "screenshotsDesc": "Fügt Logos für die Rasteransicht und Screenshots für alle Spiele hinzu." } -} \ No newline at end of file +} diff --git a/lang/eo-UY.json b/lang/eo-UY.json index f8b7c6900..e457a3bf3 100644 --- a/lang/eo-UY.json +++ b/lang/eo-UY.json @@ -1,11 +1,19 @@ { - "name": "Angla", + "name": "Esperanto", "config": { "configHeader": "Agordoj", "configDesc": "(Vi devas klaki 'Konservu kaj Rekomencu' por ekvalidi la ŝanĝojn)", "preferencesHeader": "Agordoj", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "Ebligu Redaktadon", "enableEditingDesc": "Ebligu redaktadon de ludoj kaj aldonaj programoj. Ankaŭ montras langetojn rilataj al redaktado.", "onDemandImages": "Laŭpetaj Bildoj", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "La lingvo uzota kiam la nuna lingvo nekompletas.", "auto": "Aŭtomata ({0})", "none": "Neniu", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Flashpointa Vojo", "flashpointPathDesc": "Vojo al la dosierujo de Flashpoint (povas esti relativa)", @@ -79,6 +88,7 @@ "helpInfo": "Ĉu vi bezonas helpon? {0}.", "help": "Legu la instrukcion", "upgradesHeader": "Ĝisdatigoj", + "updateFeedHeader": "News Feed", "installComplete": "Instalado Finiĝis", "alreadyInstalled": "Jam Instalite", "download": "Elŝuti", @@ -103,7 +113,8 @@ "clearLog": "Forigu Protokolon", "copy404Urls": "Kopiu 404-URLojn", "uploadLog": "Alŝutu protokolon", - "copiedToClipboard": "Kopiita al Tondobufro" + "copiedToClipboard": "Kopiita al Tondobufro", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Hejmo", @@ -266,7 +277,7 @@ "thumbnail": "Miniaturo", "screenshot": "Ekrankopio", "dropImageHere": "Faligu bildon ĉi tie por aldoni ĝin", - "noGameSelected": "Neniu Ludo Elektita", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Alklaku ludon por elekti ĝin.", "deleteAdditionalApplication": "Forigi Plian Aplikon", "deleteGameAndAdditionalApps": "Forigi Ludon (kaj Pliajn Aplikojn)", @@ -288,7 +299,10 @@ "saveAndRestart": "Konservu kaj Rekomencu", "thereAreNoGames": "Neniuj ludoj.", "noGameMatchedDesc": "Neniu ludotitolo konvenas vian serĉon.", - "noGameMatchedSearch": "Provu serĉi ion malpli restriktan." + "noGameMatchedSearch": "Provu serĉi ion malpli restriktan.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Nomo", @@ -413,7 +427,9 @@ "installingFiles": "Instalanta Dosieroj...", "complete": "Kompleta", "exportMetaEditTitle": "Eksportu Meta-redakton", - "exportMetaEditDesc": "Elektu ĉiujn eksportotajn ecojn:" + "exportMetaEditDesc": "Elektu ĉiujn eksportotajn ecojn:", + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Vidu Bildeton en Dosierujo", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Ludoj", + "arcadeSingular": "Game", "arcadePlural": "Ĉiuj Ludoj", "theatre": "Animacioj", + "theatreSingular": "Animation", "theatrePlural": "Ĉiuj Animacioj", "auditorium": "NG Aŭditorio", + "auditoriumSingular": "Animation", "auditoriumPlural": "Ĉiuj NGaj Animacioj" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logotipoj kaj Ekrankopioj", "screenshotsDesc": "Aldonas emblemojn por Krada vido kaj ekrankopioj por ĉiuj ludoj." } -} \ No newline at end of file +} diff --git a/lang/es-ES.json b/lang/es-ES.json index da87b4b56..210771f00 100644 --- a/lang/es-ES.json +++ b/lang/es-ES.json @@ -4,8 +4,16 @@ "configHeader": "Configuración", "configDesc": "(Debes presionar 'Guardar y Reiniciar' para que algunos cambios surtan efecto)", "preferencesHeader": "Preferencias", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Mostrar Filtros Extremos", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Animaciones elegantes", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Límite de búsqueda", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Ilimitado", + "searchLimitValue": "{0} resultados", "enableEditing": "Habilitar la edición", "enableEditingDesc": "Hablitar la edición de juegos y aplicaciones adicionales. También muestra pestañas relacionadas con la edición.", "onDemandImages": "Imágenes bajo demanda", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Qué idioma usar en caso de que el idioma actual este incompleto.", "auto": "Automático ({0})", "none": "Ninguno", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Ruta de Flashpoint", "flashpointPathDesc": "Ruta de la carpeta Flashpoint (puede ser relativa)", @@ -28,11 +37,11 @@ "nativePlatforms": "Plataformas nativas", "nativePlatformsDesc": "Usar versiones nativas de estas plataformas. (Si no está disponible, utilizará Wine)", "tagFilterGroups": "Grupos de filtros de etiquetas", - "tagFilterGroupsDesc": "Estos grupos filtraran sugerencias y juegos afuera de los resultados de búsqueda", + "tagFilterGroupsDesc": "Estos grupos filtrarán sugerencias y juegos afuera de los resultados de búsqueda", "editTagFilter": "Editar grupo de filtros de etiquetas", "duplicateTagFilter": "Duplicar grupo de filtros de etiquetas", "deleteTagFilter": "Eliminar grupo de filtros de etiquetas", - "appPathOverrides": "Reemplazos de Rutas de Aplicación", + "appPathOverrides": "Reemplazos de rutas de aplicación", "appPathOverridesDesc": "Reemplaza la ruta de la aplicación de la izquierda con la de la derecha al iniciar juegos.", "visualsHeader": "Visuales", "useCustomTitleBar": "Usar barra de título personalizada", @@ -79,6 +88,7 @@ "helpInfo": "¿Necesitas Ayuda? {0}.", "help": "Lee el manual", "upgradesHeader": "Mejoras", + "updateFeedHeader": "News Feed", "installComplete": "Instalación completada", "alreadyInstalled": "Ya instalado", "download": "Descargar", @@ -103,7 +113,8 @@ "clearLog": "Limpiar el registro", "copy404Urls": "Copiar las URLs 404", "uploadLog": "Subir registro", - "copiedToClipboard": "Copiado al portapapeles" + "copiedToClipboard": "Copiado al portapapeles", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Inicio", @@ -177,9 +188,9 @@ "importTags": "Importar etiquetas", "importTagsDesc": "Importa etiquetas desde un archivo creado por 'Exportar etiquetas'", "migrateExtremeGames": "Migrar juegos extremos", - "migrateExtremeGamesDesc": "Remueve 'Extremo' de cualquier juego y asigna la etiqueta 'LEGACY-Extremo' a ellos", - "massImportGameData": "Mass Import Game Data", - "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", + "migrateExtremeGamesDesc": "Remueve 'Extreme' de cualquier juego y asigna la etiqueta 'LEGACY-Extreme' a ellos", + "massImportGameData": "Importar en masa datos de juego", + "massImportGameDataDesc": "Importa en masa paquetes de datos de juego, tratando de igualar su nombre de archivo al UUID de un juego.", "servicesHeader": "Servicios en segundo plano", "servicesMissing": "No hay servicios.", "running": "Corriendo", @@ -266,7 +277,7 @@ "thumbnail": "Miniatura", "screenshot": "Captura de pantalla", "dropImageHere": "Suelta una imagen aquí para agregarla", - "noGameSelected": "Ningún juego seleccionado", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Haz clic en un juego para seleccionarlo.", "deleteAdditionalApplication": "Eliminar aplicación adicional", "deleteGameAndAdditionalApps": "Eliminar juego (y aplicaciones adicionales)", @@ -288,7 +299,10 @@ "saveAndRestart": "Guardar y reiniciar", "thereAreNoGames": "No hay juegos.", "noGameMatchedDesc": "Ningún título de un juego coincide con tu búsqueda.", - "noGameMatchedSearch": "Intenta buscar algo menos restrictivo." + "noGameMatchedSearch": "Intenta buscar algo menos restrictivo.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Nombre", @@ -371,7 +385,7 @@ "nonExistingLibrary": "'Biblioteca' no es el nombre de una biblioteca existente! (La predeterminada será usada)", "nonContentFolders": "Carpetas diferentes a 'content' encontradas en la carpeta de curación. ¿Deberían estar ahí?", "noTags": "No hay etiquetas en esta curación.", - "noSource": "'Source' is a required field.", + "noSource": "'Fuente' es un campo obligatorio.", "unenteredTag": "El campo de texto de etiquetas no ha sido enviado.", "noLogo": "No hay logotipo en esta curacion.", "noScreenshot": "No hay captura de pantalla en esta curación.", @@ -413,7 +427,9 @@ "installingFiles": "Instalando archivos...", "complete": "Completado", "exportMetaEditTitle": "Exportar correcciones de metadatos", - "exportMetaEditDesc": "Selecciona todas las propiedades a exportar:" + "exportMetaEditDesc": "Selecciona todas las propiedades a exportar:", + "showImage": "Mostrar imagen", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Ver miniatura en la carpeta", @@ -463,7 +479,7 @@ "areYouSurePlaylistRemove": "¿Está seguro que desea eliminar esta entrada de la lista de reproducción?", "cancel": "Cancelar", "mergePlaylists": "Combinar listas de reproducción", - "newPlaylist": "Mantener separados", + "newPlaylist": "Mantener separadas", "uploadPrivacyWarning": "Esto hará que sus registros estén disponibles públicamente.\nSe copiará un enlace al portapapeles.\n\n¿Está seguro?", "overwriteFileTitle": "¡El archivo ya existe!", "overwriteFileMessage": "Ya existe un archivo con el mismo nombre. ¿Desea sobrescribirlo?", @@ -484,18 +500,21 @@ "deleteTagAlias": "¿Eliminar este alias de etiqueta?", "deleteSource": "¿Eliminar esta fuente?", "uninstallGame": "¿Desinstalar este juego?", - "unableToUninstallGameData": "No se pudo eliminar el paquete de datos. ¿Está en uso? Intenta reiniciar el lanzador.", - "unableToDeleteGame": "No se pudo eliminar el juego. ¿Está en uso? Intenta reiniciar el lanzador.", + "unableToUninstallGameData": "No se pudo eliminar el paquete de datos. ¿Está en uso? Intenta reiniciar la aplicación.", + "unableToDeleteGame": "No se pudo eliminar el juego. ¿Está en uso? Intenta reiniciar la aplicación.", "downloadingGame": "Descargando juego...", "verifyingGame": "Verificando juego...", "aFewMinutes": "Esto puede tomar unos minutos." }, "libraries": { "arcade": "Juegos", + "arcadeSingular": "Game", "arcadePlural": "Todos los juegos", "theatre": "Animaciones", + "theatreSingular": "Animation", "theatrePlural": "Todas las animaciones", "auditorium": "Auditorio NG", + "auditoriumSingular": "Animation", "auditoriumPlural": "Todas las animaciones NG" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logotipos y capturas de pantalla", "screenshotsDesc": "Añade logotipos para la vista en cuadrícula y capturas de pantalla para todos los juegos." } -} \ No newline at end of file +} diff --git a/lang/et-EE.json b/lang/et-EE.json index b75958e64..b0ba4c726 100644 --- a/lang/et-EE.json +++ b/lang/et-EE.json @@ -4,8 +4,16 @@ "configHeader": "Sätted", "configDesc": "(Sa pead vajutama 'Salvesta & Ava Uuesti' , et muudatused tuleksid nähtavale)", "preferencesHeader": "Eelistused", - "extremeGames": "Ekstreemsed Mängud", - "extremeGamesDesc": "Näita mänge seksuaalse, vägivaldse või muu sisuga, mis on sobimatu lastele", + "extremeGames": "Näita ekstreemseid filtreid", + "extremeGamesDesc": "Lubab kasutada, luua ning muuta ekstreemseid filtreid mis võimaldavad kontrollida sisu mis ei pruugi sobida alaealistele.", + "hideExtremeScreenshots": "Peida Ekstreemseid Ekraanipilte", + "hideExtremeScreenshotsDesc": "Peidab ekstreemsete mängude ekraanipilte, neid saab avada klikkides pildi kastile.", + "fancyAnimations": "Uhked animatsioonid", + "fancyAnimationsDesc": "Luba uhked animatsioonid käivitajas.", + "searchLimit": "Otsingu limiit", + "searchLimitDesc": "Piira tulemuste arvu mistahes otsingus", + "searchLimitUnlimited": "Piiramatu", + "searchLimitValue": "{0} Tulemust", "enableEditing": "Luba Kohaldada", "enableEditingDesc": "Luba kohaldamist mängudele ja lisarakendustele. Avab ka selle jaoks akna.", "onDemandImages": "Pildid Nõudlusel", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Seda keelt hakkab rakendus kasutama pooliku esmase keele asemel.", "auto": "Automaatne ({0})", "none": "Mitte Ühtegi", + "contentFiltersHeader": "Sisu filtrid", "flashpointHeader": "Flashpoint", "flashpointPath": "Flashpointi Tee", "flashpointPathDesc": "Tee Flashpointi kaustani (võib olla suhteline)", @@ -79,6 +88,7 @@ "helpInfo": "Vajad abi? {0}.", "help": "Loe Käsiraamatut", "upgradesHeader": "Täiendused", + "updateFeedHeader": "Uudisvoog", "installComplete": "Installimine Valmis", "alreadyInstalled": "Juba Installitud", "download": "Lae Alla", @@ -103,7 +113,8 @@ "clearLog": "Tühjenda Logid", "copy404Urls": "Kopeeri 404 URL'id", "uploadLog": "Laadi Uus Logi Üles", - "copiedToClipboard": "Kopeeritud Lõikelauale" + "copiedToClipboard": "Kopeeritud Lõikelauale", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Pealeht", @@ -124,7 +135,7 @@ "newGame": "Uus mäng", "list": "Nimekiri", "grid": "Ruudustik", - "searchResults": "Otsi Tulemusi" + "searchResults": "Tulemusi leitud" }, "filter": { "dateAdded": "Kuupäev Lisatud", @@ -185,7 +196,7 @@ "running": "Mängib", "stopped": "Peatatud", "killing": "Tapab", - "start": "Start", + "start": "Käivita", "startDesc": "Alusta teenus", "stop": "Stopp", "stopDesc": "Peata mängiv teenus", @@ -266,7 +277,7 @@ "thumbnail": "Logo", "screenshot": "Ekraanipilt", "dropImageHere": "Kukuta pilt siia, et seda lisada", - "noGameSelected": "Ühtegi Mängu Pole Valitud", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Vajuta mängule, et seda valida.", "deleteAdditionalApplication": "Kustuta Lisarakendus", "deleteGameAndAdditionalApps": "Kustuta Mäng (ja Selle Lisarakendused)", @@ -288,7 +299,10 @@ "saveAndRestart": "Salvesta & Ava Uuesti", "thereAreNoGames": "Siin pole ühtegi mängu.", "noGameMatchedDesc": "Ükski mängu pealkiri ei sarnanenud sinu otsinguga.", - "noGameMatchedSearch": "Proovi otsida midagi vähem piiravamat." + "noGameMatchedSearch": "Proovi otsida midagi vähem piiravamat.", + "mountParameters": "Rakendusparameetrid", + "noMountParameters": "Pole Rakendusparameetreid", + "showExtremeScreenshot": "Näita Ekstreemset Ekraanipilti" }, "tags": { "name": "Nimi", @@ -413,7 +427,9 @@ "installingFiles": "Installib Faile...", "complete": "Valmis", "exportMetaEditTitle": "Ekspordi Meta Muudatus", - "exportMetaEditDesc": "Vali kõik väljad, mida eksportida:" + "exportMetaEditDesc": "Vali kõik väljad, mida eksportida:", + "showImage": "Näita pilti", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Vaata Logo Kaustas", @@ -492,18 +508,21 @@ }, "libraries": { "arcade": "Mängud", + "arcadeSingular": "Game", "arcadePlural": "Kõik Mängud", "theatre": "Animatsioonid", + "theatreSingular": "Animation", "theatrePlural": "Kõik Animatsioonid", "auditorium": "NG Auditoorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Kõik NG'i Animatsioonid" }, "upgrades": { - "infinity": "Flashpoint Infinity", + "infinity": "Flashpoint Lõpmatus", "infinityDesc": "Kohustuslikud failid. Annab toetust Flashi mängudele.", "tech": "Teised Tehnoloogiad", "techDesc": "Annab toetust teistele tehnoloogiatele: Shockwave, Unity, Java, HTML5 jne", "screenshots": "Logod & Ekraanipildid", "screenshotsDesc": "Annab logod ruudustikvaates ja ekraanipildid kõikidele mängudele." } -} \ No newline at end of file +} diff --git a/lang/fi-FI.json b/lang/fi-FI.json index 87edbe8ea..a4d912694 100644 --- a/lang/fi-FI.json +++ b/lang/fi-FI.json @@ -4,8 +4,16 @@ "configHeader": "Asetukset", "configDesc": "(Käyttäjän on valittava 'Tallennus ja uudelleenkäynnistys' jotta valinnat jäävät voimaan)", "preferencesHeader": "Asetukset", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Näytä lisää suodattimia", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Piilota Äärimmäiset Kuvakaappaukset", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Hienot animaatiot", + "fancyAnimationsDesc": "Ota hienot animaatiot käyttöön launcherissa.", + "searchLimit": "Hakuraja", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Rajaton", + "searchLimitValue": "{0} tulosta", "enableEditing": "Muokkaustila", "enableEditingDesc": "Ota käyttöön pelien ja muiden sovellusten muokkaus. Näyttää myös muokkaukseen liittyvät välilehdet.", "onDemandImages": "Kysyntää Koskevissa Kuvissa", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Mikä kieli näytetään, jos ensisijaisen kielen käännökset puuttuvat.", "auto": "Automaattinen kielivalinta ({0})", "none": "Ei mitään", + "contentFiltersHeader": "Sisältösuodattimet", "flashpointHeader": "Flashpoint", "flashpointPath": "Flashpointin ohjelmistopolun", "flashpointPathDesc": "Kiintolevyn ohjelmistopolku Flashpointin kansioon (voi olla relatiivinen)", @@ -23,7 +32,7 @@ "libraries": "Kirjastot", "randomLibraries": "Satunnaiset Kirjastot", "randomLibrariesDesc": "Kirjastot, joita käytetään satunnaisille Pickeille kotisivulla.", - "updateSource": "Update Source", + "updateSource": "Päivitä Lähde", "platforms": "Alustat", "nativePlatforms": "Natiivialustat", "nativePlatformsDesc": "Käytä natiiviversioita näistä alustoista. (Ei saatavilla jos Wine on käytössä)", @@ -57,7 +66,7 @@ "extLogoSets": "Logon Sarja(t)", "extApplications": "Sovellukset", "saveAndRestart": "Tallenna ja uudelleenkäynnistä", - "saveAndClose": "Save and Close", + "saveAndClose": "Tallenna ja Sulje", "tagFilterGroupEditor": "Tag Filter Group Editor" }, "home": { @@ -79,6 +88,7 @@ "helpInfo": "Oletko hukassa? Avun löydät {0}.", "help": "Lue käsikirja", "upgradesHeader": "Päivitykset", + "updateFeedHeader": "Uutissyöte", "installComplete": "Asennus valmis", "alreadyInstalled": "Asennettu valmiiksi", "download": "Lataa", @@ -103,7 +113,8 @@ "clearLog": "Tyhjennä lokitiedosto", "copy404Urls": "Kopioi 404 URL-osoitteet", "uploadLog": "Lataa Loki", - "copiedToClipboard": "Kopioitu leikepöydälle" + "copiedToClipboard": "Kopioitu leikepöydälle", + "openLogsWindow": "Avaa Loki-Ikkuna" }, "app": { "home": "Koti", @@ -258,15 +269,15 @@ "searching": "Etsitään...", "library": "Kirjasto", "defaultLibrary": "Oletuskirjasto", - "uninstallGame": "Uninstall Game", - "installed": "Installed", - "notInstalled": "Not Installed", - "legacyGame": "Legacy Game", - "download": "Download", + "uninstallGame": "Poista Peli", + "installed": "Asennettu", + "notInstalled": "Ei Asennettu", + "legacyGame": "Legacy Peli", + "download": "Lataa", "thumbnail": "Kuvake", "screenshot": "Ruutukaappaus", "dropImageHere": "Raahaa tänne kuvatiedosto lisätäksesi sen", - "noGameSelected": "Ei peliä valittuna", + "noGameSelected": "Ei {0} Valittu", "clickToSelectGame": "Klikkaa peliä valitaksesi sen.", "deleteAdditionalApplication": "Poista lisäohjelma", "deleteGameAndAdditionalApps": "Poista peli (ja lisäohjelmat)", @@ -288,7 +299,10 @@ "saveAndRestart": "Tallennus ja uudelleenkäynnistys", "thereAreNoGames": "Pelejä ei löydy.", "noGameMatchedDesc": "Ei hakua vastaavia pelejä.", - "noGameMatchedSearch": "Yritä etsiä yleisemmillä hakusanoilla." + "noGameMatchedSearch": "Yritä etsiä yleisemmillä hakusanoilla.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Nimi", @@ -413,13 +427,15 @@ "installingFiles": "Asennetaan tiedostoja...", "complete": "Valmis", "exportMetaEditTitle": "Vie Meta Muokkaus", - "exportMetaEditDesc": "Valitse kaikki vietävät ominaisuudet:" + "exportMetaEditDesc": "Valitse kaikki vietävät ominaisuudet:", + "showImage": "Näytä kuva", + "searching": "Haetaan..." }, "menu": { "viewThumbnailInFolder": "Katso kuvaketta kansiossa", "viewScreenshotInFolder": "Katso ruutukaappausta kansiossa", "openFileLocation": "Avaa tiedoston sijainti", - "addToPlaylist": "Add to Playlist", + "addToPlaylist": "Lisää soittolistaan", "duplicateMetaOnly": "Kopioi (Vain metatiedot)", "duplicateMetaAndImages": "Kopioi (Meta ja kuvat)", "copyGameUUID": "Kopioi Pelin UUID", @@ -459,7 +475,7 @@ "restartNow": "Käynnistä nyt?", "restartToApplyUpgrade": "Tämä päivitys ei tapahdu ennen kuin käynnistät ohjelman uudelleen? Haluatko tehdä sen nyt?", "areYouSure": "Oletko varma?", - "areYouSureDelete": "Are you sure you want to delete this entry?", + "areYouSureDelete": "Oletko varma, että haluat poistaa tämän kohteen?", "areYouSurePlaylistRemove": "Are you sure you want to remove this entry from the playlist?", "cancel": "Peruuta", "mergePlaylists": "Yhdistä Soittolistat", @@ -472,30 +488,33 @@ "deleteCuration": "Delete this Curation?", "importCuration": "Import this Curation?", "deleteGameImage": "Delete this Game Image?", - "deletePlaylist": "Delete this Playlist?", + "deletePlaylist": "Poistetaanko tämä soittolista?", "importAllCurations": "Import all Curations?", "deleteAllCurations": "Delete all Curations?", "removePlaylistGame": "Remove this Game from the Playlist?", "deleteGame": "Delete this Game? This cannot be undone.", "deleteGameData": "Delete this Game Data? This cannot be undone.", - "deleteAddApp": "Delete this Add App?", - "deleteTagCategory": "Delete this Tag Category?", - "deleteTag": "Delete this Tag?", + "deleteAddApp": "Poistetaanko tämä Lisä sovellus?", + "deleteTagCategory": "Poistetaanko tämän Tagin Kategoria?", + "deleteTag": "Poista tämä Tagi?", "deleteTagAlias": "Delete this Tag Alias?", - "deleteSource": "Delete this Source?", - "uninstallGame": "Uninstall this Game?", - "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", - "unableToDeleteGame": "Unable to delete game. Is it in use? Try restarting the launcher.", - "downloadingGame": "Downloading Game...", - "verifyingGame": "Verifying Game...", - "aFewMinutes": "This may take a few minutes." + "deleteSource": "Poistetaanko tämä lähde?", + "uninstallGame": "Poista tämä peli?", + "unableToUninstallGameData": "Datapakettia ei voida poistaa. Onko se käytössä? Yritä käynnistää launcher uudelleen.", + "unableToDeleteGame": "Peliä ei voi poistaa. Onko se käytössä? Yritä käynnistää launcher uudelleen.", + "downloadingGame": "Ladataan Peliä...", + "verifyingGame": "Vahvistetaan Peliä...", + "aFewMinutes": "Tämä saattaa viedä hetken." }, "libraries": { "arcade": "Pelit", + "arcadeSingular": "Peli", "arcadePlural": "Kaikki Pelit", "theatre": "Animaatiot", + "theatreSingular": "Animaatio", "theatrePlural": "Kaikki animaatiot", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animaatio", "auditoriumPlural": "Kaikki NG-animaatiot" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logot ja ruutukaappaukset", "screenshotsDesc": "Lisää pelien logot verkkonäkymään ja pelikuvat kaikkiin peleihin." } -} \ No newline at end of file +} diff --git a/lang/fil-PH.json b/lang/fil-PH.json index 711cc2c25..f34effb0e 100644 --- a/lang/fil-PH.json +++ b/lang/fil-PH.json @@ -4,8 +4,16 @@ "configHeader": "Config", "configDesc": "(Pindutin ang 'Save & Restart' upang maisave ang mga pagbabago)", "preferencesHeader": "Preperensiya", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "Pahintulutan ang Pagbabago", "enableEditingDesc": "I-enable ang pag-edit ng mga laro at mga aplikasyon. Pati na rin ang pag-kita ng mga tab na may kaugnay sa pag-edit.", "onDemandImages": "On Demand na Imahe", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Anong wika ang ibig mong gamitin sakali na hindi kumpleto ang napiling wika.", "auto": "Auto ({0})", "none": "None", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Lokasyon ng Flashpoint", "flashpointPathDesc": "Lokasyon ng Flashpoint folder (pwedeng kaugnay)", @@ -23,15 +32,15 @@ "libraries": "Mga Librerya", "randomLibraries": "Random na Librerya", "randomLibrariesDesc": "Libreryang gagamitin para sa Random Picks sa home page.", - "updateSource": "Update Source", + "updateSource": "I-update ang mga Source", "platforms": "Platforms", "nativePlatforms": "Orihinal na Platforms", "nativePlatformsDesc": "Gamitin ang orihinal na bersyon ng mga platforms. (Kung hindi maari, Wine ang gagamitin)", - "tagFilterGroups": "Tag Filter Groups", - "tagFilterGroupsDesc": "These groups will filter suggestions and games out of search results", - "editTagFilter": "Edit Tag Filter Group", - "duplicateTagFilter": "Duplicate Tag Filter Group", - "deleteTagFilter": "Delete Tag Filter Group", + "tagFilterGroups": "Pangkat ng Tag Filter", + "tagFilterGroupsDesc": "Ang mga pangkat na ito ay kayang I-filter ang mga payo at ang mga laro sa labas ng resulta ng paghahanap", + "editTagFilter": "I-edit ang mga Pangkat ng Tag Filter", + "duplicateTagFilter": "I-doble ang mga Pangkat ng Tag Filter", + "deleteTagFilter": "I-delete ang mga Pangkat ng Tag Filter", "appPathOverrides": "Mga Override ng App Path", "appPathOverridesDesc": "Ino-override ang landas ng application sa kaliwa gamit ang isa sa kanan kapag naglulunsad ng mga laro.", "visualsHeader": "Mga Biswal", @@ -57,8 +66,8 @@ "extLogoSets": "Mga Set ng Logo", "extApplications": "(Mga) Application", "saveAndRestart": "Isave at irestart", - "saveAndClose": "Save and Close", - "tagFilterGroupEditor": "Tag Filter Group Editor" + "saveAndClose": "I-save at isara", + "tagFilterGroupEditor": "Taga-edit ng Pangkat ng Tag Filter" }, "home": { "updateHeader": "I-update ang Launcher", @@ -76,9 +85,10 @@ "allAnimations": "Lahat ng Animasyon", "configInfo": "May gusto kang baguhin? Pumunta sa {0}.", "config": "Config", - "helpInfo": "Kailangan mo ng tulong? {0}.", + "helpInfo": "Kailangan ng tulong? {0}.", "help": "Basahin ang manual", "upgradesHeader": "I-upgrade", + "updateFeedHeader": "News Feed", "installComplete": "Nakompleto na ang pag-install", "alreadyInstalled": "Naka-install na", "download": "I-download", @@ -103,7 +113,8 @@ "clearLog": "Burahin ang Log", "copy404Urls": "I-kopya ang 404 URLs", "uploadLog": "Log ng pag-upload", - "copiedToClipboard": "Nakopya sa Clipboard" + "copiedToClipboard": "Nakopya sa Clipboard", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Home", @@ -116,10 +127,10 @@ "curate": "Curate", "developer": "Developer", "searchPlaceholder": "Hanapin...", - "hideRightSidebar": "Itago ang right sidebar", - "showRightSidebar": "Ipakita ang right sidebar", - "hideLeftSidebar": "Itago ang left sidebar", - "showLeftSidebar": "Ibakita ang left sidebar", + "hideRightSidebar": "Itago ang kanang sidebar", + "showRightSidebar": "Ipakita ang kanang sidebar", + "hideLeftSidebar": "Itago ang kaliwang sidebar", + "showLeftSidebar": "Ipakita ang left sidebar", "total": "Total", "newGame": "Bagong Laro", "list": "Listahan", @@ -139,14 +150,14 @@ }, "developer": { "developerHeader": "Developer", - "developerDesc": "Nandito lahat ng developer tools.", - "checkMissingImages": "Tingnan ang lahat ng Nawawalang Imahe", - "checkMissingImagesDesc": "Ilista lahat ng laro na walang thumbnail o screenshot", - "checkGameIds": "Tingnan ang Game IDs", + "developerDesc": "Nandito ang lahat ng mga kailangang developer tools.", + "checkMissingImages": "Tingnan ang lahat ng mga Nawawalang Imahe", + "checkMissingImagesDesc": "Ilista ang lahat ng laro na walang thumbnail o screenshot", + "checkGameIds": "Tingnan ang mga Game IDs", "checkGameIdsDesc": "Ilista lahat ng laro na may nadoble o maling IDs", - "checkGameTitles": "Tingnan ang Game Titles", + "checkGameTitles": "Tingnan ang mga Game Titles", "checkGameTitlesDesc": "Ipakita lahat ng mga laro na nadoble sa iisang platform", - "checkGameFields": "Tingnan ang Game Fields", + "checkGameFields": "Tingnan ang mga Game Fields", "checkGameFieldsDesc": "Ipakita lahat ng mga laro na may walang nakasulat sa fields (fields na hindi dapat blanko)", "checkPlaylists": "Tingnan ang Playlists", "checkPlaylistsDesc": "Ipakita lahat ng mga playlists na may nadoble o maling IDs, o mga laro na may kulang, maling, o nadobleng IDs", @@ -176,8 +187,8 @@ "exportTagsDesc": "Gumawa ng file na puno ng mga tag at kategorya ng tag na maaring gagamitin sa 'I-import ang mga tag'", "importTags": "I-import ang mga tag", "importTagsDesc": "I-import ang mga tag mula sa file na ginawa ng 'I-export ang tag'", - "migrateExtremeGames": "Migrate Extreme Games", - "migrateExtremeGamesDesc": "Removes 'Extreme' from any games and assigns the 'LEGACY-Extreme' tag to them", + "migrateExtremeGames": "Ilipat ang mga Extreme na Laro", + "migrateExtremeGamesDesc": "Tatanggalin ang 'Extreme' sa anumang laro at I-aassign ang 'LEGACY-Extreme' tag sa mga ito", "massImportGameData": "Mass Import Game Data", "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", "servicesHeader": "Background Services", @@ -258,15 +269,15 @@ "searching": "Hinahanap...", "library": "Aklatan", "defaultLibrary": "Default na aklatan", - "uninstallGame": "Uninstall Game", - "installed": "Installed", - "notInstalled": "Not Installed", - "legacyGame": "Legacy Game", - "download": "Download", + "uninstallGame": "I-uninstall ang Laro", + "installed": "Naka-install", + "notInstalled": "Hindi naka-install", + "legacyGame": "Legacy na Laro", + "download": "I-download", "thumbnail": "Thumbnail", "screenshot": "Screenshot", "dropImageHere": "Ilagay ang imahe dito para madagdag", - "noGameSelected": "Walang larong nakapili", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "I-click ang laro para mapili.", "deleteAdditionalApplication": "I-tangal ang Karagdagang Aplikasyon", "deleteGameAndAdditionalApps": "I-tangal ang laro (at ang Karagdagang Aplikasyon)", @@ -288,7 +299,10 @@ "saveAndRestart": "I-save at i-restart", "thereAreNoGames": "Walang laro dito.", "noGameMatchedDesc": "Walang titulo ng laro ang nag-match sa na-search mo.", - "noGameMatchedSearch": "Subukan mo i-search na di-gaanong strikto." + "noGameMatchedSearch": "Subukan mo i-search na di-gaanong strikto.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Pangalan", @@ -321,7 +335,7 @@ "saveImportedCurations": "I-save ang mga na-import na curation", "keepArchiveKey": "Panatilihin ang Curation Archive UUID", "symlinkCurationContent": "Folder ng Symlink Curation Content (May admin prompt sa Windows, kailangan para sa MAD4FP)", - "useTagFilters": "Use Tag Filters in Suggestions", + "useTagFilters": "Gamitin ang Tag Filter sa mga payo", "openCurationsFolder": "Curations Folder", "openCurationsFolderDesc": "I-buksan ang \"Curations Folder\" sa explorer", "openExportsFolder": "Mga na-export na curation", @@ -336,8 +350,8 @@ "loadArchiveDesc": "Iload ang isa o higit pang Curation archives", "loadFolder": "Iload ang Folder", "loadFolderDesc": "Iload ang isa o higit pang Curation folders", - "scanNewCurationFolders": "Scan For New Curations", - "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", + "scanNewCurationFolders": "I-hanap ang mga bagong Curation", + "scanNewCurationFoldersDesc": "I-hanap ang mga curation na naka-dagdag pagtapos ng pagsimulang hanap", "noCurations": "Walang Curations", "id": "Curation Folder", "heading": "Pamagat", @@ -370,14 +384,14 @@ "unusedPlatform": "Ang 'Platform' ay mali. Tiyaking tama ang baybay nito!", "nonExistingLibrary": "Ang 'Library' ay mali! (Default ang gagamitin)", "nonContentFolders": "May mga folders sa Curation folder na hindi kasali. Kasama ba talaga sila doon?", - "noTags": "There are no tags on this curation.", - "noSource": "'Source' is a required field.", - "unenteredTag": "The Tags text field hasn't been submitted.", - "noLogo": "There is no logo on this curation.", - "noScreenshot": "There is no screenshot on this curation.", + "noTags": "Walang tag sa curation na ito.", + "noSource": "'Source' ang kailangan.", + "unenteredTag": "Ang Tags text field ay hindi naka-submit.", + "noLogo": "Walang logo sa curation na ito.", + "noScreenshot": "Walang screenshot sa curation na ito.", "ilc_notHttp": "Gamitin ang HTTP.", "ilc_nonExistant": "Piliin ang file sa inyong curation's 'content' folder.", - "sort": "Sort Curations (A-Z)" + "sort": "I-sort ang mga Curation (A-Z)" }, "playlist": { "enterDescriptionHere": "Maglagay ng deskripsyon dito...", @@ -413,13 +427,15 @@ "installingFiles": "Iniinstall ang mga files...", "complete": "Kumpleto na ang upgrade", "exportMetaEditTitle": "I-Export ang mga Meta Edits", - "exportMetaEditDesc": "I-select ang lahat ng mga property para I-export:" + "exportMetaEditDesc": "I-select ang lahat ng mga property para I-export:", + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Ipakita ang Thumbnail sa folder", "viewScreenshotInFolder": "Ipakita ang Screenshot sa Folder", "openFileLocation": "Buksan ang File Location", - "addToPlaylist": "Add to Playlist", + "addToPlaylist": "I-dagdag sa Playlist", "duplicateMetaOnly": "Iduplicate (Meta)", "duplicateMetaAndImages": "Iduplicate (Meta at mga Larawan)", "copyGameUUID": "I-kopya ang Game UUID", @@ -459,8 +475,8 @@ "restartNow": "I-restart ngayon?", "restartToApplyUpgrade": "Ang upgrade na ito ay hindi mai-install hanggang sa mag-restart ka.\nNais mo bang gawin ito ngayon?", "areYouSure": "Sigurado ka ba?", - "areYouSureDelete": "Are you sure you want to delete this entry?", - "areYouSurePlaylistRemove": "Are you sure you want to remove this entry from the playlist?", + "areYouSureDelete": "Sigurado kang gusto mong I-delete ang entry na ito?", + "areYouSurePlaylistRemove": "Sigurado kang gusto mong I-delete ang entry sa playlist na ito?", "cancel": "Kanselahin", "mergePlaylists": "I-merge ang mga playlist", "newPlaylist": "Stay Separate", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Laro", + "arcadeSingular": "Game", "arcadePlural": "Lahat ng Laro", "theatre": "Animasyon", + "theatreSingular": "Animation", "theatrePlural": "Lahat ng Animasyon", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Lahat ng NG Animasyon" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logo at Screenshot", "screenshotsDesc": "Dagdag ng logo para sa Grid view at screenshot para sa lahat ng laro." } -} \ No newline at end of file +} diff --git a/lang/fr-FR.json b/lang/fr-FR.json index 3a3ee5835..503e70c0d 100644 --- a/lang/fr-FR.json +++ b/lang/fr-FR.json @@ -2,63 +2,72 @@ "name": "Français", "config": { "configHeader": "Configuration", - "configDesc": "(Tu dois appuyer sur 'Sauvegarder & redémarrer' pour que certains changements s'appliquent)", + "configDesc": "(Vous devez appuyer sur 'Sauvegarder & redémarrer' pour que certains changements s'appliquent)", "preferencesHeader": "Préférences", - "extremeGames": "Contenu Extrême", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Afficher les filtres extrêmes", + "extremeGamesDesc": "Permet de basculer, de créer et de modifier des filtres de balises Extrême pour contrôler le contenu inadapté aux enfants.", + "hideExtremeScreenshots": "Cacher les captures d'écran extrêmes", + "hideExtremeScreenshotsDesc": "Cache les captures d'écran de contenu taguées Extrême, peuvent être réaffichées en cliquant sur la zone de l'image.", + "fancyAnimations": "Animations de qualité supérieure", + "fancyAnimationsDesc": "Activer les animations de qualité supérieure dans le lanceur.", + "searchLimit": "Limite de recherche", + "searchLimitDesc": "Limiter le nombre de résultats retournés dans n'importe quelle recherche", + "searchLimitUnlimited": "Illimitée", + "searchLimitValue": "{0} résultats", "enableEditing": "Activer le mode édition", - "enableEditingDesc": "Active l'édition des jeux et des applications supplémentaires. Affiche également les onglets liés à l'édition.", + "enableEditingDesc": "Active l'édition des jeux et des applications additionnelles. Affiche également les onglets liés à l'édition.", "onDemandImages": "Images sur demande", "onDemandImagesDesc": "Télécharge et sauvegarde les logos et captures d'écran manquants quand nécessaire.", "currentLanguage": "Langue", - "currentLanguageDesc": "La langue qui sera principalement utilisée par le lanceur.", - "fallbackLanguage": "Langue de secours", - "fallbackLanguageDesc": "Langue à utiliser à la place d'une langue actuelle incomplète.", + "currentLanguageDesc": "Langue qui sera principalement utilisée par le lanceur.", + "fallbackLanguage": "Langue de repli", + "fallbackLanguageDesc": "Langue qui sera utilisée à la place d'une langue actuelle incomplète.", "auto": "Auto ({0})", "none": "Aucune", + "contentFiltersHeader": "Filtres de contenu", "flashpointHeader": "Flashpoint", - "flashpointPath": "Chemin de Flashpoint", + "flashpointPath": "Chemin Flashpoint", "flashpointPathDesc": "Chemin vers le dossier de Flashpoint (peut être relatif)", "browse": "Parcourir", "libraries": "Bibliothèques", "randomLibraries": "Bibliothèques aléatoires", "randomLibrariesDesc": "Bibliothèques à utiliser pour les sélections aléatoires sur la page d'accueil.", - "updateSource": "Update Source", + "updateSource": "Actualiser la source", "platforms": "Plateformes", "nativePlatforms": "Plateformes natives", "nativePlatformsDesc": "Utilisez des versions natives de ces plateformes. (Si non disponibles, Wine sera utilisé)", - "tagFilterGroups": "Groupes de filtres de balises", - "tagFilterGroupsDesc": "Ces groupes filtreront les suggestions et les jeux en dehors des résultats de recherche", - "editTagFilter": "Modifier le groupe de filtres de balises", - "duplicateTagFilter": "Dupliquer le groupe de filtres de balises", - "deleteTagFilter": "Supprimer le groupe de filtres de balises", - "appPathOverrides": "Remplacements du chemin de l'application", - "appPathOverridesDesc": "Remplace le chemin de l'application à gauche avec celui de droite lors du lancement de jeux.", + "tagFilterGroups": "Groupes de filtres de tags", + "tagFilterGroupsDesc": "Ces groupes filtreront les suggestions et les jeux des résultats de recherche", + "editTagFilter": "Modifier le groupe de filtres de tags", + "duplicateTagFilter": "Dupliquer le groupe de filtres de tags", + "deleteTagFilter": "Supprimer le groupe de filtres de tags", + "appPathOverrides": "Remplacements du chemin d'application", + "appPathOverridesDesc": "Remplace le chemin d'application à gauche avec celui de droite lors du lancement de jeux.", "visualsHeader": "Visuels", "useCustomTitleBar": "Utiliser une barre de titre personnalisée", "useCustomTitleBarDesc": "Utilise une barre de titre personnalisée en haut de cette fenêtre.", "theme": "Thème", "noTheme": "Aucun thème", "themeDesc": "ID du thème à utiliser.", - "logoSet": "Ensemble de logos", - "noLogoSet": "Aucun ensemble de logos défini", - "logoSetDesc": "ID de l'ensemble de logos à utiliser.", + "logoSet": "Set de logos", + "noLogoSet": "Aucun set de logos défini", + "logoSetDesc": "ID du set de logos à utiliser.", "advancedHeader": "Avancé", "showDeveloperTab": "Afficher l'onglet Développeur", - "showDeveloperTabDesc": "Affiche l'onglet 'Développeur'. Ceci n'est probablement utile que pour les développeurs et les curateurs.", + "showDeveloperTabDesc": "Affiche l'onglet 'Développeur'. Ceci n'est probablement utile que pour les développeurs et les conservateurs.", "server": "Serveur", "serverDesc": "Processus serveur à exécuter au démarrage du lanceur.", "metadataServerHost": "Hôte du serveur de métadonnées", - "metadataServerHostDesc": "Hôte d'un serveur de métadonnées. Utilisé pour synchroniser les mises à jours et nouvelles informations de jeu avec le lanceur.", + "metadataServerHostDesc": "Hôte d'un serveur de métadonnées. Utilisé pour synchroniser des infos nouvelles et à jour au lanceur.", "extensionsHeader": "Extensions", "noExtensionsLoaded": "Pas d'extensions chargées, Installez-les dans '{0}'", "extDevScripts": "Script(s) de développeur", "extThemes": "Thème(s)", - "extLogoSets": "Ensemble(s) de logos", + "extLogoSets": "Set(s) de logos", "extApplications": "Application(s)", "saveAndRestart": "Sauvegarder et redémarrer", - "saveAndClose": "Save and Close", - "tagFilterGroupEditor": "Tag Filter Group Editor" + "saveAndClose": "Sauvegarder et fermer", + "tagFilterGroupEditor": "Éditeur de groupe de filtre de tags" }, "home": { "updateHeader": "Mise à jour du lanceur", @@ -68,29 +77,30 @@ "upToDate": "À jour.", "downloadingUpdate": "Téléchargement de la mise à jour...", "quickStartHeader": "Démarrage rapide", - "hallOfFameInfo": "Tu veux seulement le meilleur du meilleur ? Essaye le {0}!", + "hallOfFameInfo": "Vous voulez seulement le meilleur du meilleur ? Essaye le {0}!", "hallOfFame": "Temple de la renommée", - "allGamesInfo": "Tu cherches quelque chose à jouer ? Viens voir {0}.", + "allGamesInfo": "Vous cherchez quelque chose à jouer ? Viens voir {0}.", "allGames": "Tous les jeux", - "allAnimationsInfo": "Tu veux juste quelque chose à regarder ? Viens voir {0}.", + "allAnimationsInfo": "Vous voulez juste quelque chose à regarder ? Viens voir {0}.", "allAnimations": "Toutes les animations", - "configInfo": "Tu veux changer quelque chose ? Va à {0}.", + "configInfo": "Vous voulez changer quelque chose ? Va à {0}.", "config": "Configuration", "helpInfo": "Besoin d'aide ? {0}.", "help": "Lire le manuel", "upgradesHeader": "Mises à niveau", + "updateFeedHeader": "Fil d'actualité", "installComplete": "Installation terminée", "alreadyInstalled": "Déjà installée", "download": "Télécharger", "update": "Mise à jour", "checkingUpgradeState": "Vérification de l'état de la mise à niveau...", - "extrasHeader": "Suppléments", - "favoritesPlaylist": "Liste des favoris", + "extrasHeader": "Extras", + "favoritesPlaylist": "Playlist des favoris", "tagList": "Liste de tags", "filterByPlatform": "Filtrer par plateforme", "plannedFeatures": "Découvre nos fonctionnalités à venir !", "notesHeader": "Notes", - "notes": "Pense à lire le manuel si tu rencontres des problèmes.", + "notes": "Pensez à lire le manuel si vous rencontrez des problèmes.", "linuxSupport": "Problèmes sur Linux ? {0}", "linuxSupportLinkText": "Ouvrir la FAQ", "randomPicks": "Choix aléatoires", @@ -98,12 +108,13 @@ }, "logs": { "filters": "Filtres", - "logLevels": "Niveaux de journalisation", + "logLevels": "Niveaux d'historique", "copyText": "Copier le texte", "clearLog": "Effacer l'historique", "copy404Urls": "Copier les URL 404", "uploadLog": "Télécharger l'historique", - "copiedToClipboard": "Copié dans le presse-papier" + "copiedToClipboard": "Copié dans le presse-papier", + "openLogsWindow": "Ouvrir la fenêtre de l'historique" }, "app": { "home": "Accueil", @@ -113,7 +124,7 @@ "logs": "Historique", "config": "Configuration", "about": "À propos", - "curate": "Curateurs", + "curate": "Conserver", "developer": "Développeur", "searchPlaceholder": "Rechercher...", "hideRightSidebar": "Cacher la barre latérale droite", @@ -130,7 +141,7 @@ "dateAdded": "Date d'ajout", "dateModified": "Date de modification", "platform": "Plateforme", - "series": "Séries", + "series": "Série", "title": "Titre", "developer": "Développeur", "publisher": "Éditeur", @@ -141,50 +152,50 @@ "developerHeader": "Développeur", "developerDesc": "C'est ici que tous les outils de développement utiles iront.", "checkMissingImages": "Vérifier les images manquantes", - "checkMissingImagesDesc": "Lister tous les jeux sans miniature ni capture d'écran", - "checkGameIds": "Vérifier les identifiants des jeux", - "checkGameIdsDesc": "Lister tous les jeux avec des identifiants en double ou invalides", + "checkMissingImagesDesc": "Liste tous les jeux sans miniature ni capture d'écran", + "checkGameIds": "Vérifier les IDs des jeux", + "checkGameIdsDesc": "Liste tous les jeux avec des IDs en double ou invalides", "checkGameTitles": "Vérifier les titres des jeux", - "checkGameTitlesDesc": "Lister tous les jeux avec des titres en double sur la même plateforme", + "checkGameTitlesDesc": "Liste tous les jeux avec des titres en double sur la même plateforme", "checkGameFields": "Vérifier les informations des jeux", - "checkGameFieldsDesc": "Lister tous les jeux avec des champs vides (ceux qui ne doivent pas être vides)", - "checkPlaylists": "Vérifier les listes de lecture", - "checkPlaylistsDesc": "Lister toutes les listes de lecture avec des identifiants en double ou invalides, ou comprenant des jeux avec des identifiants manquants, invalides ou en double", + "checkGameFieldsDesc": "Liste tous les jeux avec des champs vides (ceux qui ne devraient pas être vides)", + "checkPlaylists": "Vérifier les playlists", + "checkPlaylistsDesc": "Liste toutes les playlists avec des IDs en double ou invalides, ou comprenant des jeux avec des IDs manquants, invalides ou en double", "checkGameFileLocation": "Vérifier les emplacements des fichiers de jeu", - "checkGameFileLocationDesc": "Lister tous les jeux avec des commandes au lancement qui ne peuvent pas être analysées en chemins de fichiers (ceci est dû à la fonction 'Ouvrir l'emplacement du fichier', qui n'arrive pas à lancer le jeu)", + "checkGameFileLocationDesc": "Liste tous les jeux avec des commandes de lancement qui ne peuvent pas être analysées en chemins de fichiers (ceci est dû à la fonction 'Ouvrir l'emplacement du fichier', qui n'arrive pas à lancer le jeu)", "checkMissingExecMappings": "Vérifier les mappage d'exécutables manquants", - "checkMissingExecMappingsDesc": "Lister tous les exécutables uniques pour win32, linux et darwin auquels manquent un mappage d'exécutable", - "renameImagesTitleToId": "Renommer les images (Titre => Identifiant)", - "renameImagesTitleToIdDesc": "Trouver toutes les images de jeu qui ont le titre du jeu dans leur nom de fichier et les renomme afin qu'elles utilisent leur identifiant à la place", - "renameImagesIdToTitle": "Renommer les images (Identifiant => Titre)", - "renameImagesIdToTitleDesc": "Trouver toutes les images de jeu qui ont l'identifiant du jeu dans leur nom de fichier et les renomme afin qu'elles utilisent son titre à la place", + "checkMissingExecMappingsDesc": "Liste tous les exécutables uniques pour win32, linux et darwin auquel manquent un mappage d'exécutable", + "renameImagesTitleToId": "Renommer les images (Titre => ID)", + "renameImagesTitleToIdDesc": "Trouve toutes les images de jeu qui ont le titre du jeu dans leur nom de fichier et les renomme afin qu'elles utilisent leur ID à la place", + "renameImagesIdToTitle": "Renommer les images (ID => Titre)", + "renameImagesIdToTitleDesc": "Trouve toutes les images de jeu qui ont l'ID du jeu dans leur nom de fichier et les renomme afin qu'elles utilisent son titre à la place", "createMissingFolders": "Créer les dossiers manquants", - "createMissingFoldersDesc": "Trouver tous les dossiers manquants dans la structure du dossier de Flashpoint et les crée", - "importLegacyPlatforms": "Importe les vieilles plateformes", - "importLegacyPlatformsDesc": "Importe les anciennes plateformes de 'Data/Platforms/' - ATTENTION : Cela va écraser les jeux déjà existant", - "importLegacyPlaylists": "Importer les anciennes listes", - "importLegacyPlaylistsDesc": "Importer les anciennes listes de '/Data/Playlist'", - "deleteAllPlaylists": "Supprimer toutes les listes de lecture", - "deleteAllPlaylistsDesc": "DANGEREUX! Supprime toutes les listes de lecture.", - "importMetaEdits": "Importer les modifications de méta", - "importMetaEditsDesc": "Importer toutes les modification de méta de '/Data/MetaEdits'", - "fixPrimaryAliases": "Corriger les alias primaires", - "fixPrimaryAliasesDesc": "Ajouter un alias primaire aux tags qui n'en ont pas", - "fixCommaTags": "Corriger les tags avec des virgules", + "createMissingFoldersDesc": "Trouve tous les dossiers manquants dans la structure du dossier de Flashpoint et les crée", + "importLegacyPlatforms": "Importer les vieilles plateformes", + "importLegacyPlatformsDesc": "Importe les anciennes plateformes de 'Data/Platforms/' - ATTENTION : Ceci va écraser les jeux en conflit", + "importLegacyPlaylists": "Importer les anciennes playlists", + "importLegacyPlaylistsDesc": "Importe les anciennes playlists de '/Data/Playlist'", + "deleteAllPlaylists": "Supprimer toutes les playlists", + "deleteAllPlaylistsDesc": "ATTENTION ! Supprime toutes les playlists.", + "importMetaEdits": "Importer les modifications de meta", + "importMetaEditsDesc": "Importe toutes les modifications de meta de '/Data/MetaEdits'", + "fixPrimaryAliases": "Fixer les alias principaux", + "fixPrimaryAliasesDesc": "Ajoute un alias principal aux tags qui n'en ont pas", + "fixCommaTags": "Fixer les tags avec virgules", "fixCommaTagsDesc": "Divise les tags avec des virgules en plusieurs tags et les applique à tous les jeux applicables", "exportTags": "Exporter les tags", "exportTagsDesc": "Crée un fichier plein de tags et de catégories de tags pouvant être utilisés par 'Importer des tags'", - "importTags": "Importer des tags", + "importTags": "Importer les tags", "importTagsDesc": "Importe les tags à partir d'un fichier créé par 'Exporter les tags'", - "migrateExtremeGames": "Migrate Extreme Games", - "migrateExtremeGamesDesc": "Removes 'Extreme' from any games and assigns the 'LEGACY-Extreme' tag to them", - "massImportGameData": "Mass Import Game Data", - "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", + "migrateExtremeGames": "Migrer les jeux Extrêmes", + "migrateExtremeGamesDesc": "Supprime 'Extrême' de n'importe quel jeu et leur assigne le tag 'LEGACY-Extreme'", + "massImportGameData": "Importer en masse des données de jeu", + "massImportGameDataDesc": "Importe en masse des packs de données de jeu, en essayant de matcher leur nom de fichier à un UUID de jeu.", "servicesHeader": "Services de fond", - "servicesMissing": "Il n'y a aucun service.", + "servicesMissing": "Il n'y a aucun services.", "running": "En cours", "stopped": "Arrêté", - "killing": "Fermeture", + "killing": "Tuer", "start": "Démarrer", "startDesc": "Démarre le service", "stop": "Arrêter", @@ -196,8 +207,8 @@ }, "about": { "aboutHeader": "À propos", - "flashpoint": "BlueMaxima's Flashpoint", - "flashpointDesc": "Le but de ce projet est d'être une archive, un musée et une collection jouable de jeux jouables sur Internet.", + "flashpoint": "Flashpoint de Bluemaxima", + "flashpointDesc": "Le but de ce projet de préservation est d'être une archive, un musée et une collection jouable de jeux d'Internet.", "website": "Site internet", "flashpointLauncher": "Lanceur Flashpoint", "flashpointLauncherDesc": "Une application de bureau open-source permettant de parcourir, gérer et jouer à des jeux provenant du projet Flashpoint.", @@ -205,90 +216,93 @@ "license": "Licence", "licenseInfo": "MIT (Lisez le fichier 'LICENSE' pour plus d'informations)", "creditsHeader": "Crédits", - "specialThanks": "Remerciement particuliers" + "specialThanks": "Remerciement spéciaux" }, "browse": { - "noTitle": "Pas de titre", + "noTitle": "Aucun titre", "by": "par", "play": "Jouer", "stop": "Arrêter", - "noDeveloper": "Pas de développeur", + "noDeveloper": "Aucun développeur", "alternateTitles": "Titres alternatifs", - "noAlternateTitles": "Il n'y a pas d'autres titre", + "noAlternateTitles": "Aucun titres alternatif", "tags": "Tags", - "noTags": "Aucuns tags", - "enterTag": "Ajouter tag", + "noTags": "Aucun tags", + "enterTag": "Ajouter un tag", "series": "Série", - "noSeries": "Pas de série", + "noSeries": "Aucune série", "publisher": "Éditeur", - "noPublisher": "Pas d'éditeur", + "noPublisher": "Aucun éditeur", "source": "Source", - "noSource": "Pas de source", + "noSource": "Aucune source", "platform": "Plateforme", - "noPlatform": "Pas de plateforme", + "noPlatform": "Aucune plateforme", "playMode": "Mode de jeu", - "noPlayMode": "Pas de mode de jeu", + "noPlayMode": "Aucun mode de jeu", "status": "Statut", - "noStatus": "Pas de statut", + "noStatus": "Aucun statut", "version": "Version", - "noVersion": "Pas de version", + "noVersion": "Aucune version", "releaseDate": "Date de publication", - "noReleaseDate": "Pas de date de publication", + "noReleaseDate": "Aucune date de publication", "language": "Langue", - "noLanguage": "Pas de langue", + "noLanguage": "Aucune langue", "dateAdded": "Date d'ajout", "dateModified": "Date de modification", "brokenInInfinity": "Cassé (dans Infinity)", "extreme": "Extrême", - "playlistNotes": "Notes de listes", - "noPlaylistNotes": "Pas de notes de liste", + "playlistNotes": "Notes de playlist", + "noPlaylistNotes": "Aucune notes de playlist", "notes": "Notes", - "noNotes": "Pas de notes", + "noNotes": "Aucune notes", "originalDescription": "Description originelle", - "noOriginalDescription": "Pas de description originelle", - "additionalApplications": "Applications supplémentaires", - "noName": "Pas de nom", + "noOriginalDescription": "Aucune description originelle", + "additionalApplications": "Applications additionnelles", + "noName": "Aucun nom", "launch": "Lancer", "autoRunBefore": "Exécuter automatiquement avant", "waitForExit": "Attendre d'avoir quitté", "applicationPath": "Chemin d'application", - "noApplicationPath": "Pas de chemin d'application", + "noApplicationPath": "Aucun chemin d'application", "launchCommand": "Commande de lancement", - "noLaunchCommand": "Pas de commande de lancement", + "noLaunchCommand": "Aucune commande de lancement", "searching": "Recherche en cours...", "library": "Bibliothèque", "defaultLibrary": "Bibliothèque par défaut", - "uninstallGame": "Uninstall Game", + "uninstallGame": "Désinstaller le jeu", "installed": "Installé", - "notInstalled": "Pas installé", + "notInstalled": "Non installé", "legacyGame": "Ancien jeu", "download": "Télécharger", "thumbnail": "Miniature", "screenshot": "Capture d'écran", "dropImageHere": "Déposer une image ici pour l'ajouter", - "noGameSelected": "Aucun jeu sélectionné", + "noGameSelected": "Aucun {0} sélectionné", "clickToSelectGame": "Cliquer sur un jeu pour le sélectionner.", - "deleteAdditionalApplication": "Supprimer l'application supplémentaire", - "deleteGameAndAdditionalApps": "Supprimer le jeu (et les applications supplémentaires)", - "removeGameFromPlaylist": "Retirer le jeu de la liste de lecture", - "saveChanges": "Enregistrer les modifications", - "discardChanges": "Annuler les modifications", - "editGame": "Modifier le jeu", + "deleteAdditionalApplication": "Supprimer l'application additionnelle", + "deleteGameAndAdditionalApps": "Supprimer le jeu (et les applications additionnelles)", + "removeGameFromPlaylist": "Supprimer le jeu de la playlist", + "saveChanges": "Sauvegarder les changements", + "discardChanges": "Annuler les changements", + "editGame": "Éditer le jeu", "allGames": "Tous les jeux", - "newPlaylist": "Nouvelle liste de lecture", - "importPlaylist": "Importer la liste de lecture", - "emptyPlaylist": "Liste de lecture vide", + "newPlaylist": "Nouvelle playlist", + "importPlaylist": "Importer playlist", + "emptyPlaylist": "Playlist vide", "noGamesFound": "Aucun jeu trouvé !", - "dropGameOnLeft": "Déposer un jeu sur cette liste de lecture dans la {0} pour l'ajouter.", + "dropGameOnLeft": "Déposer un jeu sur cette playlist dans la {0} pour l'ajouter.", "leftSidebar": "barre latérale gauche", - "setFlashpointPathQuestion": "As-tu défini le chemin vers le {0} à la page {1}?", - "flashpointPath": "chemin de Flashpoint", + "setFlashpointPathQuestion": "Avez-vous défini le chemin vers le {0} à la page {1}?", + "flashpointPath": "Chemin Flashpoint", "config": "Configuration", - "noteSaveAndRestart": "Note : Tu dois appuyer sur {0} afin que la modification prenne effet.", + "noteSaveAndRestart": "Note : Vous devez appuyer sur {0} afin que le changement prenne effet.", "saveAndRestart": "Sauvegarder & redémarrer", - "thereAreNoGames": "Il n'y a aucun jeu.", - "noGameMatchedDesc": "Aucun titre de jeu ne correspond à ta recherche.", - "noGameMatchedSearch": "Essaie de chercher quelque chose de moins restrictif." + "thereAreNoGames": "Il n'y a aucun jeux.", + "noGameMatchedDesc": "Aucun titre de jeu correspond à votre recherche.", + "noGameMatchedSearch": "Essayez de chercher quelque chose moins restrictivement.", + "mountParameters": "Paramètres de montage", + "noMountParameters": "Aucun paramètre de montage", + "showExtremeScreenshot": "Afficher la capture d'écran extrême" }, "tags": { "name": "Nom", @@ -300,105 +314,105 @@ "newCategory": "Nouvelle catégorie", "enterAlias": "Entrer l'alias", "aliases": "Alias", - "editTag": "Modifier le tag", + "editTag": "Éditer le tag", "color": "Couleur", - "noTagSelected": "Pas de tag sélectionné", - "clickToSelectTag": "Cliquer pour sélectionner un filtre", + "noTagSelected": "Aucun tag sélectionné", + "clickToSelectTag": "Cliquer pour sélectionner un tag", "deleteTagAlias": "Supprimer l'alias du tag", "setPrimaryAlias": "Définir l'alias principal", - "mergeIntoTag": "Fusionner en filtre", + "mergeIntoTag": "Fusionner en tag", "mergeTag": "Fusionner les tags", - "makeAliasWhenMerged": "Faire de ce tag un alias ?", + "makeAliasWhenMerged": "Faire un alias de ce tag ?", "deleteTag": "Supprimer le tag", - "deleteTagCategory": "Supprimer la catégorie \"Tag\"", + "deleteTagCategory": "Supprimer la catégorie tag", "locked": "Protégé pendant le traitement..." }, "curate": { "importAll": "Tout importer", - "importAllDesc": "Importe toutes les curations actuellement chargés", - "deleteAll": "Tout effacer", - "deleteAllDesc": "Effacer toutes les curations déjà chargées", - "saveImportedCurations": "Enregistrer les curations importées", + "importAllDesc": "Importe toutes les conservations actuellement chargées", + "deleteAll": "Tout supprimer", + "deleteAllDesc": "Supprime toutes les conservations actuellement chargées", + "saveImportedCurations": "Sauvegarder les conservations importées", "keepArchiveKey": "Garder l'UUID de l'archive de curation", "symlinkCurationContent": "Lier symboliquement le dossier de contenu de curation (Nécessite les droits d'administrateur sous Windows, requis pour MAD4FP)", "useTagFilters": "Utiliser les filtres de tags dans les suggestions", - "openCurationsFolder": "Dossier de curations", - "openCurationsFolderDesc": "Ouvrir le dossier de curations dans l'explorateur", - "openExportsFolder": "Curations exportées", - "openExportsFolderDesc": "Ouvrir le dossier par défaut des curations exportées.", - "openImportedFolder": "Curations importées", - "openImportedFolderDesc": "Ouvrir le dossier par défaut des curations importées.", - "newCuration": "Nouvelle curation", - "newCurationDesc": "Crée une nouvelle curation avec un dossier unique", - "loadMeta": "Charger les métadonnées", - "loadMetaDesc": "Charge un ou plusieurs meta-fichiers de conservation", + "openCurationsFolder": "Dossier de conservations", + "openCurationsFolderDesc": "Ouvre le dossier de conservations dans l'explorateur", + "openExportsFolder": "Conservations exportées", + "openExportsFolderDesc": "Ouvre le dossier par défaut des conservations exportées.", + "openImportedFolder": "Conservations importées", + "openImportedFolderDesc": "Ouvre le dossier par défaut des conservations importées.", + "newCuration": "Nouvelle conservation", + "newCurationDesc": "Crée une nouvelle conservation avec un dossier unique", + "loadMeta": "Charger un fichier meta", + "loadMetaDesc": "Charge un ou plusieurs fichiers meta de conservation", "loadArchive": "Charger une archive", - "loadArchiveDesc": "Charge une ou plusieurs archives de curations", + "loadArchiveDesc": "Charge une ou plusieurs archives de conservations", "loadFolder": "Charger un dossier", - "loadFolderDesc": "Charge un ou plusieurs dossiers de curations", - "scanNewCurationFolders": "Scan For New Curations", - "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", - "noCurations": "Aucune curation", - "id": "Dossier de curations", + "loadFolderDesc": "Charge un ou plusieurs dossiers de conservations", + "scanNewCurationFolders": "Scan nouvelles conservations", + "scanNewCurationFoldersDesc": "Scanne pour des conservations qui ont été ajoutées après l'analyse initiale", + "noCurations": "Aucune conservations", + "id": "Dossier de conservations", "heading": "En-tête", - "noHeading": "Pas d'en-tête", - "folderName": "Dossier des suppléments", - "noFolderName": "Pas de nom de dossier", + "noHeading": "Aucun en-tête", + "folderName": "Dossier des Extras", + "noFolderName": "Aucun nom de dossier", "message": "Message de lancement", "noMessage": "Aucun message", - "curationNotes": "Notes de curation", - "noCurationNotes": "Pas de notes de curation", + "curationNotes": "Notes de conservation", + "noCurationNotes": "Aucune notes de conservation", "newAddApp": "Nouvelle application", - "addExtras": "Ajouter des suppléments", + "addExtras": "Ajouter des Extras", "addMessage": "Ajouter un message", - "removeAddApp": "Enlever l'application", - "indexContent": "Actualiser le contenu", + "removeAddApp": "Supprimer l'application", + "indexContent": "Actualiser Content", "delete": "Effacer", - "deleteCurationDesc": "Effacer cette curation (les fichiers ne peuvent pas être récupérés)", - "openFolder": "Ouvrir le dossier", - "run": "Exécuter", - "runWithMAD4FP": "Lancer avec MAD4FP", + "deleteCurationDesc": "Supprime cette conservation (les fichiers ne peuvent pas être récupérés)", + "openFolder": "Ouvrir dossier", + "run": "Lancer", + "runWithMAD4FP": "Lancer (MAD4FP)", "export": "Exporter", "import": "Importer", - "contentFiles": "Fichiers de contenu", + "contentFiles": "Fichiers Content", "warnings": "Avertissements", - "noLaunchCommand": "Le champ «Commande au lancement» est obligatoire. Merci de le remplir avant d'importer.", - "invalidLaunchCommand": "Le champ «Commande au lancement» ne remplit pas les prérequis suivants :", - "releaseDateInvalid": "La date de sortie doit suivre AAAA-MM-JJ (le mois et le jour sont facultatifs).", - "unusedApplicationPath": "Le chemin d'application contient une valeur inutilisée. Assure-toi qu'il est correctement orthographié!", + "noLaunchCommand": "Le champ «Commande de lancement» est obligatoire. Remplissez le avant d'importer.", + "invalidLaunchCommand": "Le champ «Commande de lancement» ne remplit pas les prérequis URL suivants :", + "releaseDateInvalid": "La 'Date de sortie' doit suivre AAAA-MM-JJ (le mois et le jour sont optionnels).", + "unusedApplicationPath": "'Chemin d'application' contient une valeur inutilisée. Assure-toi qu'elle est correctement orthographiée !", "unusedTags": "Les tags suivants ne sont présents sur aucun autre jeu. Sont-ils corrects ?", "unusedPlatform": "Le champ 'Plateforme' contient une valeur inutilisée. Assure-toi qu'elle est orthographiée correctement !", - "nonExistingLibrary": "Le champ 'Bibliothèque' ne correspond à aucune bibliothèque existante ! (La bibliothèque par défaut sera utilisée)", - "nonContentFolders": "D'autres types de dossier été trouvés dans le dossier des curations. Sont-ils censés y être ?", - "noTags": "There are no tags on this curation.", - "noSource": "'Source' is a required field.", - "unenteredTag": "The Tags text field hasn't been submitted.", - "noLogo": "There is no logo on this curation.", - "noScreenshot": "There is no screenshot on this curation.", - "ilc_notHttp": "Utiliser HTTP.", - "ilc_nonExistant": "Indique un fichier existant dans ton dossier de curations.", - "sort": "Sort Curations (A-Z)" + "nonExistingLibrary": "Le champ 'Bibliothèque' ne correspond à aucune bibliothèque existante ! (Celle par défaut sera utilisée)", + "nonContentFolders": "D'autres dossiers que 'Content' ont été trouvés dans le dossier de la conservation. Sont-ils censés y être ?", + "noTags": "Il n'y a aucun tags sur cette conservation.", + "noSource": "'Source' est un champ obligatoire.", + "unenteredTag": "Le champ Tags n'a pas été soumis.", + "noLogo": "Il n'y a aucun logo sur cette conservation.", + "noScreenshot": "Il n'y a aucune capture d'écran sur cette conservation.", + "ilc_notHttp": "Utilise HTTP.", + "ilc_nonExistant": "Pointe un fichier existant dans ton dossier 'Content' de conservation.", + "sort": "Trier les conservations (A-Z)" }, "playlist": { "enterDescriptionHere": "Ajouter une description ici...", - "noDescription": "Pas de description", - "save": "Enregistrer", - "saveDesc": "Enregistrer les modifications apportées et arrêter l'édition", + "noDescription": "Aucune description", + "save": "Sauvegarder", + "saveDesc": "Sauvegarder les changements apportés et arrêter l'édition", "discard": "Annuler", - "discardDesc": "Annuler les modifications apportées et arrêter l'édition", - "edit": "Modifier", - "editDesc": "Modifier cette liste de lecture", + "discardDesc": "Annuler les changements apportés et arrêter l'édition", + "edit": "Éditer", + "editDesc": "Éditer cette playlist", "delete": "Supprimer", - "deleteDesc": "Supprimer cette liste de lecture", - "changeIcon": "Changer l'icône de la liste de lecture", - "duplicatePlaylistDesc": "Dupliquer la liste de lecture", - "exportPlaylistDesc": "Exporter la liste de lecture vers un fichier", - "areYouSure": "Es-tu sûr(e) ?", - "noTitle": "Pas de titre", + "deleteDesc": "Supprimer cette playlist", + "changeIcon": "Changer l'icône de la playlist", + "duplicatePlaylistDesc": "Dupliquer la playlist", + "exportPlaylistDesc": "Exporter la playlist vers un fichier", + "areYouSure": "Êtes-vous sûr(e) ?", + "noTitle": "Aucun titre", "titlePlaceholder": "Titre...", - "noAuthor": "Pas d'auteur(e)", + "noAuthor": "Aucun auteur(e)", "authorPlaceholder": "Auteur(e)...", - "id": "Identifiant", + "id": "ID", "by": "par", "extreme": "extrême" }, @@ -412,90 +426,95 @@ "extracting": "Extraction en cours", "installingFiles": "Installation des fichiers...", "complete": "Fini", - "exportMetaEditTitle": "Exporter les meta-modifications", - "exportMetaEditDesc": "Sélectionner toutes les propriétés à exporter :" + "exportMetaEditTitle": "Exporter les modifications de meta", + "exportMetaEditDesc": "Sélectionner toutes les propriétés à exporter :", + "showImage": "Afficher l'image", + "searching": "Recherche en cours..." }, "menu": { "viewThumbnailInFolder": "Voir la miniature dans le dossier", "viewScreenshotInFolder": "Voir la capture d'écran dans le dossier", "openFileLocation": "Ouvrir l'emplacement du fichier", - "addToPlaylist": "Ajouter à la liste de lecture", - "duplicateMetaOnly": "Dupliquer (seulement les meta-données)", - "duplicateMetaAndImages": "Dupliquer (meta-données et images)", + "addToPlaylist": "Ajouter à la playlist", + "duplicateMetaOnly": "Dupliquer (meta seulement)", + "duplicateMetaAndImages": "Dupliquer (meta et images)", "copyGameUUID": "Copier l'UUID du jeu", - "exportMetaOnly": "Exporter (seulement les meta-données)", - "exportMetaAndImages": "Exporter (meta-données et images)", - "exportMetaEdit": "Exporter les modifications de meta-données", - "duplicatePlaylist": "Dupliquer la liste de lecture", - "exportPlaylist": "Exporter la liste de lecture" + "exportMetaOnly": "Exporter (meta seulement)", + "exportMetaAndImages": "Exporter (meta et images)", + "exportMetaEdit": "Exporter les modifications de meta", + "duplicatePlaylist": "Dupliquer la playlist", + "exportPlaylist": "Exporter la playlist" }, "dialog": { - "programNotFound": "Programme introuvable !", + "programNotFound": "Programme non trouvé !", "phpNotFound": "PHP n'a pas été trouvé. Est-il installé ?\nVous pouvez rencontrer des comportements inattendus et des erreurs sans lui.", "wineNotFound": "Wine n'est pas installé, mais est requis pour exécuter ce jeu.\nMerci de consulter l'article wiki du support Linux pour plus de détails.", - "flashpointPathNotFound": "Le dossier Flashpoint n'est pas défini ou n'est pas valide. Veux-tu choisir un dossier pour les installations futures ?", - "fileNotFound": "Fichier introuvable !", - "flashpointPathInvalid": "Chemin de Flashpoint non valide !", - "pathNotFound": "Chemin introuvable !", - "playlistConflict": "Conflit de liste de lecture", + "flashpointPathNotFound": "Le dossier Flashpoint n'est pas défini ou invalide. Voulez-vous choisir un dossier pour les installations futures ?", + "fileNotFound": "Fichier non trouvé !", + "flashpointPathInvalid": "Chemin Flashpoint invalide !", + "pathNotFound": "Chemin non trouvé !", + "playlistConflict": "Conflit de playlist", "importedPlaylistAlreadyExists": "La playlist importée existe déjà avec le même nom ! - '{0}'", - "importPlaylistAs": "Importer une liste de lecture en tant que", - "selectFileToExportMeta": "Sélectionnez le fichier où sauvegarder les méta-données", - "selectFolderToExportMetaAndImages": "Sélectionnez le dossier où sauvegarder les méta-données et les images", + "importPlaylistAs": "Importer une playlist en tant que", + "selectFileToExportMeta": "Sélectionnez le fichier où sauvegarder la meta", + "selectFolderToExportMetaAndImages": "Sélectionnez le dossier où sauvegarder la meta et les images", "replaceFilesQuestion": "Remplacer les fichiers ?", - "exportedAlreadyExistsYesNo": "Un ou plusieurs des fichiers exportés existent déjà.\n Veux-tu les remplacer ?", - "selectFolder": "Sélectionner le dossier", - "selectScreenshot": "Sélectionner une capture d'écran", - "selectThumbnail": "Sélectionner une miniature", - "selectCurationFolder": "Sélectionner un ou plusieurs dossier(s) de curation", - "selectCurationArchive": "Sélectionner une ou plusieurs archive(s) de curation", - "selectCurationMeta": "Sélectionner une ou plusieurs méta-donnée(s) de curation", - "selectPlaylistToImport": "Sélectionner la liste de lecture à importer", - "selectFileToExportPlaylist": "Sélectionner le fichier où sauvegarder la liste de lecture", + "exportedAlreadyExistsYesNo": "Un ou plusieurs des fichiers exportés existent déjà.\n Voulez-vous les remplacer ?", + "selectFolder": "Sélectionnez le dossier", + "selectScreenshot": "Sélectionnez une capture d'écran", + "selectThumbnail": "Sélectionnez une miniature", + "selectCurationFolder": "Sélectionnez dossier(s) de conservation", + "selectCurationArchive": "Sélectionnez archive(s) de conservation", + "selectCurationMeta": "Sélectionnez meta(s) de conservation", + "selectPlaylistToImport": "Sélectionnez la playlist à importer", + "selectFileToExportPlaylist": "Sélectionnez le fichier où sauvegarder la playlist", "dataRequired": "Plus de données requises", - "dataRequiredDesc": "Tu dois installer la mise à niveau sur l'écran d'accueil avant de pouvoir utiliser le lanceur.\n\nVeux-tu le faire maintenant ?", + "dataRequiredDesc": "Vous devez installer la mise à niveau sur l'écran d'accueil avant de pouvoir utiliser le lanceur.\n\nVoulez-vous le faire maintenant ?", "upgradeWillInstallTo": "{0} sera installé(e) dans", "verifyPathSelection": "Les futures mises à niveau seront installées ici. Est-ce correct ?", "restartNow": "Redémarrer maintenant ?", - "restartToApplyUpgrade": "Cette mise à niveau ne sera appliquée qu'après un redémarrage.\nVeux-tu le faire maintenant ?", - "areYouSure": "Es-tu sûr ?", + "restartToApplyUpgrade": "Cette mise à niveau ne sera appliquée qu'après un redémarrage.\nVoulez-vous le faire maintenant ?", + "areYouSure": "Êtes-vous sûr ?", "areYouSureDelete": "Êtes-vous sûr de vouloir supprimer cette entrée ?", - "areYouSurePlaylistRemove": "Êtes-vous sûr de vouloir supprimer cette entrée de la liste de lecture?", + "areYouSurePlaylistRemove": "Êtes-vous sûr de vouloir supprimer cette entrée de la playlist ?", "cancel": "Annuler", - "mergePlaylists": "Fusionner les listes de lecture", + "mergePlaylists": "Fusionner les playlists", "newPlaylist": "Les laisser séparées", - "uploadPrivacyWarning": "Cela va rendre ton historique public.\nUn lien va être copié dans ton presse-papier.\n\nEs-tu sûr ?", + "uploadPrivacyWarning": "Ceci va rendre votre historique public.\nUn lien va être copié dans votre presse-papier.\n\nÊtes-vous sûr ?", "overwriteFileTitle": "Le fichier existe déjà !", - "overwriteFileMessage": "Un fichier du même nom existe déjà. Veux-tu l'écraser ?", + "overwriteFileMessage": "Un fichier du même nom existe déjà. Voulez-vous l'écraser ?", "overwriteFileDetail": "Chemin du fichier :", - "deleteTagFilterGroup": "Supprimer ce groupe de filtres de balises?", - "deleteCuration": "Supprimer cette curation?", - "importCuration": "Import this Curation?", - "deleteGameImage": "Supprimer cette image de jeu?", - "deletePlaylist": "Supprimer cette liste de lecture?", - "importAllCurations": "Importer toutes les curations?", - "deleteAllCurations": "Supprimer toutes les curations?", - "removePlaylistGame": "Retirer ce jeu de la liste de lecture?", - "deleteGame": "Delete this Game? This cannot be undone.", - "deleteGameData": "Delete this Game Data? This cannot be undone.", - "deleteAddApp": "Delete this Add App?", + "deleteTagFilterGroup": "Supprimer ce groupe de filtres de tags ?", + "deleteCuration": "Supprimer cette conservation ?", + "importCuration": "Importer cette conservation ?", + "deleteGameImage": "Supprimer cette image de jeu ?", + "deletePlaylist": "Supprimer cette playlist ?", + "importAllCurations": "Importer toutes les conservations ?", + "deleteAllCurations": "Supprimer toutes les conservations ?", + "removePlaylistGame": "Retirer ce jeu de la playlist ?", + "deleteGame": "Supprimer ce jeu ? Ceci ne peut être annulé.", + "deleteGameData": "Supprimer ces données de jeu ? Ceci ne peut être annulé.", + "deleteAddApp": "Supprimer cette application additionnelle ?", "deleteTagCategory": "Supprimer cette catégorie de tag ?", - "deleteTag": "Supprimer cette balise?", - "deleteTagAlias": "Supprimer cet alias de balise?", - "deleteSource": "Delete this Source?", - "uninstallGame": "Uninstall this Game?", - "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", - "unableToDeleteGame": "Unable to delete game. Is it in use? Try restarting the launcher.", - "downloadingGame": "Downloading Game...", - "verifyingGame": "Verifying Game...", - "aFewMinutes": "This may take a few minutes." + "deleteTag": "Supprimer ce tag ?", + "deleteTagAlias": "Supprimer cet alias de tag ?", + "deleteSource": "Supprimer cette source ?", + "uninstallGame": "Désinstaller ce jeu ?", + "unableToUninstallGameData": "Incapable de supprimer le data pack. Est-il utilisé ? Essayez de redémarrer le lanceur.", + "unableToDeleteGame": "Incapable de supprimer le jeu. Est-il utilisé ? Essayez de redémarrer le lanceur.", + "downloadingGame": "Téléchargement du jeu...", + "verifyingGame": "Vérification du jeu...", + "aFewMinutes": "Ceci peut prendre quelques minutes." }, "libraries": { "arcade": "Jeux", + "arcadeSingular": "Jeu", "arcadePlural": "Tous les jeux", "theatre": "Animations", + "theatreSingular": "Animation", "theatrePlural": "Toutes les animations", "auditorium": "Auditorium NG", + "auditoriumSingular": "Animation", "auditoriumPlural": "Toutes les animations NG" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logos et captures d'écran", "screenshotsDesc": "Ajoute des logos pour la vue en grille et des captures d'écran pour tous les jeux." } -} \ No newline at end of file +} diff --git a/lang/hu-HU.json b/lang/hu-HU.json index 2e919d2b0..85e86029c 100644 --- a/lang/hu-HU.json +++ b/lang/hu-HU.json @@ -4,8 +4,16 @@ "configHeader": "Beállítások", "configDesc": "(Bizonyos változások csak mentés és újraindítás után lépnek érvénybe)", "preferencesHeader": "Preferenciák", - "extremeGames": "Extrém tartalom", - "extremeGamesDesc": "Tagek láthatóságának engedélyezése az 'Extrém' tag-el szűrt csoportokban és a megegyező játékok mutatása.", + "extremeGames": "Extrém tartalomszűrők mutatása", + "extremeGamesDesc": "Engedélyezi az \"extrém tartalmak\" szűrő bekapcsolását, készítését és módosítását a gyereknek nem ajánlott tartalom irányítására.", + "hideExtremeScreenshots": "Extrém játékok képernyőmentéseinek elrejtése", + "hideExtremeScreenshotsDesc": "Elrejti az extrém-ként taggelt játékok képernyőmentéseit, előhozható a képdobozra kattintással.", + "fancyAnimations": "Szép animációk", + "fancyAnimationsDesc": "Látványos animációk engedélyezése a launcherben.", + "searchLimit": "Keresés korlátozása", + "searchLimitDesc": "A visszaadott keresési találatok korlátozása bármelyik keresésnél", + "searchLimitUnlimited": "Korlátlan", + "searchLimitValue": "{0} eredmény", "enableEditing": "Szerkesztés engedélyezése", "enableEditingDesc": "Játékok és további alkalmazások módosítását engedélyezi, valamint a szerkesztéshez kapcsolódó lapokat is mutatja.", "onDemandImages": "Igény szerinti Képek", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Ezt a nyelvet fogja használni a felület ha valami nincs lefordítva az elsődleges nyelvben.", "auto": "Automatikus", "none": "Semelyik", + "contentFiltersHeader": "Tartalom szűrők", "flashpointHeader": "Flashpoint", "flashpointPath": "Flashpoint útvonal", "flashpointPathDesc": "Útvonal a Flashpoint mappához. (Lehet relatív)", @@ -79,6 +88,7 @@ "helpInfo": "Segítség kell?", "help": "Olvassa el a kézikönyvet", "upgradesHeader": "Frissítések", + "updateFeedHeader": "Hírfolyam", "installComplete": "Telepítés befejezve", "alreadyInstalled": "Már telepítve", "download": "Letöltés", @@ -103,7 +113,8 @@ "clearLog": "Napló törlése", "copy404Urls": "404-es URL-ek másolása", "uploadLog": "Log feltöltése", - "copiedToClipboard": "Vágólapra másolva" + "copiedToClipboard": "Vágólapra másolva", + "openLogsWindow": "A Logok ablakot megnyitja" }, "app": { "home": "Kezdőlap", @@ -266,7 +277,7 @@ "thumbnail": "Előnézet", "screenshot": "Képernyőmentés", "dropImageHere": "Húzzon be egy képet ide, hogy hozzáadja", - "noGameSelected": "Nincs kiválasztott játék", + "noGameSelected": "Nincs kiválasztva", "clickToSelectGame": "Kattintson egy játékra a kiválasztáshoz.", "deleteAdditionalApplication": "További alkalmazások törlése", "deleteGameAndAdditionalApps": "Játék törlése (és a hozzá kapcsolódó alkalmazásoké)", @@ -288,7 +299,10 @@ "saveAndRestart": "Mentés és újraindítás", "thereAreNoGames": "Nincsenek játékok.", "noGameMatchedDesc": "Nincs megfelelő találat a keresésre.", - "noGameMatchedSearch": "Próbáljon meg szélesebb körben keresni." + "noGameMatchedSearch": "Próbáljon meg szélesebb körben keresni.", + "mountParameters": "Mount paraméterek", + "noMountParameters": "Nincsen mount paraméter(ek)", + "showExtremeScreenshot": "Extrém képernyőmentések mutatása" }, "tags": { "name": "Név", @@ -413,7 +427,9 @@ "installingFiles": "Fájlok telepítése...", "complete": "Kész", "exportMetaEditTitle": "Meta módosítás exportálása", - "exportMetaEditDesc": "Válassza ki az összes exportálni kívánt tulajdonságot" + "exportMetaEditDesc": "Válassza ki az összes exportálni kívánt tulajdonságot", + "showImage": "Kép mutatása", + "searching": "Keresés..." }, "menu": { "viewThumbnailInFolder": "Előnézeti kép megtekintése mappában", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Játékok", + "arcadeSingular": "Játék", "arcadePlural": "Összes Játék", "theatre": "Animációk", + "theatreSingular": "Animáció", "theatrePlural": "Összes Animáció", "auditorium": "NG Auditórium", + "auditoriumSingular": "Animáció", "auditoriumPlural": "Összes NG Animáció" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logók és Képernyőmentések", "screenshotsDesc": "Logók hozzáadása a rácsos megjelenítéshez és képernyőmentések az összes játékhoz." } -} \ No newline at end of file +} diff --git a/lang/it-IT.json b/lang/it-IT.json index bd62fc9e8..a31c7d745 100644 --- a/lang/it-IT.json +++ b/lang/it-IT.json @@ -1,119 +1,130 @@ { - "name": "Inglese", + "name": "Italiano", "config": { "configHeader": "Impostazioni", - "configDesc": "(Devi premere 'Salva & riavvia' per rendere effettive alcune modifiche)", + "configDesc": "(Devi premere 'Salva e Riavvia' per rendere effettive alcune modifiche)", "preferencesHeader": "Preferenze", - "extremeGames": "Contenuto Estremo", - "extremeGamesDesc": "Consenti di mostrare i tag all'interno dei gruppi di filtro tag 'Extreme' e delle partite corrispondenti.", + "extremeGames": "Mostra Filtri Estremi", + "extremeGamesDesc": "Consente di attivare, creare e modificare filtri etichette estremi per controllare i contenuti non adatti ai bambini.", + "hideExtremeScreenshots": "Nascondi Schermate Estreme", + "hideExtremeScreenshotsDesc": "Nasconde le schermate dei contenuti etichettati estremi, possono essere mostrate cliccando sul riquadro immagine.", + "fancyAnimations": "Animazioni Estrose", + "fancyAnimationsDesc": "Abilita le animazioni estrose nel launcher.", + "searchLimit": "Limite di Ricerca", + "searchLimitDesc": "Limita il numero di risultati trovati per qualsiasi ricerca", + "searchLimitUnlimited": "Illimitati", + "searchLimitValue": "{0} Risultati", "enableEditing": "Abilita Modifica", "enableEditingDesc": "Abilita la modifica dei giochi e delle applicazioni aggiuntive. Mostra anche le schede relative alla modifica.", - "onDemandImages": "Immagini Su Richiesta", - "onDemandImagesDesc": "Scaricare e salvare loghi e schermate mancanti in quanto necessari.", + "onDemandImages": "Immagini su Richiesta", + "onDemandImagesDesc": "Scarica e salva loghi e schermate mancanti quando necessari.", "currentLanguage": "Lingua", - "currentLanguageDesc": "Quale lingua verrà utilizzata in prevalenza nel launcher.", - "fallbackLanguage": "Lingua di riserva", - "fallbackLanguageDesc": "Quale lingua usare al posto della lingua utilizzata se incompleta.", + "currentLanguageDesc": "Quale lingua verrà utilizzata principalmente dal launcher.", + "fallbackLanguage": "Lingua di Riserva", + "fallbackLanguageDesc": "Quale lingua usare al posto della lingua principale se questa è incompleta.", "auto": "Automatico ({0})", "none": "Nessuna", + "contentFiltersHeader": "Filtri del Contenuto", "flashpointHeader": "Flashpoint", "flashpointPath": "Percorso File di Flashpoint", - "flashpointPathDesc": "Percorso alla cartella Flashpoint (può variare lievemente)", + "flashpointPathDesc": "Percorso della cartella Flashpoint (può essere relativo)", "browse": "Sfoglia", "libraries": "Librerie", "randomLibraries": "Librerie Casuali", - "randomLibrariesDesc": "Librerie da usare per le Scelte Casuali nella home page.", - "updateSource": "Aggiorna Sorgente", + "randomLibrariesDesc": "Librerie da usare per le Scelte Casuali nella pagina principale.", + "updateSource": "Aggiorna Fonte", "platforms": "Piattaforme", - "nativePlatforms": "Piattaforme native", - "nativePlatformsDesc": "Usa versioni native di queste piattaforme. (Se non sono disponibili utilizzerà Wine)", - "tagFilterGroups": "Gruppi di filtri di etichette", - "tagFilterGroupsDesc": "Questi gruppi filtreranno i suggerimenti e i giochi dai risultati della ricerca", - "editTagFilter": "Modifica Gruppi di filtri di etichette", - "duplicateTagFilter": "Duplica Gruppo di filtri di etichette", - "deleteTagFilter": "Elimina Gruppo di filtri di etichette", - "appPathOverrides": "Sovvrascrittura Percorso App", - "appPathOverridesDesc": "Sostituisce il percorso dell'applicazione a sinistra con quello a destra quando si lanciano i giochi.", - "visualsHeader": "Apparenza", - "useCustomTitleBar": "Usa Barra del Titolo personalizzata", - "useCustomTitleBarDesc": "Usa una barra dei titoli personalizzata in cima a questa finestra.", + "nativePlatforms": "Piattaforme Native", + "nativePlatformsDesc": "Usa le versioni native di queste piattaforme. (Se non sono disponibili si utilizzerà Wine)", + "tagFilterGroups": "Filtra per Gruppi di Etichette", + "tagFilterGroupsDesc": "I suggerimenti e i giochi appartenenti a questi gruppi non compariranno nei risultati di ricerca", + "editTagFilter": "Modifica Filtro per Gruppi di Etichette", + "duplicateTagFilter": "Duplica Filtro per Gruppi di Etichette", + "deleteTagFilter": "Elimina Filtro per Gruppi di Etichette", + "appPathOverrides": "Sovrascrittura Percorso Applicazione", + "appPathOverridesDesc": "Sostituisce il percorso dell'applicazione a sinistra con quello a destra quando si avviano i giochi.", + "visualsHeader": "Aspetto", + "useCustomTitleBar": "Usa Barra del Titolo Personalizzata", + "useCustomTitleBarDesc": "Usa una barra del titolo personalizzata nella parte superiore di questa finestra.", "theme": "Tema", "noTheme": "Nessun Tema", "themeDesc": "ID del tema da usare.", - "logoSet": "Set Logo", - "noLogoSet": "Nessun Logo Set", - "logoSetDesc": "ID del logo impostato da usare.", + "logoSet": "Set di Loghi", + "noLogoSet": "Nessun Set di Loghi", + "logoSetDesc": "ID del set di loghi da usare.", "advancedHeader": "Avanzate", "showDeveloperTab": "Mostra Scheda Sviluppatore", "showDeveloperTabDesc": "Mostra la scheda 'Sviluppatore'. Molto probabilmente utile solo per sviluppatori e curatori.", "server": "Server", "serverDesc": "Quale Processo Server da eseguire all'avvio del launcher.", "metadataServerHost": "Host Server Metadati", - "metadataServerHostDesc": "Host di un server di metadati. Utilizzato per sincronizzare le informazioni di gioco nuove e aggiornate al launcher.", + "metadataServerHostDesc": "Host di un server di metadati. Utilizzato per sincronizzare le informazioni di gioco nuove e aggiornate con il launcher.", "extensionsHeader": "Estensioni", - "noExtensionsLoaded": "Nessuna estensione caricata, installale su '{0}'", - "extDevScripts": "Script Sviluppatore(ri)", + "noExtensionsLoaded": "Nessuna Estensione caricata, installale su '{0}'", + "extDevScripts": "Script Sviluppatore", "extThemes": "Tema(i)", - "extLogoSets": "Set Di Logo(hi)", + "extLogoSets": "Set di Loghi", "extApplications": "Applicazione(i)", "saveAndRestart": "Salva e Riavvia", "saveAndClose": "Salva e Chiudi", - "tagFilterGroupEditor": "Editor Gruppo Filtri Etichette" + "tagFilterGroupEditor": "Editor Filtri per Gruppi di Etichette" }, "home": { "updateHeader": "Aggiornamento Launcher", "currentVersion": "Versione Attuale", - "nextVersion": "Versione successiva", + "nextVersion": "Versione Successiva", "updateAvailable": "Aggiornamento Disponibile", "upToDate": "Aggiornato.", - "downloadingUpdate": "Download aggiornamento...", + "downloadingUpdate": "Scaricamento Aggiornamento...", "quickStartHeader": "Avvio Rapido", - "hallOfFameInfo": "Vuoi solo il meglio del meglio? Prova {0}!", + "hallOfFameInfo": "Vuoi solo il meglio del meglio? Visita la {0}!", "hallOfFame": "Hall of Fame", - "allGamesInfo": "Stai cercando qualcosa da giocare? Dai un'cchiata a {0}.", + "allGamesInfo": "Stai cercando un gioco? Dai un'occhiata a {0}.", "allGames": "Tutti i Giochi", - "allAnimationsInfo": "Vuoi solo qualcosa da vedere? Guarda {0}.", + "allAnimationsInfo": "Vuoi solo vedere qualcosa? Guarda {0}.", "allAnimations": "Tutte le Animazioni", - "configInfo": "Vuoi cambiare qualcosa? Vai a {0}.", + "configInfo": "Vuoi cambiare qualcosa? Vai in {0}.", "config": "Impostazioni", - "helpInfo": "Serve una mano? {0}.", + "helpInfo": "Serve aiuto? {0}.", "help": "Leggi il manuale", - "upgradesHeader": "Potenziamenti", + "upgradesHeader": "Aggiornamenti", + "updateFeedHeader": "Notizie", "installComplete": "Installazione Completata", "alreadyInstalled": "Già Installato", "download": "Scarica", "update": "Aggiorna", - "checkingUpgradeState": "Controllo stato aggiornamento...", + "checkingUpgradeState": "Controllo Stato Aggiornamenti...", "extrasHeader": "Extra", - "favoritesPlaylist": "Playlist dei Preferiti", - "tagList": "Lista Tag", + "favoritesPlaylist": "Scaletta dei Preferiti", + "tagList": "Elenco Etichette", "filterByPlatform": "Filtra per piattaforma", "plannedFeatures": "Scopri le funzioni su cui stiamo lavorando!", "notesHeader": "Note", - "notes": "Non dimenticare di leggere il manuale se hai problemi.", + "notes": "Non dimenticare di leggere il manuale se riscontri dei problemi.", "linuxSupport": "Problemi su Linux? {0}", "linuxSupportLinkText": "Apri FAQ", - "randomPicks": "Selezioni casuali", - "rerollPicks": "Rilaminare le scelte" + "randomPicks": "Scelta Casuale", + "rerollPicks": "Nuova Scelta" }, "logs": { "filters": "Filtri", - "logLevels": "Livelli Di Log", - "copyText": "Copia testo", + "logLevels": "Livelli di Registro", + "copyText": "Copia Testo", "clearLog": "Svuota Registro Eventi", - "copy404Urls": "Copia 404 URL", - "uploadLog": "Carica Log", - "copiedToClipboard": "Copiato negli appunti" + "copy404Urls": "Copia URL 404", + "uploadLog": "Carica Registro", + "copiedToClipboard": "Copiato negli appunti", + "openLogsWindow": "Apri Finestra Di Log" }, "app": { - "home": "Home", + "home": "Per Iniziare", "browse": "Sfoglia", "tags": "Etichette", "categories": "Categorie", "logs": "Registro Eventi", "config": "Impostazioni", "about": "Informazioni", - "curate": "Curatela", + "curate": "Curatore", "developer": "Sviluppatore", "searchPlaceholder": "Cerca...", "hideRightSidebar": "Nascondi barra laterale destra", @@ -121,65 +132,65 @@ "hideLeftSidebar": "Nascondi barra laterale sinistra", "showLeftSidebar": "Mostra barra laterale sinistra", "total": "Totale", - "newGame": "Nuova Partita", + "newGame": "Nuovo Gioco", "list": "Elenco", "grid": "Griglia", - "searchResults": "Risultati Ricerca" + "searchResults": "Risultati di Ricerca" }, "filter": { - "dateAdded": "Data aggiunta", - "dateModified": "Data Modifica", + "dateAdded": "Data di Aggiunta", + "dateModified": "Data di Modifica", "platform": "Piattaforma", "series": "Serie", "title": "Titolo", "developer": "Sviluppatore", - "publisher": "Editore", + "publisher": "Pubblicatore", "ascending": "Ascendente", "descending": "Discendente" }, "developer": { "developerHeader": "Sviluppatore", - "developerDesc": "Questo è il luogo in cui andranno tutti gli strumenti utili agli sviluppatori.", - "checkMissingImages": "Controlla immagini mancanti", - "checkMissingImagesDesc": "Elenca tutti i giochi senza anteprime o schermate", - "checkGameIds": "Controlla ID del gioco", + "developerDesc": "Qui è dove si trovano tutti gli strumenti utili agli sviluppatori.", + "checkMissingImages": "Controlla Immagini Mancanti", + "checkMissingImagesDesc": "Elenca tutti i giochi sprovvisti di anteprime e/o schermate", + "checkGameIds": "Controlla ID dei Giochi", "checkGameIdsDesc": "Elenca tutti i giochi con ID duplicati o non validi", - "checkGameTitles": "Controlla i titoli dei giochi", + "checkGameTitles": "Controlla i Titoli dei Giochi", "checkGameTitlesDesc": "Elenca tutti i giochi con titoli duplicati sulla stessa piattaforma", - "checkGameFields": "Controlla i campi di gioco", + "checkGameFields": "Controlla i Campi dei Giochi", "checkGameFieldsDesc": "Elenca tutti i giochi con campi necessari vuoti", - "checkPlaylists": "Controlla Playlist", - "checkPlaylistsDesc": "Elenca tutte le playlist con ID duplicati o non validi, o che hanno giochi con ID mancanti, non validi o duplicati", - "checkGameFileLocation": "Controlla la posizione dei file di gioco", - "checkGameFileLocationDesc": "Elenca tutti i giochi con comandi di avvio che non possono essere trasformati in percorsi ai file (questo è correlato alla funzione 'Apri Posizione File', non avviare il gioco)", - "checkMissingExecMappings": "Controlla mappature Exec Mancanti", - "checkMissingExecMappingsDesc": "Elenca tutte le esecuzioni uniche per win32, linux e Darwin che non hanno una mappatura exec", - "renameImagesTitleToId": "Rinomina immagini (Titolo => ID)", - "renameImagesTitleToIdDesc": "Trova tutte le immagini di gioco con il titolo nel loro nome e rinominale per usarne l'ID", - "renameImagesIdToTitle": "Rinomina immagini (ID => Titolo)", - "renameImagesIdToTitleDesc": "Trova tutte le immagini digioco con l'ID nel loro nome e rinominale per usarne il titolo", + "checkPlaylists": "Controlla Scalette", + "checkPlaylistsDesc": "Elenca tutte le scalette con ID duplicati o non validi, o che hanno giochi con ID mancanti, non validi o duplicati", + "checkGameFileLocation": "Controlla la Posizione dei File dei Giochi", + "checkGameFileLocationDesc": "Elenca tutti i giochi con comandi di avvio che non possono essere trasformati in percorsi ai file (questo comando è correlato alla funzione 'Apri Posizione File', non all'avvio del gioco)", + "checkMissingExecMappings": "Controlla Mappature Exec Mancanti", + "checkMissingExecMappingsDesc": "Elenca tutte le exec esclusive per win32, linux e darwin che non hanno una mappatura exec", + "renameImagesTitleToId": "Rinomina Immagini (Titolo => ID)", + "renameImagesTitleToIdDesc": "Trova tutte le immagini dei giochi con il titolo del gioco nel loro nome e rinominale affinché ne usino invece l'ID", + "renameImagesIdToTitle": "Rinomina Immagini (ID => Titolo)", + "renameImagesIdToTitleDesc": "Trova tutte le immagini dei giochi che hanno l'ID del gioco nel loro nome e rinominale affinché ne usino invece il titolo", "createMissingFolders": "Crea Cartelle Mancanti", "createMissingFoldersDesc": "Trova tutte le cartelle mancanti nella struttura della cartella Flashpoint e creale", "importLegacyPlatforms": "Importa Piattaforme Legacy", - "importLegacyPlatformsDesc": "Imports Legacy Platforms from `Data/Platforms/` - ATTENZIONE: Sovrascriverà i giochi in conflitto", + "importLegacyPlatformsDesc": "Importa Piattaforme Legacy da `Data/Platforms/` - ATTENZIONE: Sovrascriverà i giochi in conflitto", "importLegacyPlaylists": "Importa Scalette Legacy", "importLegacyPlaylistsDesc": "Importa Scalette Legacy da `/Data/Playlists`", - "deleteAllPlaylists": "Elimina Tutte le playlist", - "deleteAllPlaylistsDesc": "PERICOLO! Elimina tutte le playlist.", - "importMetaEdits": "Import Meta Edits", - "importMetaEditsDesc": "Importa TUTTE le modifiche dei Meta da `/Data/MetaEdits`", + "deleteAllPlaylists": "Elimina Tutte le Scalette", + "deleteAllPlaylistsDesc": "PERICOLOSO! Elimina tutte le scalette.", + "importMetaEdits": "Importa Modifiche dei Meta", + "importMetaEditsDesc": "Importa TUTTE le Modifiche dei Meta da `/Data/MetaEdits`", "fixPrimaryAliases": "Correggi Alias Primari", - "fixPrimaryAliasesDesc": "Aggiungi un alias primario a ogni etichetta a cui mancano", - "fixCommaTags": "Correggi etichette con le virgole", - "fixCommaTagsDesc": "Divide le etichette con le virgole in molte etichette e li applica a tutti i giochi applicabili", + "fixPrimaryAliasesDesc": "Aggiungi un alias primario a ogni etichetta a cui ne manca uno", + "fixCommaTags": "Correggi Etichette con le Virgole", + "fixCommaTagsDesc": "Divide le etichette con le virgole in più etichette e le applica a tutti i giochi possibili", "exportTags": "Esporta Etichette", - "exportTagsDesc": "Crea un file pieno di eticehtte e categorie di eticehtte che possono essere utilizzati da 'Importa eticehtte'", + "exportTagsDesc": "Crea un file pieno di etichette e di categorie di etichette che può essere utilizzato da 'Importa Etichette'", "importTags": "Importa Etichette", - "importTagsDesc": "Importa eticehtte da un file creato da 'Esporta etichetta'", - "migrateExtremeGames": "Migrare Giochi Estremi", - "migrateExtremeGamesDesc": "Rimuove 'Estremo' da qualsiasi gioco e assegna il tag 'Estremo-ORIGINALE' a loro", - "massImportGameData": "Importazione Massiva Dati Di Gioco", - "massImportGameDataDesc": "Importazione massica di pacchetti di dati di gioco, cercando di abbinare il loro nome di file a un UUID di gioco.", + "importTagsDesc": "Importa Etichette da un file creato da 'Esporta Etichette'", + "migrateExtremeGames": "Sposta Giochi Estremi", + "migrateExtremeGamesDesc": "Rimuove 'Estremo' da ogni gioco e assegna loro l'etichetta 'LEGACY-Extreme'", + "massImportGameData": "Importa Massivamente i Dati di Gioco", + "massImportGameDataDesc": "Importa massivamente pacchetti di Dati dei Giochi, cercando di abbinare i loro nomi all'UUID di un gioco.", "servicesHeader": "Servizi in secondo piano", "servicesMissing": "Non ci sono servizi.", "running": "In esecuzione", @@ -197,98 +208,101 @@ "about": { "aboutHeader": "Informazioni", "flashpoint": "BlueMaxima's Flashpoint", - "flashpointDesc": "Il progetto di conservazione per cui è stato costruito questo launcher mira a essere un archivio, un museo e una collezione giocabile di giochi nati e fondati sul web.", + "flashpointDesc": "Il progetto di preservazione per cui è stato costruito questo launcher vuole essere un archivio, un museo e una collezione giocabile di giochi di Internet nati e basati sul web.", "website": "Sito Web", "flashpointLauncher": "Launcher di Flashpoint", - "flashpointLauncherDesc": "Un programma open-source utilizzato per sfogliare, gestire e giocare i giochi dal progetto Flashpoint.", + "flashpointLauncherDesc": "Un programma open-source per computer desktop che consente di giocare ai giochi del progetto Flashpoint, di sfogliarli e di gestirli.", "version": "Versione", "license": "Licenza", "licenseInfo": "MIT (Leggi il file chiamato \"LICENSE\" per maggiori informazioni)", "creditsHeader": "Ringraziamenti", - "specialThanks": "Ringraziamenti speciali" + "specialThanks": "Ringraziamenti Speciali" }, "browse": { - "noTitle": "Nessun titolo", + "noTitle": "Nessun Titolo", "by": "da", - "play": "Riproduci", + "play": "Esegui", "stop": "Interrompi", - "noDeveloper": "Nessun sviluppatore", - "alternateTitles": "Titoli alternativi", - "noAlternateTitles": "Nessun titolo alternativo", - "tags": "Tag", - "noTags": "Nessun Tag", + "noDeveloper": "Nessuno Sviluppatore", + "alternateTitles": "Titoli Alternativi", + "noAlternateTitles": "Nessun Titolo Alternativo", + "tags": "Etichette", + "noTags": "Nessuna Etichetta", "enterTag": "Inserisci Etichetta", "series": "Serie", "noSeries": "Nessuna Serie", "publisher": "Pubblicato da", - "noPublisher": "Nessun Editore", + "noPublisher": "Nessun Pubblicatore", "source": "Fonte", "noSource": "Nessuna Fonte", "platform": "Piattaforma", "noPlatform": "Nessuna Piattaforma", - "playMode": "Modalità Gioco", - "noPlayMode": "Nessuna Modalità Gioco", + "playMode": "Modalità di Gioco", + "noPlayMode": "Nessuna Modalità di Gioco", "status": "Stato", - "noStatus": "Nessuno stato", + "noStatus": "Nessuno Stato", "version": "Versione", - "noVersion": "Nessuna versione", - "releaseDate": "Data di rilascio", - "noReleaseDate": "Nessuna Data di Rilascio", + "noVersion": "Nessuna Versione", + "releaseDate": "Data di Pubblicazione", + "noReleaseDate": "Nessuna Data di Pubblicazione", "language": "Lingua", - "noLanguage": "Nessuna lingua", - "dateAdded": "Data di aggiunta", - "dateModified": "Data Modifica", - "brokenInInfinity": "Rotto (in Infinito)", + "noLanguage": "Nessuna Lingua", + "dateAdded": "Data di Aggiunta", + "dateModified": "Data di Modifica", + "brokenInInfinity": "Non Funzionante (in Infinity)", "extreme": "Estremo", - "playlistNotes": "Appunti Playlist", - "noPlaylistNotes": "Nessun appunto per la playlist", + "playlistNotes": "Appunti per la Scaletta", + "noPlaylistNotes": "Nessun Appunto per la Scaletta", "notes": "Appunti", "noNotes": "Nessun Appunto", - "originalDescription": "Descrizione originale", - "noOriginalDescription": "Nessuna descrizione originale", - "additionalApplications": "Applicazioni aggiuntive", - "noName": "Nessun nome", + "originalDescription": "Descrizione Originale", + "noOriginalDescription": "Nessuna Descrizione Originale", + "additionalApplications": "Applicazioni Aggiuntive", + "noName": "Nessun Nome", "launch": "Avvia", - "autoRunBefore": "Avvio automatico prima", - "waitForExit": "Attendi l'uscita", - "applicationPath": "Percorso File dell'applicazione", - "noApplicationPath": "Nessun percorso file dell'applicazione", - "launchCommand": "Comando d'avvio", - "noLaunchCommand": "Nessun comando d'avvio", + "autoRunBefore": "Avvia Automaticamente Prima", + "waitForExit": "Attendi l'Uscita", + "applicationPath": "Percorso File dell'Applicazione", + "noApplicationPath": "Nessun Percorso File dell'Applicazione", + "launchCommand": "Comando di Avvio", + "noLaunchCommand": "Nessun Comando di Avvio", "searching": "Ricerca...", "library": "Libreria", - "defaultLibrary": "Libreria predefinita", - "uninstallGame": "Disinstalla giochi", + "defaultLibrary": "Libreria Predefinita", + "uninstallGame": "Disinstalla Gioco", "installed": "Installato", - "notInstalled": "Non installato", + "notInstalled": "Non Installato", "legacyGame": "Gioco Legacy", "download": "Scarica", "thumbnail": "Anteprima", - "screenshot": "Schermate", + "screenshot": "Schermata", "dropImageHere": "Trascina un'immagine qui per aggiungerla", - "noGameSelected": "Nessun gioco selezionato", + "noGameSelected": "Nessun {0} selezionato", "clickToSelectGame": "Clicca su un gioco per selezionarlo.", "deleteAdditionalApplication": "Elimina Applicazione Aggiuntiva", - "deleteGameAndAdditionalApps": "Elimina Gioco (e Ulteriori Applicazioni)", - "removeGameFromPlaylist": "Rimuovi Gioco dalla Playlist", + "deleteGameAndAdditionalApps": "Elimina Gioco (e Applicazioni Aggiuntive)", + "removeGameFromPlaylist": "Rimuovi questo Gioco dalla Scaletta", "saveChanges": "Salva le Modifiche", - "discardChanges": "Annulla le modifiche", - "editGame": "Modifica Gioco", - "allGames": "Tutti i giochi", - "newPlaylist": "Nuova playlist", - "importPlaylist": "Importa playlist", - "emptyPlaylist": "Playlist vuota", - "noGamesFound": "Nessun gioco trovato!", - "dropGameOnLeft": "Trascina un gioco su questa playlist nella {0} per aggiungerlo.", + "discardChanges": "Annulla le Modifiche", + "editGame": "Modifica il Gioco", + "allGames": "Tutti i Giochi", + "newPlaylist": "Crea una Nuova Scaletta", + "importPlaylist": "Importa una Scaletta", + "emptyPlaylist": "Scaletta Vuota", + "noGamesFound": "Nessun Gioco Trovato!", + "dropGameOnLeft": "Trascina un gioco su questa scaletta nella {0} per aggiungerlo.", "leftSidebar": "barra laterale sinistra", - "setFlashpointPathQuestion": "Hai impostato il percorso {0} nella pagina {1}?", + "setFlashpointPathQuestion": "Hai impostato il {0} nella pagina {1}?", "flashpointPath": "Percorso File di Flashpoint", "config": "Impostazioni", "noteSaveAndRestart": "Nota: Devi premere {0} affinché le modifiche abbiano effetto.", - "saveAndRestart": "Salva e Riavvia", + "saveAndRestart": "Salva & Riavvia", "thereAreNoGames": "Non ci sono giochi.", "noGameMatchedDesc": "Nessun gioco corrisponde alla tua ricerca.", - "noGameMatchedSearch": "Prova a cercare qualcosa di meno specifico." + "noGameMatchedSearch": "Prova a cercare qualcosa di meno specifico.", + "mountParameters": "Parametri di Montaggio", + "noMountParameters": "Nessun Parametro di Montaggio", + "showExtremeScreenshot": "Mostra Schermata Estrema" }, "tags": { "name": "Nome", @@ -300,210 +314,215 @@ "newCategory": "Nuova Categoria", "enterAlias": "Inserisci Alias", "aliases": "Alias", - "editTag": "Modifica Tag", + "editTag": "Modifica l'Etichetta", "color": "Colore", - "noTagSelected": "Nessuna Etichetta Selezionato", - "clickToSelectTag": "Fare clic per selezionare Etichetta", - "deleteTagAlias": "Elimina Alias Etichetta", - "setPrimaryAlias": "Imposta Alias Primario", + "noTagSelected": "Nessuna Etichetta Selezionata", + "clickToSelectTag": "Fai clic per selezionare un'Etichetta", + "deleteTagAlias": "Elimina Alias dell'Etichetta", + "setPrimaryAlias": "Scegli come Alias Primario", "mergeIntoTag": "Unisci all'Etichetta", - "mergeTag": "Unisci Etichetta", - "makeAliasWhenMerged": "Fare di questo un alias del'etichetta?", - "deleteTag": "Elimina etichetta", - "deleteTagCategory": "Elimina Categoria Etichetta", + "mergeTag": "Unisci l'Etichetta", + "makeAliasWhenMerged": "Imposta come alias dell'etichetta?", + "deleteTag": "Elimina l'Etichetta", + "deleteTagCategory": "Elimina la Categoria Etichette", "locked": "Bloccato durante l'elaborazione..." }, "curate": { "importAll": "Importa Tutti", - "importAllDesc": "Importa tutte le curazioni che sono attualmente caricate", - "deleteAll": "Elimina tutto", - "deleteAllDesc": "Elimina tutte le curazioni che sono attualmente caricate", - "saveImportedCurations": "Salva Curature Importate", - "keepArchiveKey": "Mantieni UUID Archivio Curazione", - "symlinkCurationContent": "Cartella dei contenuti della curva del collegamento simbolico (prompt dell'amministratore su Windows, richiesto per MAD4FP)", - "useTagFilters": "Usa filtri etichette nei suggerimenti", - "openCurationsFolder": "Cartella Curatela", - "openCurationsFolderDesc": "Apri la cartella Curations in explorer", - "openExportsFolder": "Curazioni esportate", - "openExportsFolderDesc": "Apri la cartella esportazione predefinita contenente le tue curazioni esportate.", - "openImportedFolder": "Curazioni importate", - "openImportedFolderDesc": "Apre la cartella predefinita delle curazioni importate contenente le tue curazioni importate.", - "newCuration": "Nuova curatela", - "newCurationDesc": "Crea una nuova curatela con una cartella unica", - "loadMeta": "Carica Metadata", - "loadMetaDesc": "Carica uno o più file metadata", + "importAllDesc": "Importa tutti i giochi curati attualmente caricati", + "deleteAll": "Elimina Tutti", + "deleteAllDesc": "Elimina tutti i giochi curati attualmente caricati", + "saveImportedCurations": "Salva Giochi Curati Importati", + "keepArchiveKey": "Mantieni UUID dell'Archivio del Gioco Curato", + "symlinkCurationContent": "Collegamento Simbolico alla Cartella Content del Gioco Curato (prompt Amministratore su Windows, richiesto per MAD4FP)", + "useTagFilters": "Usa Filtri Etichette nei Suggerimenti", + "openCurationsFolder": "Cartella Curatore", + "openCurationsFolderDesc": "Apri la Cartella Curations in Esplora Risorse", + "openExportsFolder": "Giochi Curati Esportati", + "openExportsFolderDesc": "Apri la cartella esportazione predefinita contenente i tuoi giochi curati esportati.", + "openImportedFolder": "Giochi Curati Importati", + "openImportedFolderDesc": "Apri la cartella importazione predefinita contenente i tuoi giochi curati importati.", + "newCuration": "Nuovo Gioco da Curare", + "newCurationDesc": "Crea una nuova cartella esclusiva per il nuovo gioco da curare", + "loadMeta": "Carica Metadati", + "loadMetaDesc": "Carica uno o più file metadati dei Giochi Curati", "loadArchive": "Carica Archivio", - "loadArchiveDesc": "Carica uno o più archivi", + "loadArchiveDesc": "Carica uno o più archivi di Giochi Curati", "loadFolder": "Carica Cartella", - "loadFolderDesc": "Carica una o più cartelle", - "scanNewCurationFolders": "Scansiona Per Nuove Curazioni", - "scanNewCurationFoldersDesc": "Scansioni per curare che sono state aggiunte dopo la scansione iniziale", - "noCurations": "Nessuna curatela", - "id": "Cartella Curatela", + "loadFolderDesc": "Carica una o più cartelle di Giochi Curati", + "scanNewCurationFolders": "Cerca Nuovi Giochi Curati", + "scanNewCurationFoldersDesc": "Scansiona in cerca di giochi curati che sono stati aggiunti dopo la scansione iniziale", + "noCurations": "Nessun Gioco Curato", + "id": "Cartella Gioco Curato", "heading": "Intestazione", - "noHeading": "Nessuna intestazione", - "folderName": "Cartella Extra", + "noHeading": "Nessuna Intestazione", + "folderName": "Cartella degli Extra", "noFolderName": "Nessun Nome Cartella", - "message": "Lancia messaggio", - "noMessage": "Nessun messaggio", - "curationNotes": "Note Curatela", - "noCurationNotes": "Nessuna nota", - "newAddApp": "Nuova App", - "addExtras": "Aggiungi extra", + "message": "Avvia Messaggio", + "noMessage": "Nessun Messaggio", + "curationNotes": "Note del Curatore", + "noCurationNotes": "Nessuna Nota del Curatore", + "newAddApp": "Nuova Applicazione", + "addExtras": "Aggiungi Extra", "addMessage": "Aggiungi Messaggio", - "removeAddApp": "Rimuovi App", - "indexContent": "Aggiorna contenuto", + "removeAddApp": "Rimuovi Applicazione", + "indexContent": "Aggiorna Lista File", "delete": "Cancella", - "deleteCurationDesc": "Elimina questa curatela (i file non possono essere recuperati)", - "openFolder": "Apri cartella", + "deleteCurationDesc": "Elimina questo gioco curato (i file non potranno essere recuperati)", + "openFolder": "Apri Cartella", "run": "Avvia", "runWithMAD4FP": "Esegui con MAD4FP", "export": "Esporta", "import": "Importa", - "contentFiles": "File di contenuto", - "warnings": "Avvertenze", - "noLaunchCommand": "'Esegui Comando' è un campo richiesto. Per favore compilalo prima di importare.", - "invalidLaunchCommand": "'Esegui comando' non corrisponde a questi requisiti di URL:", - "releaseDateInvalid": "'Data di rilascio' deve seguire AAAA-MM-GG (mese e giorno sono opzionali).", - "unusedApplicationPath": "'Percorso Applicazione' ha un valore non utilizzato. Assicurati che sia scritto correttamente!", - "unusedTags": "I seguenti tag non sono presenti in nessun altro gioco. Sono corretti?", - "unusedPlatform": "'Piattaforma' ha un valore non utilizzato. Assicurati che sia scritto correttamente!", + "contentFiles": "Lista dei File della Cartella Content", + "warnings": "Attenzione", + "noLaunchCommand": "'Comando di Avvio' è un campo richiesto. Per favore compilalo prima di importare.", + "invalidLaunchCommand": "'Comando di Avvio' non corrisponde a questi requisiti di URL:", + "releaseDateInvalid": "'Data di pubblicazione' deve seguire il formato AAAA-MM-GG (MM e GG opzionali).", + "unusedApplicationPath": "'Percorso Applicazione' non valido. Assicurati che sia scritto correttamente!", + "unusedTags": "Le seguenti etichette non sono presenti in nessun altro gioco. Sono corrette?", + "unusedPlatform": "'Piattaforma' non valida. Assicurati che sia scritta correttamente!", "nonExistingLibrary": "'Libreria' non è il nome di una libreria esistente! (Verrà utilizzata quella predefinita)", - "nonContentFolders": "Nessuna cartella di contenuto trovata nella cartella Curation. Dovrebbero esserci?", - "noTags": "Non ci sono etichette su questa curazione.", + "nonContentFolders": "Rilevate altre cartelle oltre alla cartella Content nella cartella del gioco che si sta curando. È previsto che ci siano?", + "noTags": "Non ci sono etichette per questo gioco curato.", "noSource": "'Fonte' è un campo obbligatorio.", - "unenteredTag": "Il campo di testo etichetta non è stato inviato.", - "noLogo": "Non c'è nessun logo su questa curazione.", - "noScreenshot": "Non c'è nessuna schermata su questa curazione.", + "unenteredTag": "Il campo di testo Etichette non è stato compilato.", + "noLogo": "Non c'è nessun logo per questo gioco curato.", + "noScreenshot": "Non c'è nessuna schermata per questo gioco curato.", "ilc_notHttp": "Usa HTTP.", - "ilc_nonExistant": "Punta ad un file esistente nella cartella 'contenuto' della tua curva.", - "sort": "Ordina Curazioni (A-Z)" + "ilc_nonExistant": "Punta ad un file esistente nella cartella 'content' del tuo gioco curato.", + "sort": "Ordina Giochi Curati (A-Z)" }, "playlist": { "enterDescriptionHere": "Inserisci qui una descrizione...", - "noDescription": "Nessuna descrizione", + "noDescription": "Nessuna Descrizione", "save": "Salva", - "saveDesc": "Salva le modifiche apportate e interrompi la modifica", + "saveDesc": "Salva le modifiche apportate e finisci di modificare", "discard": "Annulla", - "discardDesc": "Annulla le modifiche apportate e interrompe la modifica", + "discardDesc": "Annulla le modifiche apportate e finisci di modificare", "edit": "Modifica", - "editDesc": "Modifica questa playlist", + "editDesc": "Modifica questa scaletta", "delete": "Elimina", - "deleteDesc": "Elimina questa playlist", - "changeIcon": "Cambia L'Icona Della Playlist", - "duplicatePlaylistDesc": "Duplica playlist", - "exportPlaylistDesc": "Esporta scaletta in un file", - "areYouSure": "Ne sei sicuro?", - "noTitle": "Nessun titolo", + "deleteDesc": "Elimina questa scaletta", + "changeIcon": "Cambia l'Icona della Scaletta", + "duplicatePlaylistDesc": "Duplica questa scaletta", + "exportPlaylistDesc": "Esporta questa scaletta in un file", + "areYouSure": "Sei sicuro?", + "noTitle": "Nessun Titolo", "titlePlaceholder": "Titolo...", - "noAuthor": "Nessun autore", + "noAuthor": "Nessun Autore", "authorPlaceholder": "Autore...", "id": "ID", "by": "da", "extreme": "estremo" }, "misc": { - "noBlankFound": "Nessuna {0} trovata", + "noBlankFound": "Nessuna {0} Trovata", "addBlank": "Aggiungi {0}", - "deleteAllBlankImages": "Elimina TUTTI le immagini di {0} per questo gioco", + "deleteAllBlankImages": "Elimina TUTTE le immagini {0} per questo gioco", "yes": "Sì", "no": "No", - "downloading": "Download in corso", + "downloading": "Scaricamento in corso", "extracting": "Estrazione", "installingFiles": "Installazione File...", - "complete": "Complete", - "exportMetaEditTitle": "Esporta Meta Edit", - "exportMetaEditDesc": "Seleziona tutte le proprietà da esportare:" + "complete": "Completato", + "exportMetaEditTitle": "Esporta Modifiche dei Meta", + "exportMetaEditDesc": "Seleziona tutte le proprietà da esportare:", + "showImage": "Mostra Immagine", + "searching": "Ricerca..." }, "menu": { - "viewThumbnailInFolder": "Visualizza Anteprima nella cartella", - "viewScreenshotInFolder": "Visualizza Schermate nella cartella", - "openFileLocation": "Apri la cartella", - "addToPlaylist": "Aggiungi alla Playlist", - "duplicateMetaOnly": "Duplica (Solo Meta)", + "viewThumbnailInFolder": "Visualizza Anteprima nella Cartella", + "viewScreenshotInFolder": "Visualizza Schermata nella Cartella", + "openFileLocation": "Apri Posizione del File", + "addToPlaylist": "Aggiungi alla Scaletta", + "duplicateMetaOnly": "Duplica (Solo i Meta)", "duplicateMetaAndImages": "Duplica (Meta & Immagini)", - "copyGameUUID": "Copia UUUID Del Gioco", - "exportMetaOnly": "Esporta (Solo Meta)", - "exportMetaAndImages": "Esporta (Meta && immagini)", - "exportMetaEdit": "Esporta Meta Edit", - "duplicatePlaylist": "Duplica playlist", - "exportPlaylist": "Esporta Playlist" + "copyGameUUID": "Copia UUID del Gioco", + "exportMetaOnly": "Esporta (Solo i Meta)", + "exportMetaAndImages": "Esporta (Meta & Immagini)", + "exportMetaEdit": "Esporta Modifiche dei Meta", + "duplicatePlaylist": "Duplica questa Scaletta", + "exportPlaylist": "Esporta questa Scaletta" }, "dialog": { "programNotFound": "Programma non trovato!", - "phpNotFound": "PHP non è stato trovato sul percorso. È installato?\nPotresti incontrare un comportamento inatteso e non funzionante.", - "wineNotFound": "Wine non è installato, ma è necessario per avviare questo gioco.\nConsulta l'articolo del supporto wiki Linux per maggiori dettagli.", - "flashpointPathNotFound": "La cartella Flashpoint non è impostata o non è valida. Vuoi scegliere una cartella su cui installare ora?", + "phpNotFound": "PHP non è stato trovato nel percorso. È installato?\nPotresti incontrare comportamenti inattesi e malfunzionamenti senza di esso.", + "wineNotFound": "Wine non è installato, ma è necessario per avviare questo gioco.\nConsulta l'articolo del supporto della wiki Linux per maggiori dettagli.", + "flashpointPathNotFound": "La cartella Flashpoint non è impostata o non è valida. Vuoi scegliere una cartella in cui installare ora?", "fileNotFound": "File non trovato!", "flashpointPathInvalid": "Percorso Flashpoint non valido!", "pathNotFound": "Percorso non trovato!", - "playlistConflict": "Conflitto Playlist", - "importedPlaylistAlreadyExists": "La playlist importata esiste già con lo stesso nome! - '{0}'", - "importPlaylistAs": "Importa playlist come", - "selectFileToExportMeta": "Seleziona il file su cui salvare meta", + "playlistConflict": "Conflitto fra Scalette", + "importedPlaylistAlreadyExists": "Esiste già una scaletta con lo stesso nome di quella importata! - '{0}'", + "importPlaylistAs": "Importa scaletta come", + "selectFileToExportMeta": "Seleziona il file in cui salvare i meta", "selectFolderToExportMetaAndImages": "Seleziona la cartella in cui salvare meta e immagini", "replaceFilesQuestion": "Sostituire i file?", - "exportedAlreadyExistsYesNo": "Uno o più dei file esportati esiste già.\n Sostituirli?", - "selectFolder": "Seleziona cartella", + "exportedAlreadyExistsYesNo": "Uno o più file esportati esistono già.\n Sostituirli?", + "selectFolder": "Seleziona Cartella", "selectScreenshot": "Seleziona Schermata", - "selectThumbnail": "Seleziona anteprima", - "selectCurationFolder": "Seleziona Cartella(e) Curatela", - "selectCurationArchive": "Seleziona archivio curatore(i)", - "selectCurationMeta": "Seleziona curatore(i) Meta(i)", - "selectPlaylistToImport": "Seleziona scaletta da importare", - "selectFileToExportPlaylist": "Seleziona il file su cui salvare la scaletta", - "dataRequired": "Ulteriori dati richiesti", - "dataRequiredDesc": "È necessario installare l'aggiornamento sulla schermata iniziale prima di poter utilizzare il launcher.\n\nDesideri farlo ora?", - "upgradeWillInstallTo": "{0} sarà installato su", + "selectThumbnail": "Seleziona Anteprima", + "selectCurationFolder": "Seleziona una o più Cartelle di Giochi Curati", + "selectCurationArchive": "Seleziona uno o più Archivi di Giochi Curati", + "selectCurationMeta": "Seleziona uno o più Metadati di Giochi Curati", + "selectPlaylistToImport": "Seleziona la Scaletta da Importare", + "selectFileToExportPlaylist": "Seleziona il file in cui salvare la Scaletta", + "dataRequired": "Sono Richiesti Ulteriori Dati", + "dataRequiredDesc": "È necessario installare l'aggiornamento nella schermata iniziale prima di poter utilizzare il launcher.\n\nDesideri farlo ora?", + "upgradeWillInstallTo": "{0} sarà installato in", "verifyPathSelection": "Anche i futuri aggiornamenti verranno installati qui. È corretto?", - "restartNow": "Riavviare ora?", + "restartNow": "Riavviare Ora?", "restartToApplyUpgrade": "Questo aggiornamento non sarà applicato fino al riavvio.\nDesideri farlo ora?", "areYouSure": "Sei sicuro?", "areYouSureDelete": "Sei sicuro di voler eliminare questa voce?", - "areYouSurePlaylistRemove": "Sei sicuro di voler rimuovere la voce dalla playlist?", + "areYouSurePlaylistRemove": "Sei sicuro di voler rimuovere questa voce dalla scaletta?", "cancel": "Annulla", - "mergePlaylists": "Unisci le playlist", - "newPlaylist": "Resta Separato", - "uploadPrivacyWarning": "Questo renderà i tuoi log disponibili pubblicamente.\nUn link sarà copiato negli appunti.\n\nSei sicuro?", + "mergePlaylists": "Unisci le Scalette", + "newPlaylist": "Lascia Separata", + "uploadPrivacyWarning": "Questo renderà i tuoi Registri disponibili pubblicamente.\nUn collegamento sarà copiato nei tuoi appunti.\n\nSei sicuro?", "overwriteFileTitle": "Il file esiste già!", "overwriteFileMessage": "Un file con lo stesso nome esiste già, desideri sovrascriverlo?", "overwriteFileDetail": "Percorso del file:", - "deleteTagFilterGroup": "Eliminare questo gruppo di filtri etichette?", - "deleteCuration": "Eliminare questa Scelta?", - "importCuration": "Importare questa Curazione?", - "deleteGameImage": "Eliminare questa immagine di gioco?", - "deletePlaylist": "Eliminare questa scaletta?", - "importAllCurations": "Importare tutte le Selezioni?", - "deleteAllCurations": "Eliminare tutte le Selezioni?", - "removePlaylistGame": "Rimuovere questo gioco dalla scaletta?", - "deleteGame": "Eliminare questo gioco? Questo non può essere annullato.", - "deleteGameData": "Eliminare questi dati di gioco? Questo non può essere annullato.", - "deleteAddApp": "Eliminare questa aggiunta di app?", - "deleteTagCategory": "Eliminare questa etichetta di categoria?", - "deleteTag": "Eliminare questa etichetta?", - "deleteTagAlias": "Eliminare questo Alias etichetta?", - "deleteSource": "Eliminare questa Sorgente?", - "uninstallGame": "Disinstallare questo gioco?", + "deleteTagFilterGroup": "Eliminare questo Filtro per Gruppo di Etichette?", + "deleteCuration": "Eliminare questo Gioco Curato?", + "importCuration": "Importare questo Gioco Curato?", + "deleteGameImage": "Eliminare questa Immagine del Gioco?", + "deletePlaylist": "Eliminare questa Scaletta?", + "importAllCurations": "Importare tutti i Giochi Curati?", + "deleteAllCurations": "Eliminare tutti i Giochi Curati?", + "removePlaylistGame": "Rimuovere questo Gioco dalla Scaletta?", + "deleteGame": "Eliminare questo Gioco? Questa azione non può essere annullata.", + "deleteGameData": "Eliminare questi Dati di Gioco? Questa azione non può essere annullata.", + "deleteAddApp": "Eliminare questa applicazione aggiuntiva?", + "deleteTagCategory": "Eliminare questa Categoria di Etichette?", + "deleteTag": "Eliminare questa Etichetta?", + "deleteTagAlias": "Eliminare questo Alias di Etichetta?", + "deleteSource": "Eliminare questa Fonte?", + "uninstallGame": "Disinstallare questo Gioco?", "unableToUninstallGameData": "Impossibile eliminare il pacchetto dati. È in uso? Prova a riavviare il launcher.", - "unableToDeleteGame": "Impossibile eliminare la partita. È in uso? Prova a riavviare il launcher.", + "unableToDeleteGame": "Impossibile eliminare il gioco. È in uso? Prova a riavviare il launcher.", "downloadingGame": "Scaricamento Gioco...", "verifyingGame": "Verifica Gioco...", - "aFewMinutes": "Questo può richiedere alcuni minuti." + "aFewMinutes": "Può richiedere alcuni minuti." }, "libraries": { "arcade": "Giochi", + "arcadeSingular": "Partita", "arcadePlural": "Tutti i Giochi", "theatre": "Animazioni", + "theatreSingular": "Animazione", "theatrePlural": "Tutte le Animazioni", "auditorium": "NG Auditorium", - "auditoriumPlural": "Tutte Le Animazioni Ng" + "auditoriumSingular": "Animazione", + "auditoriumPlural": "Tutte le Animazioni NG" }, "upgrades": { "infinity": "Flashpoint Infinity", "infinityDesc": "File richiesti. Aggiunge il supporto per i giochi Flash.", - "tech": "Altre tecnologie", - "techDesc": "Aggiunge supporto per tutte le altre tecnologie - Shockwave, Unità, Java, HTML5, ecc.", - "screenshots": "Loghi e schermate", - "screenshotsDesc": "Aggiunge loghi per la visualizzazione Grid e screenshot per tutti i giochi." + "tech": "Altre Tecnologie", + "techDesc": "Aggiunge supporto per tutte le altre tecnologie - Shockwave, Unity, Java, HTML5, ecc.", + "screenshots": "Loghi & Schermate", + "screenshotsDesc": "Aggiunge loghi per la visualizzazione a Griglia e schermate per tutti i giochi." } -} \ No newline at end of file +} diff --git a/lang/ja-JP.json b/lang/ja-JP.json index f27128378..128dafae6 100644 --- a/lang/ja-JP.json +++ b/lang/ja-JP.json @@ -1,11 +1,19 @@ { - "name": "英語", + "name": "日本語", "config": { "configHeader": "設定", "configDesc": "(一部の変更を有効にするには、”変更を保存して再起動”を押す必要があります)", "preferencesHeader": "基本設定", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "カスタムを有効にする", "enableEditingDesc": "ゲームや追加のアプリケーションの編集を有効にします。編集に関連するタブも表示されます。", "onDemandImages": "オンデマンド画像", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "設定言語の翻訳が未完成であった場合に使用する言語", "auto": "自動 ({0})", "none": "なし", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "フラッシュポイント", "flashpointPath": "Flashpointのパス", "flashpointPathDesc": "Flashpointフォルダのパス (相対パスでよい)", @@ -79,6 +88,7 @@ "helpInfo": "ヘルプが必要ですか?: {0}", "help": "マニュアルを読む", "upgradesHeader": "アップグレード", + "updateFeedHeader": "News Feed", "installComplete": "インストール完了", "alreadyInstalled": "既にインストールされています", "download": "ダウンロード", @@ -103,7 +113,8 @@ "clearLog": "ログを消去", "copy404Urls": "404 URLをコピー", "uploadLog": "ログをアップロード", - "copiedToClipboard": "クリップボードにコピーしました" + "copiedToClipboard": "クリップボードにコピーしました", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "ホーム", @@ -262,11 +273,11 @@ "installed": "Installed", "notInstalled": "Not Installed", "legacyGame": "Legacy Game", - "download": "Download", + "download": "ダウンロード", "thumbnail": "サムネイル", "screenshot": "スクリーンショット", "dropImageHere": "追加する画像をここにドロップしてください。", - "noGameSelected": "ゲームが選択されていません", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "それを選択するゲームをクリックします。", "deleteAdditionalApplication": "追加したアプリケーションを削除します。", "deleteGameAndAdditionalApps": "ゲーム (と追加したアプリケーション) を削除します。", @@ -288,7 +299,10 @@ "saveAndRestart": "変更を保存して再起動", "thereAreNoGames": "ゲームはありません。", "noGameMatchedDesc": "検索条件に一致するゲームはありません。", - "noGameMatchedSearch": "制限の少ないものを探してみてください。" + "noGameMatchedSearch": "制限の少ないものを探してみてください。", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "名前", @@ -413,7 +427,9 @@ "installingFiles": "ファイルをインストール中...", "complete": "完了", "exportMetaEditTitle": "メタ編集をエクスポート", - "exportMetaEditDesc": "エクスポートするすべてのプロパティを選択:" + "exportMetaEditDesc": "エクスポートするすべてのプロパティを選択:", + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "サムネイルフォルダーを表示します", @@ -486,16 +502,19 @@ "uninstallGame": "Uninstall this Game?", "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", "unableToDeleteGame": "Unable to delete game. Is it in use? Try restarting the launcher.", - "downloadingGame": "Downloading Game...", + "downloadingGame": "ゲームをダウンロード中...", "verifyingGame": "Verifying Game...", "aFewMinutes": "This may take a few minutes." }, "libraries": { "arcade": "ゲーム", + "arcadeSingular": "Game", "arcadePlural": "すべてのゲーム", "theatre": "アニメーション", + "theatreSingular": "Animation", "theatrePlural": "すべてのアニメーション", "auditorium": "NG講堂(講堂)", + "auditoriumSingular": "Animation", "auditoriumPlural": "すべてのNGアニメーション" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "ロゴ&スクリーンショット", "screenshotsDesc": "すべてのゲームにグリットビューでのロゴとスクリーンショットを追加します。" } -} \ No newline at end of file +} diff --git a/lang/mk-MK.json b/lang/mk-MK.json index dee996c17..7487a0e20 100644 --- a/lang/mk-MK.json +++ b/lang/mk-MK.json @@ -4,8 +4,16 @@ "configHeader": "Конфиг", "configDesc": "(Мора да притиснеш 'Зачувај и Рестарт' за да стапат некои промени)", "preferencesHeader": "Нагодувања", - "extremeGames": "Екстремни игри", - "extremeGamesDesc": "Покажете игри со сексуална, насилна или друга содржина несоодветна за деца.", + "extremeGames": "Покажи Екстремни Филтри", + "extremeGamesDesc": "Дозволува екстремните филтри за ознаки да се менуваат, креираат и менуваат за да ја контролираат содржината што е несоодветна за деца.", + "hideExtremeScreenshots": "Криј Екстремни Слики од екранот", + "hideExtremeScreenshotsDesc": "Сокрива слики од екранот на екстремно обележана содржина, може да се покажува со клик на полето за слики.", + "fancyAnimations": "Фенси Анимации", + "fancyAnimationsDesc": "Овозможи фенси анимации во лаунчерот.", + "searchLimit": "Ограничи Пребарување", + "searchLimitDesc": "Ограничете го бројот на вратени резултати при секое пребарување", + "searchLimitUnlimited": "Неограничено", + "searchLimitValue": "{0} Резултати", "enableEditing": "Овозможи Уредувања", "enableEditingDesc": "Овозможи уредување на игри и дополнителни апликации. Исто така, покажува јазичиња поврзани со уредување.", "onDemandImages": "Слики на барање", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Кој јазик да се користи наместо нецелосен тековен јазик.", "auto": "Аутоматско ({0})", "none": "Ништо", + "contentFiltersHeader": "Содржински Филтри", "flashpointHeader": "Flashpoint", "flashpointPath": "Патека на Flashpoint", "flashpointPathDesc": "Патека до папката на Flashpoint (може да биде релативна)", @@ -79,6 +88,7 @@ "helpInfo": "Треба помош? {0}.", "help": "Читај го упаството", "upgradesHeader": "Ажурирања", + "updateFeedHeader": "Новости", "installComplete": "Инсталацијата е завршена", "alreadyInstalled": "Веќе инсталирано", "download": "Превземање", @@ -94,7 +104,7 @@ "linuxSupport": "Проблеми во Linux? {0}", "linuxSupportLinkText": "Отворен FAQ", "randomPicks": "Случајни Избори", - "rerollPicks": "Re-Roll Picks" + "rerollPicks": "Пре-Врти Избори" }, "logs": { "filters": "Филтри", @@ -103,7 +113,8 @@ "clearLog": "Исчисти Лог", "copy404Urls": "Копирај 404 URLs", "uploadLog": "Прикачи Лог", - "copiedToClipboard": "Копирано во Kлипборд" + "copiedToClipboard": "Копирано во Kлипборд", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Почеток", @@ -266,7 +277,7 @@ "thumbnail": "Лого", "screenshot": "Слика од екранот", "dropImageHere": "Постави слика за да ја додадеш", - "noGameSelected": "Нема селектирана игра", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Кликни на игра за да ја селектираш.", "deleteAdditionalApplication": "Избриши Додатната Апликација", "deleteGameAndAdditionalApps": "Избриши игра(и додатните апликации)", @@ -288,7 +299,10 @@ "saveAndRestart": "Зачувај и Рестарт", "thereAreNoGames": "Тука нема игри.", "noGameMatchedDesc": "Ниту еден наслов на играта не одговара на вашето пребарување.", - "noGameMatchedSearch": "Обидете се да побарате нешто помалку ограничувачко." + "noGameMatchedSearch": "Обидете се да побарате нешто помалку ограничувачко.", + "mountParameters": "Моунт Параметри", + "noMountParameters": "Нема Моунт Параметри", + "showExtremeScreenshot": "Покажувај Eкстремни Cлики од Eкраноt" }, "tags": { "name": "Име", @@ -413,7 +427,9 @@ "installingFiles": "Инсталирање Фајлови...", "complete": "Завршено", "exportMetaEditTitle": "Изведувај Meta Edit", - "exportMetaEditDesc": "Изберете ги сите својства за извоз:" + "exportMetaEditDesc": "Изберете ги сите својства за извоз:", + "showImage": "Покажи Слика", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Прегледај лого во папката", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Игри", + "arcadeSingular": "Game", "arcadePlural": "Сите Игри", "theatre": "Анимации", + "theatreSingular": "Animation", "theatrePlural": "Сите Анимации", "auditorium": "НГ Аудиториум", + "auditoriumSingular": "Animation", "auditoriumPlural": "Сите НГ Анимации" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Логоа и слики од екранот", "screenshotsDesc": "Додава логоа за преглед на мрежата и слики од екранот за сите игри." } -} \ No newline at end of file +} diff --git a/lang/nl-NL.json b/lang/nl-NL.json index 9a035aaa5..38188424a 100644 --- a/lang/nl-NL.json +++ b/lang/nl-NL.json @@ -4,8 +4,16 @@ "configHeader": "Instellingen", "configDesc": "(Druk op 'Opslaan & Herstarten om enkele wijzigingen door te voeren)", "preferencesHeader": "Voorkeuren", - "extremeGames": "Extreme inhoud", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Toon Extreme Filters", + "extremeGamesDesc": "Maakt het mogelijk om Extreme tag-filters aan te zetten, te maken en aan te passen aan inhoud die niet geschikt is voor kinderen.", + "hideExtremeScreenshots": "Extreme Screenshots Verbergen", + "hideExtremeScreenshotsDesc": "Verbergt screenshots van Extreme-gelabelde inhoud, kan niet worden verborgen met een klik op de afbeeldingsbox.", + "fancyAnimations": "Mooie Animaties", + "fancyAnimationsDesc": "Inschakelen van mooie animaties in de launcher.", + "searchLimit": "Zoek Limiet", + "searchLimitDesc": "Beperk het aantal resultaten dat bij elke zoekopdracht wordt verkregen", + "searchLimitUnlimited": "Onbeperkt", + "searchLimitValue": "{0} Resultaten", "enableEditing": "Bewerken inschakelen", "enableEditingDesc": "Bewerken inschakelen voor spellen en extra applicaties. Toont ook de gerelateerde tabbladen voor het bewerken.", "onDemandImages": "Aangevraagde afbeeldingen", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Verandert de taal naar jouw alternatieve taal als er onvolledige taalpakketen zijn.", "auto": "Automatisch ({0})", "none": "Geen", + "contentFiltersHeader": "Inhoud Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Flashpoint pad", "flashpointPathDesc": "Pad naar de Flashpoint map (kan relatief zijn)", @@ -23,15 +32,15 @@ "libraries": "Bibliotheken", "randomLibraries": "Willekeurige bibliotheken", "randomLibrariesDesc": "Bibliotheken voor het gebruik van de willekeurige keuzes op de startpagina.", - "updateSource": "Bronnen aan het updaten", + "updateSource": "Bron bijwerken", "platforms": "Platformen", "nativePlatforms": "Oorspronkelijke platformen", "nativePlatformsDesc": "Gebruik originele versies van deze platformen. (Indien niet beschikbaar zal het 'Wine' gebruiken)", - "tagFilterGroups": "Tag Filter Groups", - "tagFilterGroupsDesc": "These groups will filter suggestions and games out of search results", - "editTagFilter": "Edit Tag Filter Group", - "duplicateTagFilter": "Duplicate Tag Filter Group", - "deleteTagFilter": "Delete Tag Filter Group", + "tagFilterGroups": "Label filtergroepen", + "tagFilterGroupsDesc": "Deze groepen zullen suggesties en spellen filteren uit de zoekresultaten", + "editTagFilter": "Labelfilter groep bewerken", + "duplicateTagFilter": "Dubbele labelfiltersgroep", + "deleteTagFilter": "Labelfiltersgroep verwijderen", "appPathOverrides": "App Pad overschrijvingen", "appPathOverridesDesc": "Overschrijft het toepassingstraject links met degene aan de rechterkant bij het starten van games.", "visualsHeader": "Uiterlijk", @@ -58,7 +67,7 @@ "extApplications": "Applicatie(s)", "saveAndRestart": "Opslaan en herstarten", "saveAndClose": "Opslaan en Afsluiten", - "tagFilterGroupEditor": "Tag Filter Group Editor" + "tagFilterGroupEditor": "Label filtergroep bewerker" }, "home": { "updateHeader": "Launcher Update", @@ -79,6 +88,7 @@ "helpInfo": "Hulp nodig? {0}.", "help": "Lees de handleiding", "upgradesHeader": "Upgraden", + "updateFeedHeader": "Nieuwsfeed", "installComplete": "Installatie voltooid", "alreadyInstalled": "Reeds geïnstalleerd", "download": "Downloaden", @@ -103,7 +113,8 @@ "clearLog": "Wis de logs", "copy404Urls": "Kopieer 404 URL's", "uploadLog": "Uploaden van logs", - "copiedToClipboard": "Gekopieerd naar klembord" + "copiedToClipboard": "Gekopieerd naar klembord", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Thuis", @@ -176,10 +187,10 @@ "exportTagsDesc": "Maakt een bestand met tags en tag categorieën die kunnen worden gebruikt door 'Labels importeren'", "importTags": "Labels importeren", "importTagsDesc": "Labels importeren van een bestand gemaakt door 'Labels exporteren'", - "migrateExtremeGames": "Migrate Extreme Games", - "migrateExtremeGamesDesc": "Removes 'Extreme' from any games and assigns the 'LEGACY-Extreme' tag to them", - "massImportGameData": "Mass Import Game Data", - "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", + "migrateExtremeGames": "Migreer Extreme Games", + "migrateExtremeGamesDesc": "Verwijdert 'Extreme' uit alle spellen en wijst de 'LEGACY-Extreme' label toe aan hen", + "massImportGameData": "Groepsgewijs Spelgegevens importeren", + "massImportGameDataDesc": "Massa importeren van Spelgegevenspakketten, probeert hun bestandsnaam te koppelen aan een spellen UUID.", "servicesHeader": "Achtergrondservices", "servicesMissing": "Er zijn geen services.", "running": "Actief", @@ -258,15 +269,15 @@ "searching": "Bezig met zoeken...", "library": "Bibliotheek", "defaultLibrary": "Standaard bibliotheek", - "uninstallGame": "Uninstall Game", - "installed": "Installed", - "notInstalled": "Not Installed", - "legacyGame": "Legacy Game", - "download": "Download", + "uninstallGame": "Verwijder spel", + "installed": "Geïnstalleerd", + "notInstalled": "Niet geïnstalleerd", + "legacyGame": "Legacy Spel", + "download": "Downloaden", "thumbnail": "Miniatuur", "screenshot": "Schermafbeelding", "dropImageHere": "Plaats hier een afbeelding om hem toe te voegen", - "noGameSelected": "Geen spel geselecteerd", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Klik op een spel om het te selecteren.", "deleteAdditionalApplication": "Extra toepassingen verwijderen", "deleteGameAndAdditionalApps": "Spel verwijderen (en zijn extra toepassingen)", @@ -288,7 +299,10 @@ "saveAndRestart": "Opslaan en herstarten", "thereAreNoGames": "Er zijn geen spellen.", "noGameMatchedDesc": "Er is geen speltitel die overeenkomt met uw zoekopdracht.", - "noGameMatchedSearch": "Probeer naar iets gemakkelijker te zoeken." + "noGameMatchedSearch": "Probeer naar iets gemakkelijker te zoeken.", + "mountParameters": "Koppel Parameters", + "noMountParameters": "Geen Koppel Parameters", + "showExtremeScreenshot": "Extreme Schermafbeelding Weergeven" }, "tags": { "name": "Naam", @@ -321,7 +335,7 @@ "saveImportedCurations": "Sla geïmporteerde curaties op", "keepArchiveKey": "Houd Curatie-Archief UUID", "symlinkCurationContent": "Symlink inhoud map voor curatie (Admin aanvragingen in Windows, vereist voor MAD4FP)", - "useTagFilters": "Use Tag Filters in Suggestions", + "useTagFilters": "Gebruik Tag Filters in Suggesties", "openCurationsFolder": "Curatie map", "openCurationsFolderDesc": "Open de curatiesmap in de verkenner", "openExportsFolder": "Geëxporteerde curaties", @@ -336,8 +350,8 @@ "loadArchiveDesc": "Laadt één of meer curatie-archieven", "loadFolder": "Laadt map", "loadFolderDesc": "Laadt één of meer curatie-mappen", - "scanNewCurationFolders": "Scan For New Curations", - "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", + "scanNewCurationFolders": "Scan voor nieuwe curaties", + "scanNewCurationFoldersDesc": "Scan voor curaties die zijn toegevoegd na de eerste scan", "noCurations": "Geen curaties", "id": "Curatie-map", "heading": "Kop", @@ -370,14 +384,14 @@ "unusedPlatform": "'Platform' heeft een ongebruikte waarde. Zorg ervoor dat het correct gespeld is!", "nonExistingLibrary": "'Bibliotheek' is niet de naam van een bestaande bibliotheek! (Standaard wordt gebruikt)", "nonContentFolders": "Niet-inhoudsmappen gevonden in curatie folder. Mogen ze er feitelijk wel zijn?", - "noTags": "There are no tags on this curation.", - "noSource": "'Source' is a required field.", - "unenteredTag": "The Tags text field hasn't been submitted.", - "noLogo": "There is no logo on this curation.", - "noScreenshot": "There is no screenshot on this curation.", + "noTags": "Deze curatie heeft geen labels.", + "noSource": "'Bron' is een verplicht veld.", + "unenteredTag": "Het veld 'Label' is niet ingediend.", + "noLogo": "Deze curatie heeft geen logo.", + "noScreenshot": "Deze curatie heeft geen schermafbeelding.", "ilc_notHttp": "Gebruik HTTP.", "ilc_nonExistant": "Richt een bestaand bestand naar de curatie 'inhouds'-map.", - "sort": "Sort Curations (A-Z)" + "sort": "Curaties sorteren (A-Z)" }, "playlist": { "enterDescriptionHere": "Voer hier een beschrijving in...", @@ -413,13 +427,15 @@ "installingFiles": "Bestanden installeren...", "complete": "Voltooid", "exportMetaEditTitle": "Exporteren van Metadata wijziging", - "exportMetaEditDesc": "Selecteer alle eigenschappen om te exporteren:" + "exportMetaEditDesc": "Selecteer alle eigenschappen om te exporteren:", + "showImage": "Afbeelding Weergeven", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Miniatuur in map bekijken", "viewScreenshotInFolder": "Schermafbeelding in map bekijken", "openFileLocation": "Open bestandslocatie", - "addToPlaylist": "Add to Playlist", + "addToPlaylist": "Toevoegen aan afspeellijst", "duplicateMetaOnly": "Dupliceren (Metadata alleen)", "duplicateMetaAndImages": "Dupliceren (Metadata && Afbeeldingen)", "copyGameUUID": "Kopieer Spel-ID (UUID)", @@ -459,43 +475,46 @@ "restartNow": "Wil je herstarten?", "restartToApplyUpgrade": "Deze upgrade wordt pas toegepast nadat u opnieuw bent opgestart.\nWilt u dit nu doen?", "areYouSure": "Weet je het zeker?", - "areYouSureDelete": "Are you sure you want to delete this entry?", - "areYouSurePlaylistRemove": "Are you sure you want to remove this entry from the playlist?", + "areYouSureDelete": "Weet u zeker dat u dit item wilt verwijderen?", + "areYouSurePlaylistRemove": "Weet je zeker dat je dit item uit de afspeellijst wilt verwijderen?", "cancel": "Annuleren", "mergePlaylists": "Afspeellijsten samenvoegen", - "newPlaylist": "Stay Separate", - "uploadPrivacyWarning": "This will make your Logs available publicly.\nA link will be copied to your clipboard.\n\nAre you sure?", + "newPlaylist": "Blijf gescheiden", + "uploadPrivacyWarning": "Hiermee worden uw Logboeken publiekelijk beschikbaar gemaakt.\nEen link wordt gekopieerd naar uw klembord.\n\nWeet u het zeker?", "overwriteFileTitle": "Dit bestand bestaat al!", "overwriteFileMessage": "Er bestaat al een bestand met dezelfde naam. Wil je het overschrijven?", "overwriteFileDetail": "Bestandspad:", - "deleteTagFilterGroup": "Delete this Tag Filter Group?", - "deleteCuration": "Delete this Curation?", - "importCuration": "Import this Curation?", - "deleteGameImage": "Delete this Game Image?", - "deletePlaylist": "Delete this Playlist?", - "importAllCurations": "Import all Curations?", - "deleteAllCurations": "Delete all Curations?", - "removePlaylistGame": "Remove this Game from the Playlist?", - "deleteGame": "Delete this Game? This cannot be undone.", - "deleteGameData": "Delete this Game Data? This cannot be undone.", - "deleteAddApp": "Delete this Add App?", - "deleteTagCategory": "Delete this Tag Category?", - "deleteTag": "Delete this Tag?", - "deleteTagAlias": "Delete this Tag Alias?", - "deleteSource": "Delete this Source?", + "deleteTagFilterGroup": "Deze labelfiltersgroep verwijderen?", + "deleteCuration": "Deze curatie verwijderen?", + "importCuration": "Deze curatie importeren?", + "deleteGameImage": "Verwijder deze Spelafbeelding?", + "deletePlaylist": "Deze afspeellijst verwijderen?", + "importAllCurations": "Alle curaties importeren?", + "deleteAllCurations": "Alle curaties verwijderen?", + "removePlaylistGame": "Verwijder dit spel uit de afspeellijst?", + "deleteGame": "Verwijder dit spel? Dit kan niet ongedaan worden gemaakt.", + "deleteGameData": "Deze spelgegevens verwijderen? Dit kan niet ongedaan worden gemaakt.", + "deleteAddApp": "Verwijder de Toevoegde App?", + "deleteTagCategory": "Deze labelcategorie verwijderen?", + "deleteTag": "Dit label verwijderen?", + "deleteTagAlias": "Deze label-alias verwijderen?", + "deleteSource": "Deze bron verwijderen?", "uninstallGame": "Deze game verwijderen?", - "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", - "unableToDeleteGame": "Unable to delete game. Is it in use? Try restarting the launcher.", - "downloadingGame": "Downloading Game...", - "verifyingGame": "Verifying Game...", - "aFewMinutes": "This may take a few minutes." + "unableToUninstallGameData": "Datapakket kan niet verwijderd worden. Is het in gebruik? Probeer het programma opnieuw te starten.", + "unableToDeleteGame": "Kan het spel niet verwijderen. Is het in gebruik? Probeer het programma opnieuw te starten.", + "downloadingGame": "Spel downloaden...", + "verifyingGame": "Spel verifiëren...", + "aFewMinutes": "Dit kan enkele minuten duren." }, "libraries": { "arcade": "Spellen", + "arcadeSingular": "Game", "arcadePlural": "Alle spellen", "theatre": "Animaties", + "theatreSingular": "Animation", "theatrePlural": "Alle animaties", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Alle NG animaties" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logo's & schermafbeeldingen", "screenshotsDesc": "Voegt logo's voor Roosterweergave en schermafbeeldingen toe voor alle spellen." } -} \ No newline at end of file +} diff --git a/lang/pl-PL.json b/lang/pl-PL.json index 5f6f88177..122e3f201 100644 --- a/lang/pl-PL.json +++ b/lang/pl-PL.json @@ -2,10 +2,18 @@ "name": "Polski", "config": { "configHeader": "Konfiguracja", - "configDesc": "(By niektóre ze zmian zadziałały musisz kliknąć 'Zapisz i uruchom ponownie')", + "configDesc": "(By niektóre ze zmian zadziałały, musisz kliknąć 'Zapisz i uruchom ponownie')", "preferencesHeader": "Ustawienia", - "extremeGames": "Zawartość ekstremalna", - "extremeGamesDesc": "Pokaż gry ekstremalne (zawierające przemoc, seks lub inną zawartość nie nadającą się dla dzieci).", + "extremeGames": "Pokaż ekstremalne filtry", + "extremeGamesDesc": "Pozwala na przełączanie, tworzenie i modyfikowanie filtrów tagów ekstremalnych, aby kontrolować zawartość nieodpowiednią dla dzieci.", + "hideExtremeScreenshots": "Ukryj ekstremalne zrzuty ekranu", + "hideExtremeScreenshotsDesc": "Ukrywa zrzuty ekranu zawartości oznaczonej jako ekstremalna. Mogą być odsłonięte, klikając na pole obrazu.", + "fancyAnimations": "Ozdobne animacje", + "fancyAnimationsDesc": "Włącz ozdobne animacje w launcherze.", + "searchLimit": "Limit wyników", + "searchLimitDesc": "Ogranicz liczbę wyników zwracanych w dowolnym wyszukiwaniu", + "searchLimitUnlimited": "Nieograniczona", + "searchLimitValue": "{0} Wyników", "enableEditing": "Uruchom tryb edycji", "enableEditingDesc": "Pozwala na edytowanie gier oraz dodatkowych aplikacji. Pokazuje także zakładkę \"Dodaj do Flashpointa\".", "onDemandImages": "Logotypy i miniaturki na żądanie", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Język używany w przypadku nieprzetłumaczenia czegoś na pierwszy.", "auto": "Automatycznie ({0})", "none": "Brak", + "contentFiltersHeader": "Filtry treści", "flashpointHeader": "Flashpoint", "flashpointPath": "Ścieżka dostępu do Flashpointa", "flashpointPathDesc": "Ścieżka do folderu Flashpointa (może być względna)", @@ -26,13 +35,13 @@ "updateSource": "Aktualizuj źródło", "platforms": "Platformy", "nativePlatforms": "Natywne platformy", - "nativePlatformsDesc": "Użyj natywnej wersji tych platform. (Jeśli nie jest dostępne, zostanie użyte Wine)", - "tagFilterGroups": "Tag Filter Groups", - "tagFilterGroupsDesc": "These groups will filter suggestions and games out of search results", - "editTagFilter": "Edit Tag Filter Group", - "duplicateTagFilter": "Duplicate Tag Filter Group", - "deleteTagFilter": "Delete Tag Filter Group", - "appPathOverrides": "Zastąpienie Ścieżki Aplikacji", + "nativePlatformsDesc": "Użyj natywnych wersji tych platform. (Jeśli nie są dostępne, zostanie użyte Wine)", + "tagFilterGroups": "Grupy filtrów tagów", + "tagFilterGroupsDesc": "Grupy te będą filtrować sugestie i gry z wyników wyszukiwań", + "editTagFilter": "Edytuj grupę filtrów tagów", + "duplicateTagFilter": "Duplikuj grupę filtrów tagów", + "deleteTagFilter": "Usuń grupę filtrów tagów", + "appPathOverrides": "Zastąpienie ścieżki aplikacji", "appPathOverridesDesc": "Nadpisuje ścieżkę aplikacji po lewej stronie na ścieżkę znajdującą się po prawej stronie przy uruchamianiu gier.", "visualsHeader": "Wygląd", "useCustomTitleBar": "Użyj własnego paska tytułowego", @@ -47,7 +56,7 @@ "showDeveloperTab": "Pokaż zakładkę dla deweloperów", "showDeveloperTabDesc": "Pokaż zakładkę 'Dla deweloperów'. Najpewniej przyda się tylko deweloperom i kuratorom.", "server": "Serwer", - "serverDesc": "Który Proces Serwera uruchomić podczas uruchamiania launchera.", + "serverDesc": "Którego procesu serwera używać przy uruchamianiu launchera.", "metadataServerHost": "Host serwera metadanych", "metadataServerHostDesc": "Host serwera metadanych. Używany do synchronizacji nowych i zaktualizowanych informacji o grze w launcherze.", "extensionsHeader": "Rozszerzenia", @@ -58,7 +67,7 @@ "extApplications": "Aplikacja(-e)", "saveAndRestart": "Zapisz i uruchom ponownie", "saveAndClose": "Zapisz i zamknij", - "tagFilterGroupEditor": "Tag Filter Group Editor" + "tagFilterGroupEditor": "Edytor grup filtrów tagów" }, "home": { "updateHeader": "Aktualizacja launchera", @@ -79,6 +88,7 @@ "helpInfo": "Potrzebujesz pomocy? {0}.", "help": "Przeczytaj plik readme", "upgradesHeader": "Komponenty", + "updateFeedHeader": "Aktualności", "installComplete": "Instalacja zakończona", "alreadyInstalled": "Już zainstalowany", "download": "Pobierz", @@ -100,10 +110,11 @@ "filters": "Filtry", "logLevels": "Poziomy logów", "copyText": "Kopiuj tekst", - "clearLog": "Wyczyść logi", + "clearLog": "Wyczyść log", "copy404Urls": "Kopiuj adresy 404", "uploadLog": "Wyślij Log", - "copiedToClipboard": "Skopiowano do schowka" + "copiedToClipboard": "Skopiowano do schowka", + "openLogsWindow": "Otwórz okno dzienników" }, "app": { "home": "Strona główna", @@ -155,19 +166,19 @@ "checkMissingExecMappings": "Sprawdź brakujące mapowania plików wykonywalnych", "checkMissingExecMappingsDesc": "Pokaż wszystkie pliki wykonywalne dla win32, linux i darwin, które nie są zmapowane", "renameImagesTitleToId": "Zmień nazwy zdjęć (Tytuł => ID)", - "renameImagesTitleToIdDesc": "Znajdź wszystkie zdjęcia gier z ich tytułami w nazwie i zmień ich nazwy by było w nich ID", + "renameImagesTitleToIdDesc": "Znajdź wszystkie obrazy gry z jej tytułem w nazwie i zmień ich nazwy na jej ID", "renameImagesIdToTitle": "Zmień nazwy zdjęć (ID => Tytuł)", - "renameImagesIdToTitleDesc": "Znajdź wszystkie zdjęcia gier z ich ID w nazwie i zmień ich nazwy by była w nich nazwa gry", + "renameImagesIdToTitleDesc": "Znajdź wszystkie obrazy gry z jej ID w nazwie i zmień ich nazwy na jej tytuł", "createMissingFolders": "Utwórz brakujące foldery", - "createMissingFoldersDesc": "Znajdź wszystkie foldery których brakuje w strukturze Flashpointa i utwórz je", + "createMissingFoldersDesc": "Znajdź wszystkie foldery, których brakuje w strukturze Flashpointa i utwórz je", "importLegacyPlatforms": "Import starszych platform", "importLegacyPlatformsDesc": "Importuje starsze platformy z `Data/Platforms/` - UWAGA: Nadpisze konfliktujące gry", "importLegacyPlaylists": "Import starszych list odtwarzania", "importLegacyPlaylistsDesc": "Importuje starsze listy odtwarzania z `/Data/Playlists`", "deleteAllPlaylists": "Usuń wszystkie playlisty", "deleteAllPlaylistsDesc": "NIEBEZPIECZNE! Usuwa wszystkie playlisty.", - "importMetaEdits": "Importuj Edycje Metadanych", - "importMetaEditsDesc": "Importuje WSZYSTKIE Edycje Metadanych z `/Data/MetaEdits`", + "importMetaEdits": "Importuj edycje metadanych", + "importMetaEditsDesc": "Importuje WSZYSTKIE edycje metadanych z `/Data/MetaEdits`", "fixPrimaryAliases": "Napraw podstawowe aliasy", "fixPrimaryAliasesDesc": "Dodaj podstawowy alias do wszystkich tagów, w których tego brakuje", "fixCommaTags": "Napraw tagi z przecinkami", @@ -179,7 +190,7 @@ "migrateExtremeGames": "Przenieś gry ekstremalne", "migrateExtremeGamesDesc": "Usuwa oznaczenie \"ekstremalne\" z każdej gry i przypisuje im tag \"LEGACY-Extreme\"", "massImportGameData": "Masowy import danych gry", - "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", + "massImportGameDataDesc": "Masowo importuje pakiety z danymi gry, próbując dopasować ich nazwę do UUID gier.", "servicesHeader": "Usługi w tle", "servicesMissing": "Brak aktywnych usług.", "running": "Działające", @@ -192,20 +203,20 @@ "restart": "Uruchom ponownie", "restartDesc": "Uruchom usługę ponownie", "details": "Szczegóły", - "detailsDesc": "Otwórz okno ze szczegółami" + "detailsDesc": "Otwórz okno ze szczegółami usługi" }, "about": { "aboutHeader": "O programie", - "flashpoint": "BlueMaxima's Flashpoint", + "flashpoint": "Flashpoint autorstwa BlueMaxima", "flashpointDesc": "Projekt archiwizacyjny, dla którego został stworzony ten launcher. Jest to jednocześnie archiwum, muzeum i kolekcja grywalnych gier przeglądarkowych.", "website": "Strona internetowa", "flashpointLauncher": "Launcher Flashpoint", - "flashpointLauncherDesc": "Aplikacja o otwartym źródle służąca do przeglądania, zarządzania i grania w gry w projekcie Flashpoint.", + "flashpointLauncherDesc": "Aplikacja o otwartym kodzie źródłowym służąca do przeglądania, zarządzania i grania w gry w projekcie Flashpoint.", "version": "Wersja", "license": "Licencja", "licenseInfo": "MIT (Przeczytaj plik 'LICENSE' by dowiedzieć się więcej)", "creditsHeader": "Twórcy", - "specialThanks": "Specjalne Podziękowania" + "specialThanks": "Specjalne podziękowania" }, "browse": { "noTitle": "Brak tytułu", @@ -261,12 +272,12 @@ "uninstallGame": "Odinstaluj grę", "installed": "Zainstalowane", "notInstalled": "Nie zainstalowano", - "legacyGame": "Legacy Game", + "legacyGame": "Starsza gra", "download": "Pobierz", "thumbnail": "Miniaturka", "screenshot": "Zrzut ekranu", "dropImageHere": "Upuść obraz tutaj, by go dodać", - "noGameSelected": "Nie wybrano gry", + "noGameSelected": "Nie wybrano {0}", "clickToSelectGame": "Kliknij na grę by ją zaznaczyć.", "deleteAdditionalApplication": "Usuń dodatkowe aplikacje", "deleteGameAndAdditionalApps": "Usuń grę (i dodatkowe aplikacje)", @@ -281,14 +292,17 @@ "noGamesFound": "Nie znaleziono gier!", "dropGameOnLeft": "Upuść grę na playliście na {0} by ją dodać.", "leftSidebar": "lewy pasek boczny", - "setFlashpointPathQuestion": "Czy ustawiłeś {0} na stronie {1}?", + "setFlashpointPathQuestion": "Czy ustawiłeś ścieżkę do {0} na stronie {1}?", "flashpointPath": "Ścieżka dostępu do Flashpointa", "config": "Konfiguracja", - "noteSaveAndRestart": "Ważne: Musisz kliknąć {0} by zachować zmiany.", + "noteSaveAndRestart": "Ważne: Musisz kliknąć {0} by nałożyć zmiany.", "saveAndRestart": "Zapisz i uruchom ponownie", "thereAreNoGames": "Brak pasujących gier.", - "noGameMatchedDesc": "Żaden tytuł gry nie odpowiada twojemu zapytaniu.", - "noGameMatchedSearch": "Spróbuj użyć bardziej ogólnego zapytania." + "noGameMatchedDesc": "Żaden tytuł gry nie odpowiada twojemu wyszukiwaniu.", + "noGameMatchedSearch": "Spróbuj użyć bardziej ogólnego wyszukiwania.", + "mountParameters": "Parametry montowania", + "noMountParameters": "Brak parametrów montowania", + "showExtremeScreenshot": "Pokaż ekstremalny zrzut ekranu" }, "tags": { "name": "Nazwa", @@ -303,11 +317,11 @@ "editTag": "Edytuj tag", "color": "Kolor", "noTagSelected": "Nie zaznaczono tagu", - "clickToSelectTag": "Kliknij aby wybrać Tag", + "clickToSelectTag": "Kliknij, aby wybrać tag", "deleteTagAlias": "Usuń alias tagu", "setPrimaryAlias": "Ustaw podstawowy alias", "mergeIntoTag": "Połącz w Tag", - "mergeTag": "Połącz Tag", + "mergeTag": "Scal tag", "makeAliasWhenMerged": "Ustawić to jako alias tagu?", "deleteTag": "Usuń tag", "deleteTagCategory": "Usuń kategorię tagu", @@ -337,7 +351,7 @@ "loadFolder": "Załaduj folder", "loadFolderDesc": "Załaduj jeden lub więcej folderów archiwizowanych gier", "scanNewCurationFolders": "Skanuj w poszukiwaniu nowych kuracji", - "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", + "scanNewCurationFoldersDesc": "Skanuje pod kątem kuracji, które zostały dodane po początkowym skanowaniu", "noCurations": "Brak archiwizowanych gier", "id": "Folder archiwizowanej gry", "heading": "Nagłówek", @@ -369,10 +383,10 @@ "unusedTags": "Następujące tagi nie są obecne w żadnej innej grze. Czy są poprawne?", "unusedPlatform": "'Platforma' ma niewykorzystywaną wartość. Upewnij się że jest poprawnie wpisana!", "nonExistingLibrary": "'Biblioteka' nie jest nazwą istniejącej biblioteki! (Zostanie użyta domyślna wartość)", - "nonContentFolders": "Subfolder inny niż 'content' znajduje się w folderze archiwizowanej gry. Czy powinien tam być?", + "nonContentFolders": "Foldery bez treści znalezione w folderze kuracji. Czy powinny tam być?", "noTags": "Brakuje tagów w tej kuracji.", "noSource": "\"Źródło\" jest wymaganym polem.", - "unenteredTag": "The Tags text field hasn't been submitted.", + "unenteredTag": "Pole tekstowe tagów nie zostało wysłane.", "noLogo": "W tej kuracji brakuje loga.", "noScreenshot": "Brakuje zrzutu ekranu w tej kuracji.", "ilc_notHttp": "Użyj HTTP.", @@ -385,7 +399,7 @@ "save": "Zapisz", "saveDesc": "Zapisz zmiany i skończ edycję", "discard": "Anuluj", - "discardDesc": "Anuluj zmiany i skończ edycję", + "discardDesc": "Anuluj zmiany i zakończ edycję", "edit": "Edytuj", "editDesc": "Edytuj tę listę odtwarzania", "delete": "Usuń", @@ -413,7 +427,9 @@ "installingFiles": "Instalowanie plików...", "complete": "Ukończono", "exportMetaEditTitle": "Eksportuj edycję metadanych", - "exportMetaEditDesc": "Wybierz wszystkie edycje metadanych do wyeksportowania:" + "exportMetaEditDesc": "Wybierz wszystkie edycje metadanych do wyeksportowania:", + "showImage": "Pokaż zdjęcie", + "searching": "Wyszukiwanie..." }, "menu": { "viewThumbnailInFolder": "Zobacz miniaturki w folderze", @@ -442,8 +458,8 @@ "importPlaylistAs": "Importuj playlistę jako", "selectFileToExportMeta": "Wybierz plik w którym zapisać metadane", "selectFolderToExportMetaAndImages": "Wybierz folder w którym zapisać metadane i zdjęcia", - "replaceFilesQuestion": "Zastąpić pliki?", - "exportedAlreadyExistsYesNo": "Jeden lub więcej eksportowanych plików już istnieje.\nCzy chcesz je zastąpić?", + "replaceFilesQuestion": "Zamienić pliki?", + "exportedAlreadyExistsYesNo": "Jeden lub więcej eksportowanych plików już istnieje.\nCzy chcesz je zamienić?", "selectFolder": "Wybierz folder", "selectScreenshot": "Wybierz zrzut ekranu", "selectThumbnail": "Wybierz miniaturkę", @@ -453,38 +469,38 @@ "selectPlaylistToImport": "Wybierz playlistę do zaimportowania", "selectFileToExportPlaylist": "Wybierz plik, w którym chcesz zapisać playlistę", "dataRequired": "Wymagane dodatkowe dane", - "dataRequiredDesc": "Musisz zainstalować dodatek na ekranie startowym zanim będziesz w stanie używać Launchera.\n\nCzy chcesz zrobić to teraz?", + "dataRequiredDesc": "Musisz zainstalować dodatek na ekranie startowym, zanim będziesz w stanie używać Launchera.\n\nCzy chcesz zrobić to teraz?", "upgradeWillInstallTo": "{0} zostanie zainstalowane w", "verifyPathSelection": "Tutaj również zostaną zainstalowane przyszłe aktualizacje. Czy to jest poprawne?", "restartNow": "Uruchomić ponownie?", - "restartToApplyUpgrade": "Ta aktualizacja nie zostanie zastosowana dopóki nie zrestartujesz.\nCzy chcesz to zrobić teraz?", - "areYouSure": "Jesteś pewien?", - "areYouSureDelete": "Are you sure you want to delete this entry?", - "areYouSurePlaylistRemove": "Are you sure you want to remove this entry from the playlist?", + "restartToApplyUpgrade": "Ta aktualizacja nie zostanie zastosowana, dopóki nie uruchomisz ponownie.\nCzy chcesz to zrobić teraz?", + "areYouSure": "Czy na pewno?", + "areYouSureDelete": "Czy na pewno chcesz usunąć tę pozycję?", + "areYouSurePlaylistRemove": "Czy na pewno chcesz usunąć tę pozycję z playlisty?", "cancel": "Anuluj", - "mergePlaylists": "Połącz playlisty", + "mergePlaylists": "Scal playlisty", "newPlaylist": "Zostaw osobno", "uploadPrivacyWarning": "Twoje logi będą publicznie dostępne.\nLink zostanie skopiowany do schowka.\n\nJesteś pewien?", "overwriteFileTitle": "Plik już istnieje!", "overwriteFileMessage": "Plik z taką samą nazwą już istnieje. Czy chcesz go nadpisać?", "overwriteFileDetail": "Ścieżka pliku:", - "deleteTagFilterGroup": "Delete this Tag Filter Group?", - "deleteCuration": "Delete this Curation?", - "importCuration": "Zaimportować tą kurację?", - "deleteGameImage": "Delete this Game Image?", + "deleteTagFilterGroup": "Usunąć tę grupę filtrów tagów?", + "deleteCuration": "Usunąć tę kurację?", + "importCuration": "Zaimportować tę kurację?", + "deleteGameImage": "Usunąć ten obraz gry?", "deletePlaylist": "Usunąć tę playlistę?", "importAllCurations": "Zaimportować wszystkie kuracje?", "deleteAllCurations": "Usunąć wszystkie kuracje?", "removePlaylistGame": "Usunąć grę z playlisty?", "deleteGame": "Usunąć tę grę? Tej operacji nie można cofnąć.", - "deleteGameData": "Delete this Game Data? This cannot be undone.", - "deleteAddApp": "Usunąć tą dodatkową aplikację?", + "deleteGameData": "Usunąć dane gry? Tej operacji nie można cofnąć.", + "deleteAddApp": "Usunąć tę dodatkową aplikację?", "deleteTagCategory": "Usunąć tę kategorię tagów?", "deleteTag": "Usunąć ten tag?", "deleteTagAlias": "Usunąć ten alias tagu?", "deleteSource": "Usunąć to źródło?", "uninstallGame": "Odinstalować tę grę?", - "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", + "unableToUninstallGameData": "Nie można usunąć paczki danych. Czy jest w użyciu? Spróbuj uruchomić ponownie launcher.", "unableToDeleteGame": "Nie można usunąć gry. Czy jest uruchomiona? Spróbuj zrestartować launcher.", "downloadingGame": "Pobieranie gry...", "verifyingGame": "Weryfikowanie gry...", @@ -492,18 +508,21 @@ }, "libraries": { "arcade": "Gry", + "arcadeSingular": "Gra", "arcadePlural": "Wszystkie gry", "theatre": "Animacje", + "theatreSingular": "Animacja", "theatrePlural": "Wszystkie animacje", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animacja", "auditoriumPlural": "Wszystkie animacje NG" }, "upgrades": { "infinity": "Flashpoint Infinity", "infinityDesc": "Wymagane pliki. Dodaje wsparcie dla gier Flash.", "tech": "Inne technologie", - "techDesc": "Dodaje wsparcie dla wszystkich innych technologii - Shockwave, Unity, Java, HTML5, itp.", + "techDesc": "Dodaje wsparcie dla wszystkich innych technologii — Shockwave, Unity, Java, HTML5 itp.", "screenshots": "Loga i zrzuty ekranu", "screenshotsDesc": "Dodaje loga dla widoku siatki i zrzuty ekranu do wszystkich gier." } -} \ No newline at end of file +} diff --git a/lang/pt-BR.json b/lang/pt-BR.json index ca104f38e..00f1eae47 100644 --- a/lang/pt-BR.json +++ b/lang/pt-BR.json @@ -4,8 +4,16 @@ "configHeader": "Configurações", "configDesc": "(Você deve pressionar 'Salvar e Reiniciar' para que algumas alterações tenham efeito)", "preferencesHeader": "Preferências", - "extremeGames": "Conteúdo Extremo", - "extremeGamesDesc": "Permitir tags dentro de grupos de filtragem de tags 'Extremos' e jogos correspondentes a serem mostrados.", + "extremeGames": "Mostrar Filtros Extremos", + "extremeGamesDesc": "Permite que filtros de tag Extremos sejam ativados, criados e modificados para controlar conteúdo inadequado para crianças.", + "hideExtremeScreenshots": "Ocultar Capturas de Tela Extremas", + "hideExtremeScreenshotsDesc": "Oculta capturas de tela de conteúdo com tags Extremas, podem ser reveladas com um clique na caixa de imagem.", + "fancyAnimations": "Animações Bonitas", + "fancyAnimationsDesc": "Habilitar animações bonitas no launcher.", + "searchLimit": "Limite de busca", + "searchLimitDesc": "Limitar o número de resultados retornados em qualquer busca", + "searchLimitUnlimited": "Ilimitado", + "searchLimitValue": "{0} Resultados", "enableEditing": "Habilitar Edição", "enableEditingDesc": "Habilita a edição de jogos e aplicativos adicionais. Também revela abas relacionadas a edição.", "onDemandImages": "Imagens Sob Demanda", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Qual idioma utilizar caso o idioma atual esteja incompleto.", "auto": "Automático ({0})", "none": "Nenhum", + "contentFiltersHeader": "Filtros de Conteúdo", "flashpointHeader": "Flashpoint", "flashpointPath": "Caminho do Flashpoint", "flashpointPathDesc": "Caminho para a pasta do Flashpoint (pode ser relativo)", @@ -79,6 +88,7 @@ "helpInfo": "Precisa de ajuda? {0}.", "help": "Leia o manual", "upgradesHeader": "Melhorias", + "updateFeedHeader": "Feed de Notícias", "installComplete": "Instalação Completa", "alreadyInstalled": "Já Instalado", "download": "Baixar", @@ -103,7 +113,8 @@ "clearLog": "Limpar Log", "copy404Urls": "Copiar URLs 404", "uploadLog": "Enviar Log", - "copiedToClipboard": "Copiado para a área de transferência" + "copiedToClipboard": "Copiado para a área de transferência", + "openLogsWindow": "Abrir Janela de Logs" }, "app": { "home": "Início", @@ -266,7 +277,7 @@ "thumbnail": "Miniatura", "screenshot": "Captura de tela", "dropImageHere": "Largue uma imagem aqui para adicioná-la", - "noGameSelected": "Nenhum Jogo Selecionado", + "noGameSelected": "Nenhum {0} Selecionado", "clickToSelectGame": "Clique em um jogo para selecioná-lo.", "deleteAdditionalApplication": "Deletar Aplicativo Adicional", "deleteGameAndAdditionalApps": "Deletar Jogo (e Aplicativos Adicionais)", @@ -288,7 +299,10 @@ "saveAndRestart": "Salvar e Reiniciar", "thereAreNoGames": "Não há jogos.", "noGameMatchedDesc": "Nenhum título de jogo corresponde à sua pesquisa.", - "noGameMatchedSearch": "Tente pesquisar por algo menos restritivo." + "noGameMatchedSearch": "Tente pesquisar por algo menos restritivo.", + "mountParameters": "Parâmetros de Mount", + "noMountParameters": "Sem Parâmetros de Mount", + "showExtremeScreenshot": "Mostrar Captura de Tela Extrema" }, "tags": { "name": "Nome", @@ -354,7 +368,7 @@ "removeAddApp": "Remover App", "indexContent": "Atualizar Conteúdo", "delete": "Deletar", - "deleteCurationDesc": "Deleta esta curação (Os arquivos não podem ser recuperados)", + "deleteCurationDesc": "Deletar esta curação (Os arquivos não podem ser recuperados)", "openFolder": "Abrir Pasta", "run": "Iniciar", "runWithMAD4FP": "Iniciar com MAD4FP", @@ -413,7 +427,9 @@ "installingFiles": "Instalando Arquivos...", "complete": "Concluído", "exportMetaEditTitle": "Exportar Edição de Metadados", - "exportMetaEditDesc": "Selecione todas as propriedades para exportar:" + "exportMetaEditDesc": "Selecione todas as propriedades para exportar:", + "showImage": "Mostrar Imagem", + "searching": "Pesquisando..." }, "menu": { "viewThumbnailInFolder": "Ver Miniatura na Pasta", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Jogos", + "arcadeSingular": "Jogo", "arcadePlural": "Todos os Jogos", "theatre": "Animações", + "theatreSingular": "Animação", "theatrePlural": "Todas as Animações", "auditorium": "Auditório NG", + "auditoriumSingular": "Animação", "auditoriumPlural": "Todas as Animações NG" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logotipos & Capturas de Tela", "screenshotsDesc": "Adiciona logotipos para exibição em grade e capturas de tela para todos os jogos." } -} \ No newline at end of file +} diff --git a/lang/pt-PT.json b/lang/pt-PT.json index f4447c651..be8df8802 100644 --- a/lang/pt-PT.json +++ b/lang/pt-PT.json @@ -4,8 +4,16 @@ "configHeader": "Configuração", "configDesc": "(Deves carregar em \"Guardar & Reiniciar\" para que algumas alterações sejam feitas)", "preferencesHeader": "Preferências", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "Habilitar Edição", "enableEditingDesc": "Habilita a edição de jogos e aplicativos adicionais. Também revela abas relacionadas a edição.", "onDemandImages": "Imagens sob demanda", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Qual é a língua a ser usada caso a língua atual esteja incompleta.", "auto": "Auto ({0})", "none": "Nenhuma", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Caminho do Flashpoint", "flashpointPathDesc": "Caminho para a pasta do Flashpoint (pode ser relativo)", @@ -79,6 +88,7 @@ "helpInfo": "Precisas de ajuda? {0}.", "help": "Leia o manual", "upgradesHeader": "Atualizações", + "updateFeedHeader": "News Feed", "installComplete": "Instalação Completada", "alreadyInstalled": "Já Está Instalado", "download": "Descarregar", @@ -103,7 +113,8 @@ "clearLog": "Limpar o Registo", "copy404Urls": "Copiar URLs 404", "uploadLog": "Enviar Log", - "copiedToClipboard": "Copiado para a área de transferência" + "copiedToClipboard": "Copiado para a área de transferência", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Página Inicial", @@ -266,7 +277,7 @@ "thumbnail": "Logotipo", "screenshot": "Captura de ecrã", "dropImageHere": "Largue uma imagem aqui para a inserir", - "noGameSelected": "Nenhum jogo seleccionado", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Clique em um jogo para selecioná-lo.", "deleteAdditionalApplication": "Excluir Aplicação Adicional", "deleteGameAndAdditionalApps": "Excluir Jogo (e Aplicações Adicionais)", @@ -288,7 +299,10 @@ "saveAndRestart": "Guardar e Reiniciar", "thereAreNoGames": "Não há jogos.", "noGameMatchedDesc": "Nenhum título do jogo corresponde à sua pesquisa.", - "noGameMatchedSearch": "Tente procurar algo menos restritivo." + "noGameMatchedSearch": "Tente procurar algo menos restritivo.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Nome", @@ -413,7 +427,9 @@ "installingFiles": "Instalando Arquivos...", "complete": "Completo", "exportMetaEditTitle": "Exportar Edições de Meta", - "exportMetaEditDesc": "Selecionar todas as propriedades para exportar:" + "exportMetaEditDesc": "Selecionar todas as propriedades para exportar:", + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Visualizar miniatura na pasta", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Jogos", + "arcadeSingular": "Game", "arcadePlural": "Todos os Jogos", "theatre": "Animações", + "theatreSingular": "Animation", "theatrePlural": "Todas as Animações", "auditorium": "Auditório de NG", + "auditoriumSingular": "Animation", "auditoriumPlural": "Todas as animações de NG" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logos e capturas de tela", "screenshotsDesc": "Adiciona logotipos para exibição em grade e capturas de tela para todos os jogos." } -} \ No newline at end of file +} diff --git a/lang/ro-RO.json b/lang/ro-RO.json index 47acc2498..f4a892f25 100644 --- a/lang/ro-RO.json +++ b/lang/ro-RO.json @@ -4,8 +4,16 @@ "configHeader": "Configurări", "configDesc": "(Unele modificări necesită apăsarea pe „Salvare și Repornire“ pentru a fi aplicate)", "preferencesHeader": "Editați preferințele", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Arată filtrele extreme", + "extremeGamesDesc": "Permite filtrele pentru jocurile extreme să fie comutate, create și modificate pentru a controla conținutul neadecvat pentru copii.", + "hideExtremeScreenshots": "Ascunde capturile de ecran extreme", + "hideExtremeScreenshotsDesc": "Ascunde capturile de ecran ale jocurilor cu eticheta „extrem“. Acțiunea poate fi anulată, apăsând pe cadranul imaginii.", + "fancyAnimations": "Animații detaliate", + "fancyAnimationsDesc": "Activează animațiile detaliate în lansator.", + "searchLimit": "Limită de căutare", + "searchLimitDesc": "Limitează numărul de rezultate returnate în orice căutare", + "searchLimitUnlimited": "Fără limită", + "searchLimitValue": "{0} rezultate", "enableEditing": "Activați editarea", "enableEditingDesc": "Activați editarea jocurilor și a aplicațiilor suplimentare. De asemenea, arată categorii legate de editare.", "onDemandImages": "Imagini la cerere", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Limba folosită de lansator, în cazul în care limba principală e incompletă.", "auto": "Auto ({0})", "none": "Nimic selectat", + "contentFiltersHeader": "Filtre de conținut", "flashpointHeader": "Flashpoint", "flashpointPath": "Calea spre dosarul Flashpoint", "flashpointPathDesc": "Calea spre dosarul lansatorului Flashpoint (poate fi relativă)", @@ -23,7 +32,7 @@ "libraries": "Biblioteci", "randomLibraries": "Biblioteci aleatoare", "randomLibrariesDesc": "Biblioteci de utilizat pentru alegeri aleatorii de pe pagina principală.", - "updateSource": "Update Source", + "updateSource": "Actualizare sursă", "platforms": "Platforme", "nativePlatforms": "Platforme native", "nativePlatformsDesc": "Folosește versiuni native ale acestor platforme. (Dacă nu sunt disponibile, se vor folosi prin Wine)", @@ -57,8 +66,8 @@ "extLogoSets": "Set de logo-uri", "extApplications": "Aplicații", "saveAndRestart": "Salvare și repornire", - "saveAndClose": "Save and Close", - "tagFilterGroupEditor": "Tag Filter Group Editor" + "saveAndClose": "Salvare și închidere", + "tagFilterGroupEditor": "Editor de grup de filtre de etichete" }, "home": { "updateHeader": "Actualizare lansator", @@ -79,6 +88,7 @@ "helpInfo": "Aveți nevoie de ajutor? {0}.", "help": "Citiți manualul", "upgradesHeader": "Suplimente", + "updateFeedHeader": "Flux de ştiri", "installComplete": "Instalare completă", "alreadyInstalled": "Instalat deja", "download": "Descărcați", @@ -103,7 +113,8 @@ "clearLog": "Goliți jurnalul", "copy404Urls": "Copiază URL-urile 404", "uploadLog": "Încarcă jurnalul", - "copiedToClipboard": "Copiat" + "copiedToClipboard": "Copiat", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Pag. principală", @@ -173,13 +184,13 @@ "fixCommaTags": "Repară etichetele în care apar virgule", "fixCommaTagsDesc": "Împarte etichetele cu virgulă în mai multe etichete și le aplică tuturor jocurilor corespunzătoare", "exportTags": "Exportați etichete", - "exportTagsDesc": "Creează un fișier cu etichete și categorii de etichete care pot fi folosite de către operațiunea \"Importați etichete\"", + "exportTagsDesc": "Creează un fișier cu etichete și categorii de etichete care pot fi folosite prin operațiunea 'Importați etichete'", "importTags": "Importați etichete", "importTagsDesc": "Importați etichete dintr-un fișier creat cu operațiunea 'Exportă etichete'", - "migrateExtremeGames": "Migrate Extreme Games", - "migrateExtremeGamesDesc": "Removes 'Extreme' from any games and assigns the 'LEGACY-Extreme' tag to them", - "massImportGameData": "Mass Import Game Data", - "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", + "migrateExtremeGames": "Migrați jocurile extreme", + "migrateExtremeGamesDesc": "Elimină „extrem“ din toate jocurile și le asociază eticheta „ÎNVECHIT-extrem“", + "massImportGameData": "Importă date de joc în masă", + "massImportGameDataDesc": "Importă în masă pachete de date ale jocurilor, încercând să le asimileze numele cu ID-ul unic.", "servicesHeader": "Servicii ce rulează în fundal", "servicesMissing": "Nu există astfel de servicii.", "running": "Rulează", @@ -208,22 +219,22 @@ "specialThanks": "Mulțumiri deosebite" }, "browse": { - "noTitle": "Nu este introdus niciun titlu", - "by": "de către", + "noTitle": "Indisponibil", + "by": "de", "play": "Jucați", "stop": "Oprire", - "noDeveloper": "Nu este introdus numele dezvoltatorului", + "noDeveloper": "Indisponibil", "alternateTitles": "Titluri alternative", "noAlternateTitles": "Fără titluri alternative", "tags": "Etichete", "noTags": "Fără etichete", "enterTag": "Introduceți eticheta", "series": "Seria", - "noSeries": "Nu este introdusă seria", + "noSeries": "Indisponibil", "publisher": "Site-ul de publicare", - "noPublisher": "Nu este introdus numele site-ului de publicare", + "noPublisher": "Indisponibil", "source": "Sursa", - "noSource": "Nu este introdusă adresa site-ului sursă", + "noSource": "Indisponibil", "platform": "Platformă", "noPlatform": "Nicio platformă selectată", "playMode": "Mod de joc", @@ -231,11 +242,11 @@ "status": "Stare", "noStatus": "Nicio stare selectată", "version": "Versiune", - "noVersion": "Nu este introdusă o versiune", + "noVersion": "Indisponibil", "releaseDate": "Data publicării", "noReleaseDate": "Data publicării necunoscută", "language": "Limba", - "noLanguage": "Nu este introdusă nicio limbă", + "noLanguage": "Indisponibil", "dateAdded": "Data adăugării", "dateModified": "Data modificării", "brokenInInfinity": "Nefuncțional în Flashpoint Infinity", @@ -245,28 +256,28 @@ "notes": "Note", "noNotes": "Nu există note", "originalDescription": "Descrierea originală a jocului", - "noOriginalDescription": "Nu este introdusă descrierea originală", + "noOriginalDescription": "Indisponibil", "additionalApplications": "Aplicații suplimentare", - "noName": "Nu este introdus un nume", + "noName": "Indisponibil", "launch": "Lansare", "autoRunBefore": "Lansare automată înainte", "waitForExit": "Așteptați pentru oprire", "applicationPath": "Calea aplicației", - "noApplicationPath": "Nu este introdusă calea aplicației", + "noApplicationPath": "Indisponibil", "launchCommand": "Comandă de lansare", - "noLaunchCommand": "Nu este introdusă comanda de lansare", + "noLaunchCommand": "Indisponibil", "searching": "Se caută...", "library": "Categorie", "defaultLibrary": "Categoria implicită", - "uninstallGame": "Uninstall Game", + "uninstallGame": "Dezinstalare joc", "installed": "Instalat", "notInstalled": "Nu e instalat", - "legacyGame": "Joc depășit", + "legacyGame": "Joc învechit", "download": "Descărcați", "thumbnail": "logo-ul jocului", "screenshot": "captură de ecran", "dropImageHere": "Glisați aici o imagine pentru a o adăuga", - "noGameSelected": "Niciun joc selectat", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Apăsați pe un joc pentru a-l selecta.", "deleteAdditionalApplication": "Înlăturați aplicația suplimentară", "deleteGameAndAdditionalApps": "Înlăturați jocul (împreună cu aplicațiile sale suplimentare)", @@ -288,7 +299,10 @@ "saveAndRestart": "Salvare și repornire", "thereAreNoGames": "Nu s-au găsit jocuri.", "noGameMatchedDesc": "Niciun joc nu se potrivește cu termenii de căutare.", - "noGameMatchedSearch": "Încercați alte cuvinte cheie." + "noGameMatchedSearch": "Încercați alte cuvinte cheie.", + "mountParameters": "Parametri de montare", + "noMountParameters": "Fără parametri de montare", + "showExtremeScreenshot": "Arată capturile de ecran extreme" }, "tags": { "name": "Nume", @@ -336,12 +350,12 @@ "loadArchiveDesc": "Încărcați arhive de reparație", "loadFolder": "Încărcați dosare", "loadFolderDesc": "Încărcați dosare de reparație", - "scanNewCurationFolders": "Scan For New Curations", - "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", + "scanNewCurationFolders": "Scanare pentru noi reparații", + "scanNewCurationFoldersDesc": "Scanează pentru reparații care au fost adăugate după scanarea inițială", "noCurations": "Fără reparații", "id": "Dosarul reparațiilor", "heading": "Nume", - "noHeading": "Nu a fost introdus numele aplicației suplimentare", + "noHeading": "Indisponibil", "folderName": "Dosarul suplimentelor", "noFolderName": "Nu a fost introdus un nume al dosarului", "message": "Mesaj de lansare", @@ -370,18 +384,18 @@ "unusedPlatform": "Platforma are o valoare nefolosită. Asigurați-vă că ați scris corect!", "nonExistingLibrary": "Categoria are un nume care nu coincide niciunei categorii predefinite. Se va folosi categoria implicită.", "nonContentFolders": "Au fost găsite dosare neutilizare în dosarul de reparații. Doriți să continuați astfel?", - "noTags": "There are no tags on this curation.", - "noSource": "'Source' is a required field.", - "unenteredTag": "The Tags text field hasn't been submitted.", - "noLogo": "There is no logo on this curation.", - "noScreenshot": "There is no screenshot on this curation.", + "noTags": "Nu există etichete pentru această reparație.", + "noSource": "Câmpul „sursă“ este necesar.", + "unenteredTag": "Câmpul de text al etichetei nu a fost specificat.", + "noLogo": "Nu există logo pentru această reparație.", + "noScreenshot": "Nu există captură de ecran pentru această reparație.", "ilc_notHttp": "Folosiți HTTP.", "ilc_nonExistant": "Selectați un fișier existent din dosarul reparației.", - "sort": "Sort Curations (A-Z)" + "sort": "Sortare reparații (de la A la Z)" }, "playlist": { "enterDescriptionHere": "Iintroduceți descrierea aici...", - "noDescription": "Nu este introdusă o descriere", + "noDescription": "Indisponibil", "save": "Salvați", "saveDesc": "Salvați modificările făcute și încheiați editarea", "discard": "Renunțați", @@ -399,7 +413,7 @@ "noAuthor": "Nu este precizat autorul", "authorPlaceholder": "Introduceți numele autorului...", "id": "ID", - "by": "după", + "by": "de", "extreme": "matur" }, "misc": { @@ -413,7 +427,9 @@ "installingFiles": "Se instalează fișierele...", "complete": "Finalizat", "exportMetaEditTitle": "Exportați modificări ale informațiilor jocurilor", - "exportMetaEditDesc": "Selectați proprietățile de exportat:" + "exportMetaEditDesc": "Selectați proprietățile de exportat:", + "showImage": "Arată imaginea", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Vizualizați imaginea în dosar", @@ -463,39 +479,42 @@ "areYouSurePlaylistRemove": "Sigur doriți să ștergeți această intrare?", "cancel": "Revocare", "mergePlaylists": "Îmbinați liste de jocuri", - "newPlaylist": "Stay Separate", - "uploadPrivacyWarning": "This will make your Logs available publicly.\nA link will be copied to your clipboard.\n\nAre you sure?", + "newPlaylist": "Păstrare separat", + "uploadPrivacyWarning": "Această acțiune va face jurnalele dv. publice și va copia linkul către acestea.\n\nDoriți să continuați?", "overwriteFileTitle": "Fişierul există deja!", "overwriteFileMessage": "Există deja un fișier cu acest nume! Doriți să suprascrieți fișierul existent?", "overwriteFileDetail": "Calea fișierului:", - "deleteTagFilterGroup": "Delete this Tag Filter Group?", - "deleteCuration": "Delete this Curation?", - "importCuration": "Import this Curation?", - "deleteGameImage": "Delete this Game Image?", - "deletePlaylist": "Delete this Playlist?", - "importAllCurations": "Import all Curations?", - "deleteAllCurations": "Delete all Curations?", - "removePlaylistGame": "Remove this Game from the Playlist?", - "deleteGame": "Delete this Game? This cannot be undone.", - "deleteGameData": "Delete this Game Data? This cannot be undone.", - "deleteAddApp": "Delete this Add App?", - "deleteTagCategory": "Delete this Tag Category?", - "deleteTag": "Delete this Tag?", - "deleteTagAlias": "Delete this Tag Alias?", - "deleteSource": "Delete this Source?", - "uninstallGame": "Uninstall this Game?", - "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", - "unableToDeleteGame": "Unable to delete game. Is it in use? Try restarting the launcher.", - "downloadingGame": "Downloading Game...", - "verifyingGame": "Verifying Game...", - "aFewMinutes": "This may take a few minutes." + "deleteTagFilterGroup": "Ștergeți acest grup de filtre de etichete?", + "deleteCuration": "Ștergeți această reparație?", + "importCuration": "Importați această reparație?", + "deleteGameImage": "Ștergeți această imagine?", + "deletePlaylist": "Ștergeți această listă?", + "importAllCurations": "Importați toate reparațiile?", + "deleteAllCurations": "Ștergeți toate reparațiile?", + "removePlaylistGame": "Eliminați acest joc din listă?", + "deleteGame": "Ștergeți jocul? Acțiunea nu poate fi anulată.", + "deleteGameData": "Ștergeți datele jocului? Acțiunea nu poate fi anulată.", + "deleteAddApp": "Ștergeți această aplicație?", + "deleteTagCategory": "Ștergeți această categorie de etichete?", + "deleteTag": "Ștergeți această categorie?", + "deleteTagAlias": "Ștergeți acest nume de etichetă?", + "deleteSource": "Ștergeți această sursă?", + "uninstallGame": "Dezinstalați jocul?", + "unableToUninstallGameData": "Nu se poate șterge pachetul de date. Verificați dacă este în folosință sau reporniți lansatorul.", + "unableToDeleteGame": "Nu se poate șterge jocul. Verificați dacă este în folosință sau reporniți lansatorul.", + "downloadingGame": "Se descarcă jocul…", + "verifyingGame": "Se verifică jocul…", + "aFewMinutes": "Acțiunea poate dura câteva minute." }, "libraries": { "arcade": "Jocuri", + "arcadeSingular": "Game", "arcadePlural": "Toate jocurile", "theatre": "Animații", + "theatreSingular": "Animation", "theatrePlural": "Toate animațiile", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Toate animațiile NG" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Sigle și capturi de ecran", "screenshotsDesc": "Adaugă sigle pentru vizualizare tip grilă și capturi de ecran pentru toate jocurile." } -} \ No newline at end of file +} diff --git a/lang/ru-RU.json b/lang/ru-RU.json index 73537c1d4..34ec2c995 100644 --- a/lang/ru-RU.json +++ b/lang/ru-RU.json @@ -1,11 +1,19 @@ { - "name": "Русский", + "name": "Английский", "config": { "configHeader": "Настройки", "configDesc": "(Вы должны нажать 'Сохранить и перезапустить' для того, чтобы изменения вошли в силу)", "preferencesHeader": "Персонализация", - "extremeGames": "Взрослый контент", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Скрыть экстремальные скриншоты", + "hideExtremeScreenshotsDesc": "Скрывает скриншоты экстремального содержимого, можно вернуть нажатием на окно изображения.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "Включить редактирование", "enableEditingDesc": "Включите редактирование игр и дополнительных приложений. Также показывает вкладки, связанные с редактированием.", "onDemandImages": "Изображения по требованию", @@ -16,24 +24,25 @@ "fallbackLanguageDesc": "Язык, который будет использоваться в случаях несовместимости с языком по умолчанию.", "auto": "Системный ({0})", "none": "Нет", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Путь", "flashpointPathDesc": "Путь к папке, в которой находится Flashpoint (может быть относительным)", "browse": "Обзор", "libraries": "Библиотеки", "randomLibraries": "Случайные библиотеки", - "randomLibrariesDesc": "Библиотеки для случайных выбора на домашней странице.", - "updateSource": "Update Source", + "randomLibrariesDesc": "Библиотеки для случайного выбора на домашней странице.", + "updateSource": "Обновить источник", "platforms": "Платформы", - "nativePlatforms": "Платформы Native", - "nativePlatformsDesc": "Используйте Native версии этих платформ. (Если нет, он будет использовать Wine)", - "tagFilterGroups": "Tag Filter Groups", - "tagFilterGroupsDesc": "These groups will filter suggestions and games out of search results", - "editTagFilter": "Edit Tag Filter Group", - "duplicateTagFilter": "Duplicate Tag Filter Group", - "deleteTagFilter": "Delete Tag Filter Group", + "nativePlatforms": "Native платформы", + "nativePlatformsDesc": "Используйте Native версии этих платформ. (Если недоступно, то будет использоваться Wine)", + "tagFilterGroups": "Группы фильтров тегов", + "tagFilterGroupsDesc": "Эти группы будут отфильтровывать предложения и игры с результатов поиска", + "editTagFilter": "Редактировать группу фильтров тегов", + "duplicateTagFilter": "Дублировать группу фильтров тегов", + "deleteTagFilter": "Удалить группу фильтров тегов", "appPathOverrides": "Переопределение пути приложения", - "appPathOverridesDesc": "Переопределяет путь к приложению слева с правой стороны при запуске игр.", + "appPathOverridesDesc": "Переопределяет путь к приложению слева с той что справа при запуске игр.", "visualsHeader": "Отображение", "useCustomTitleBar": "Использовать пользовательский заголовок", "useCustomTitleBarDesc": "Использовать пользовательский заголовок сверху этого окна.", @@ -42,12 +51,12 @@ "themeDesc": "ID темы для использования.", "logoSet": "Набор логотипа", "noLogoSet": "Нет набора логотипа", - "logoSetDesc": "ID используемого набора логотипа для использования.", + "logoSetDesc": "ID набора логотипа для использования.", "advancedHeader": "Дополнительно", - "showDeveloperTab": "Показывать вкладку 'Разработка'", - "showDeveloperTabDesc": "Показывает вкладку 'Разработчикам'. Она в большинстве полезна случаев только для разработчиков и кураторов.", + "showDeveloperTab": "Показать вкладку разработчика", + "showDeveloperTabDesc": "Показывает вкладку 'Разработчиков'. Она в большинстве случаев полезна только для разработчиков и кураторов.", "server": "Сервер", - "serverDesc": "Какой процесс сервера запускается при запуске лаунчера.", + "serverDesc": "Какой процесс сервера запустить при запуске лаунчера.", "metadataServerHost": "Хост сервера метаданных", "metadataServerHostDesc": "Хост сервера метаданных. Используется для синхронизации новой и обновлённой информации об игре в лаунчере.", "extensionsHeader": "Расширения", @@ -57,30 +66,31 @@ "extLogoSets": "Набор логотипа(ов)", "extApplications": "Приложение(я)", "saveAndRestart": "Сохранить и перезапустить", - "saveAndClose": "Save and Close", - "tagFilterGroupEditor": "Tag Filter Group Editor" + "saveAndClose": "Сохранить и закрыть", + "tagFilterGroupEditor": "Редактор групп фильтров тегов" }, "home": { "updateHeader": "Обновление Лаунчера", "currentVersion": "Текущая версия", "nextVersion": "Следующая версия", - "updateAvailable": "Обновление доступно", - "upToDate": "Обновиться.", + "updateAvailable": "Доступно обновление", + "upToDate": "Актуальная версия.", "downloadingUpdate": "Загрузка обновления...", "quickStartHeader": "Быстрый запуск", - "hallOfFameInfo": "Хотите только лучшие игры? Загляните в {0}!", + "hallOfFameInfo": "Хотите только лучшее из лучших? Загляните в {0}!", "hallOfFame": "Зал Славы", "allGamesInfo": "Ищите, во что поиграть? Посмотрите {0}.", "allGames": "Все игры", "allAnimationsInfo": "Ищите, что посмотреть? Посмотрите {0}.", "allAnimations": "Все анимации", - "configInfo": "Хотите что-нибудь настроить? Вам в {0}.", - "config": "Настройки", + "configInfo": "Хотите что-нибудь изменить? Вам в {0}.", + "config": "Конфиг", "helpInfo": "Нужна помощь? {0}.", "help": "Прочитать руководство", "upgradesHeader": "Улучшения", - "installComplete": "Установка завершено", - "alreadyInstalled": "Установлено", + "updateFeedHeader": "News Feed", + "installComplete": "Установка завершена", + "alreadyInstalled": "Уже Установлено", "download": "Загрузить", "update": "Обновить", "checkingUpgradeState": "Проверка состояния обновления...", @@ -89,21 +99,22 @@ "tagList": "Лист Тегов", "filterByPlatform": "Сортировать по платформе", "plannedFeatures": "Ознакомьтесь с запланированными функциями!", - "notesHeader": "Примечание", - "notes": "Не забудьте прочитать руководство, если у вас есть какие-то вопросы.", + "notesHeader": "Примечания", + "notes": "Не забудьте прочитать руководство, если у вас есть какие-то неполадки.", "linuxSupport": "Проблемы на Linux? {0}", "linuxSupportLinkText": "Открыть FAQ", - "randomPicks": "Случайные игры", + "randomPicks": "Случайный выбор", "rerollPicks": "Повторить выбор" }, "logs": { "filters": "Фильтры", "logLevels": "Уровни лога", - "copyText": "Копировать текст", + "copyText": "Скопировать текст", "clearLog": "Очистить лог", - "copy404Urls": "Копировать 404 URL", + "copy404Urls": "Скопировать 404 ссылки", "uploadLog": "Журнал загрузки", - "copiedToClipboard": "Скопировано в буфер обмена" + "copiedToClipboard": "Скопировано в буфер обмена", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Главная", @@ -176,10 +187,10 @@ "exportTagsDesc": "Создаёт файл с тегами и категориями, которые могут быть использованы при импорте тегов", "importTags": "Импорт тегов", "importTagsDesc": "Импортировать теги из файла, созданного 'Экспорт тегов'", - "migrateExtremeGames": "Migrate Extreme Games", - "migrateExtremeGamesDesc": "Removes 'Extreme' from any games and assigns the 'LEGACY-Extreme' tag to them", - "massImportGameData": "Mass Import Game Data", - "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", + "migrateExtremeGames": "Перенести экстремальные игры", + "migrateExtremeGamesDesc": "Удаляет 'Extreme' из любых игр и присваивает им тег 'LEGACY-Extreme'", + "massImportGameData": "Массовый импорт данных игры", + "massImportGameDataDesc": "Массовый импорт наборов игровых данных, пытающихся сопоставить их имя с UUID игр.", "servicesHeader": "Фоновые сервисы", "servicesMissing": "Нет сервисов.", "running": "Работает", @@ -258,15 +269,15 @@ "searching": "Идёт поиск...", "library": "Библиотека", "defaultLibrary": "Библиотека по умолчанию", - "uninstallGame": "Uninstall Game", - "installed": "Installed", - "notInstalled": "Not Installed", - "legacyGame": "Legacy Game", - "download": "Download", + "uninstallGame": "Удалить игру", + "installed": "Установлено", + "notInstalled": "Не установлено", + "legacyGame": "Устаревшая игра", + "download": "Загрузить", "thumbnail": "Превью", "screenshot": "Скриншот", "dropImageHere": "Поместите изображение сюда, чтобы добавить его", - "noGameSelected": "Нет выбранной игры", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Нажмите на игру, чтобы выбрать её.", "deleteAdditionalApplication": "Удалить дополнительные приложения", "deleteGameAndAdditionalApps": "Удалить игру (и дополнительные приложения)", @@ -288,7 +299,10 @@ "saveAndRestart": "Сохранить и перезапустить", "thereAreNoGames": "Игры не найдены.", "noGameMatchedDesc": "Нет игр, соответствующих вашему запросу.", - "noGameMatchedSearch": "Попробуйте изменить запрос на менее конкретный." + "noGameMatchedSearch": "Попробуйте изменить запрос на менее конкретный.", + "mountParameters": "Параметры монтирования", + "noMountParameters": "Нет параметров шейдера", + "showExtremeScreenshot": "Показать Экстремальный Скриншот" }, "tags": { "name": "Наименование", @@ -321,7 +335,7 @@ "saveImportedCurations": "Сохранить импортированные курации", "keepArchiveKey": "Сохранить архив UUID", "symlinkCurationContent": "Symlink Curation Content Folder (Требует подтверждение администратора в Windows, требуется для MAD4FP)", - "useTagFilters": "Use Tag Filters in Suggestions", + "useTagFilters": "Использовать фильтры тегов в предложениях", "openCurationsFolder": "Папка Курации", "openCurationsFolderDesc": "Откройте папку Кураций в проводнике", "openExportsFolder": "Экспортируемые курации", @@ -336,8 +350,8 @@ "loadArchiveDesc": "Загрузить один или больше архивов", "loadFolder": "Загрузить папку", "loadFolderDesc": "Загрузить одну или больше папок", - "scanNewCurationFolders": "Scan For New Curations", - "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", + "scanNewCurationFolders": "Сканировать новые курирования", + "scanNewCurationFoldersDesc": "Сканирование на предмет курирования, добавленных после первоначального сканирования", "noCurations": "Нет Курации", "id": "Папка курирования", "heading": "Название", @@ -370,14 +384,14 @@ "unusedPlatform": "'Платформа' имеет неиспользуемое значение. Убедитесь, что указали его правильно!", "nonExistingLibrary": "'Библиотека' не является именем существующей библиотеки! (Будет использована библиотека по умолчанию)", "nonContentFolders": "Папки без содержимого, найденные в папке Курации. Они должны быть там?", - "noTags": "There are no tags on this curation.", - "noSource": "'Source' is a required field.", - "unenteredTag": "The Tags text field hasn't been submitted.", - "noLogo": "There is no logo on this curation.", - "noScreenshot": "There is no screenshot on this curation.", + "noTags": "На этом курировании нет тегов.", + "noSource": "Поле «Источник» обязательно для заполнения.", + "unenteredTag": "Текстовое поле тегов не было отправлено.", + "noLogo": "На этом курировании нет логотипа.", + "noScreenshot": "На этом курировании нет скриншотов.", "ilc_notHttp": "Использовать HTTP.", "ilc_nonExistant": "Укажите на существующий файл в папке 'содержимое' вашего курирования.", - "sort": "Sort Curations (A-Z)" + "sort": "Сортировка курирований (A-Z)" }, "playlist": { "enterDescriptionHere": "Введите описание сюда...", @@ -413,13 +427,15 @@ "installingFiles": "Установка файлов...", "complete": "Завершено", "exportMetaEditTitle": "Экспорт мета-правок", - "exportMetaEditDesc": "Выберите все свойства для экспорта:" + "exportMetaEditDesc": "Выберите все свойства для экспорта:", + "showImage": "Показать Изображение", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Отображать превью в папке", "viewScreenshotInFolder": "Отображать скриншоты в папке", "openFileLocation": "Открыть расположение файлов", - "addToPlaylist": "Add to Playlist", + "addToPlaylist": "Добавить в плейлист", "duplicateMetaOnly": "Дублировать (Только метафайлы)", "duplicateMetaAndImages": "Дублировать (Метафайлы и изображения)", "copyGameUUID": "Скопировать UUID игры", @@ -459,43 +475,46 @@ "restartNow": "Перезагрузить сейчас?", "restartToApplyUpgrade": "Это обновление не будет применено, пока вы не перезапустите.\nВы хотите сделать это сейчас?", "areYouSure": "Вы уверены?", - "areYouSureDelete": "Are you sure you want to delete this entry?", - "areYouSurePlaylistRemove": "Are you sure you want to remove this entry from the playlist?", + "areYouSureDelete": "Вы уверены, что хотите удалить эту запись?", + "areYouSurePlaylistRemove": "Вы уверены, что хотите удалить эту запись из плейлиста?", "cancel": "Отмена", "mergePlaylists": "Объединить плейлисты", - "newPlaylist": "Stay Separate", - "uploadPrivacyWarning": "This will make your Logs available publicly.\nA link will be copied to your clipboard.\n\nAre you sure?", + "newPlaylist": "Оставить раздельно", + "uploadPrivacyWarning": "Это сделает ваш отчёт общедоступным.\nСсылка будет скопирована в буфер обмена.\n\nВы уверены?", "overwriteFileTitle": "Файл уже существует!", "overwriteFileMessage": "Файл с таким именем уже существует. Вы хотите перезаписать его?", "overwriteFileDetail": "Путь к файлу:", - "deleteTagFilterGroup": "Delete this Tag Filter Group?", - "deleteCuration": "Delete this Curation?", - "importCuration": "Import this Curation?", - "deleteGameImage": "Delete this Game Image?", - "deletePlaylist": "Delete this Playlist?", - "importAllCurations": "Import all Curations?", - "deleteAllCurations": "Delete all Curations?", - "removePlaylistGame": "Remove this Game from the Playlist?", - "deleteGame": "Delete this Game? This cannot be undone.", - "deleteGameData": "Delete this Game Data? This cannot be undone.", - "deleteAddApp": "Delete this Add App?", - "deleteTagCategory": "Delete this Tag Category?", - "deleteTag": "Delete this Tag?", - "deleteTagAlias": "Delete this Tag Alias?", - "deleteSource": "Delete this Source?", - "uninstallGame": "Uninstall this Game?", - "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", - "unableToDeleteGame": "Unable to delete game. Is it in use? Try restarting the launcher.", - "downloadingGame": "Downloading Game...", - "verifyingGame": "Verifying Game...", - "aFewMinutes": "This may take a few minutes." + "deleteTagFilterGroup": "Удалить эту группу фильтров тегов?", + "deleteCuration": "Удалить это курирование?", + "importCuration": "Импортировать это курирование?", + "deleteGameImage": "Удалить это изображение игры?", + "deletePlaylist": "Удалить этот плейлист?", + "importAllCurations": "Импортировать все курирования?", + "deleteAllCurations": "Удалить все курирования?", + "removePlaylistGame": "Удалить эту игру из плейлиста?", + "deleteGame": "Удалить эту игру? Операция не может быть отменена.", + "deleteGameData": "Удалить данные от этой игры? Операция не может быть отменена.", + "deleteAddApp": "Удалить это добавить приложение?", + "deleteTagCategory": "Удалить эту категорию тегов?", + "deleteTag": "Удалить этот тег?", + "deleteTagAlias": "Удалить этот псевдоним тегов?", + "deleteSource": "Удалить этот источник?", + "uninstallGame": "Удалить эту игру?", + "unableToUninstallGameData": "Не удается удалить набор данных. Используется ли он? Попробуйте перезапустить лаунчер.", + "unableToDeleteGame": "Не удается удалить игру. Используется ли она? Попробуйте перезапустить лаунчер.", + "downloadingGame": "Загрузка игры...", + "verifyingGame": "Проверка игры...", + "aFewMinutes": "Это может занять несколько минут." }, "libraries": { "arcade": "Игры", + "arcadeSingular": "Game", "arcadePlural": "Все игры", "theatre": "Анимации", + "theatreSingular": "Animation", "theatrePlural": "Все анимации", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Все NG анимации" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Логотипы и скриншоты", "screenshotsDesc": "Добавляет логотипы для сетки и скриншоты для всех игр." } -} \ No newline at end of file +} diff --git a/lang/sr-CS.json b/lang/sr-CS.json index 39113991b..f3705f41a 100644 --- a/lang/sr-CS.json +++ b/lang/sr-CS.json @@ -4,8 +4,16 @@ "configHeader": "Podešavanja", "configDesc": "(Moraš da pritisneš 'Sačuvaj i pokreni ponovo' da bi neke promene stupile na snagu)", "preferencesHeader": "Postavke", - "extremeGames": "Ekstremni sadržaj", - "extremeGamesDesc": "Dozvolite prikazivanje oznaka unutar grupa filtera za oznake „Ekstreme“ i odgovarajućih igara.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "Uključi uređivanje", "enableEditingDesc": "Omogućava uređivanje igara i dodatnih aplikacija. Takođe prikazuje kartice vezane za uređivanje.", "onDemandImages": "Slike na zahtev", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Koji jezik da se koristi umesto nepotpunog trenutnog jezika.", "auto": "Automatski ({0})", "none": "Nijedan", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Putanja Flashpoint-a", "flashpointPathDesc": "Putanja do Flashpoint fascikle (može da bude relativna)", @@ -79,6 +88,7 @@ "helpInfo": "Treba ti pomoć? {0}.", "help": "Pročitaj priručnik", "upgradesHeader": "Nadogradnje", + "updateFeedHeader": "News Feed", "installComplete": "Instalacija je gotova", "alreadyInstalled": "Već instalirano", "download": "Preuzmi", @@ -103,7 +113,8 @@ "clearLog": "Očisti zapis", "copy404Urls": "Kopiraj 404 URL-ove", "uploadLog": "Pošalji zapis", - "copiedToClipboard": "Kopirano u privremenu memoriju" + "copiedToClipboard": "Kopirano u privremenu memoriju", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Početak", @@ -266,7 +277,7 @@ "thumbnail": "Simbolična slika", "screenshot": "Slika ekrana", "dropImageHere": "Dovuci sliku ovde da bi je dodao", - "noGameSelected": "Nijedna igra nije izabrana", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Klikni na igru da bi ga izabrao.", "deleteAdditionalApplication": "Izbriši dodatnu aplikaciju", "deleteGameAndAdditionalApps": "Izbriši igru (i dodatne aplikacije)", @@ -288,7 +299,10 @@ "saveAndRestart": "Sačuvaj i ponovo pokreni", "thereAreNoGames": "Nema igara.", "noGameMatchedDesc": "Nijedan naslov igre nije odgovarao sa tvojom pretragom.", - "noGameMatchedSearch": "Pokušaj da tražiš nešto manje restriktivno." + "noGameMatchedSearch": "Pokušaj da tražiš nešto manje restriktivno.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Ime", @@ -413,7 +427,9 @@ "installingFiles": "Instaliram fajlove...", "complete": "Gotovo", "exportMetaEditTitle": "Izvoz izmena metapodatka", - "exportMetaEditDesc": "Izaberite sve postavke za iznošenje:" + "exportMetaEditDesc": "Izaberite sve postavke za iznošenje:", + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Pregledaj simboličnu sliku u fascikli", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Igre", + "arcadeSingular": "Game", "arcadePlural": "Sve igre", "theatre": "Animacije", + "theatreSingular": "Animation", "theatrePlural": "Sve animacije", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Sve NG animacije" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logotipi i slike ekrana", "screenshotsDesc": "Dodaje logotip za pregled u rešetki i slike ekrana za sve igre." } -} \ No newline at end of file +} diff --git a/lang/sr-SP.json b/lang/sr-SP.json index 83c400b26..72c15414e 100644 --- a/lang/sr-SP.json +++ b/lang/sr-SP.json @@ -4,18 +4,27 @@ "configHeader": "Подешавања", "configDesc": "(Мораш да притиснеш 'Сачувај и покрени поново' да би неке промене ступиле на снагу)", "preferencesHeader": "Поставке", - "extremeGames": "Екстремни садржај", - "extremeGamesDesc": "Дозволите приказивање ознака унутар група филтера за ознаке „Екстреме“ и одговарајућих игара.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "Укључи уређивање", "enableEditingDesc": "Омогућава уређивање игара и додатних апликација. Такође приказује картице везане за уређивање.", "onDemandImages": "Слике на захтев", "onDemandImagesDesc": "Преузми и сачувај недостајуће логотипе и слике екрана чим су потребни.", "currentLanguage": "Језик", - "currentLanguageDesc": "Који језик ће апликација да користи пробитно.", + "currentLanguageDesc": "Који језик ће апликација да користи првобитно.", "fallbackLanguage": "Споредни језик", "fallbackLanguageDesc": "Који језик да се користи уместо непотпуног тренутног језика.", "auto": "Аутоматски ({0})", "none": "Ниједан", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Путања Flashpoint-а", "flashpointPathDesc": "Путања до Flashpoint фасцикле (може да буде релативна)", @@ -52,7 +61,7 @@ "metadataServerHostDesc": "Хост сервера за метаподатак. Користи се за синхронизацију нове и исправљене информације о игри са покретачем.", "extensionsHeader": "Додаци", "noExtensionsLoaded": "Додаци нису учитани, инсталирај их у '{0}'", - "extDevScripts": "Програмерска скрипта/е", + "extDevScripts": "Програмерска скрипта(е)", "extThemes": "Тема(е)", "extLogoSets": "Скуп(ови) логотипа", "extApplications": "Апликација(е)", @@ -79,6 +88,7 @@ "helpInfo": "Треба ти помоћ? {0}.", "help": "Прочитај приручник", "upgradesHeader": "Надоградњe", + "updateFeedHeader": "News Feed", "installComplete": "Инсталација је готова", "alreadyInstalled": "Већ инсталирано", "download": "Преузми", @@ -103,7 +113,8 @@ "clearLog": "Очисти запис", "copy404Urls": "Копирај 404 URL-ове", "uploadLog": "Пошаљи запис", - "copiedToClipboard": "Копирано у привремену меморију" + "copiedToClipboard": "Копирано у привремену меморију", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Почетак", @@ -266,7 +277,7 @@ "thumbnail": "Симболична слика", "screenshot": "Слика екрана", "dropImageHere": "Довуци слику овде да би је додао", - "noGameSelected": "Ниједна игра није изабрана", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Кликни на игру да би га изабрао.", "deleteAdditionalApplication": "Избриши додатну апликацију", "deleteGameAndAdditionalApps": "Избриши игру (и додатне апликације)", @@ -288,7 +299,10 @@ "saveAndRestart": "Сачувај и поново покрени", "thereAreNoGames": "Нема игара.", "noGameMatchedDesc": "Ниједан наслов игре није одговарао са твојом претрагом.", - "noGameMatchedSearch": "Покушај да тражиш нешто мање рестриктивно." + "noGameMatchedSearch": "Покушај да тражиш нешто мање рестриктивно.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Име", @@ -413,7 +427,9 @@ "installingFiles": "Инсталирам фајлове...", "complete": "Готово", "exportMetaEditTitle": "Извоз измена метаподатка", - "exportMetaEditDesc": "Изаберите све поставке за изношење:" + "exportMetaEditDesc": "Изаберите све поставке за изношење:", + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Прегледај симболичну слику у фасцикли", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Игре", + "arcadeSingular": "Game", "arcadePlural": "Све игре", "theatre": "Анимације", + "theatreSingular": "Animation", "theatrePlural": "Све анимације", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Све NG анимације" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Логотипи и слике екрана", "screenshotsDesc": "Додаје логотип за преглед у решетки и слике екрана за све игре." } -} \ No newline at end of file +} diff --git a/lang/sv-SE.json b/lang/sv-SE.json index 0ecee1b08..7e5d92c34 100644 --- a/lang/sv-SE.json +++ b/lang/sv-SE.json @@ -4,8 +4,16 @@ "configHeader": "Inställningar", "configDesc": "(Du kan behöva klicka på \"Spara & starta om\" för att vissa ändringar ska börja gälla)", "preferencesHeader": "Alternativ", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "Aktivera redigering", "enableEditingDesc": "Aktivera redigering av spel och andra applikationer. Visar också flikar relaterade till redigering.", "onDemandImages": "Bilder efter förfrågan", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Vilket språk som ska användas istället om det valda språket saknar en översättning.", "auto": "Auto ({0})", "none": "Inget", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Sökväg till Flashpoint", "flashpointPathDesc": "Sökväg till mappen där Flashpoint ligger (kan vara relativ)", @@ -23,15 +32,15 @@ "libraries": "Arkiv", "randomLibraries": "Slumpmässigt arkiv", "randomLibrariesDesc": "Arkiv att använda för slumpmässiga bilder på hemsidan.", - "updateSource": "Update Source", + "updateSource": "Uppdatera källa", "platforms": "Plattformar", "nativePlatforms": "Nativa plattformar", "nativePlatformsDesc": "Använd versioner av dessa plattformar som är gjorda för ditt system från början. (Om ingen finns används Wine istället)", - "tagFilterGroups": "Tag Filter Groups", - "tagFilterGroupsDesc": "These groups will filter suggestions and games out of search results", - "editTagFilter": "Edit Tag Filter Group", - "duplicateTagFilter": "Duplicate Tag Filter Group", - "deleteTagFilter": "Delete Tag Filter Group", + "tagFilterGroups": "Tagga filtergrupper", + "tagFilterGroupsDesc": "Dessa grupper filtrerar förslag och spel från sökresultaten", + "editTagFilter": "Redigera tagg filtergrupp", + "duplicateTagFilter": "Duplicera tagg filtergrupp", + "deleteTagFilter": "Ta bort tagg filtergrupp", "appPathOverrides": "Appens sökväg åsidosätter", "appPathOverridesDesc": "Åsidosätter sökvägen till vänster med den till höger när du startar spel.", "visualsHeader": "Utseende", @@ -57,8 +66,8 @@ "extLogoSets": "Logotyp Set(s)", "extApplications": "Applikation(er)", "saveAndRestart": "Spara och starta om", - "saveAndClose": "Save and Close", - "tagFilterGroupEditor": "Tag Filter Group Editor" + "saveAndClose": "Spara och stäng", + "tagFilterGroupEditor": "Tag filtergrupp Editor" }, "home": { "updateHeader": "Uppdatering", @@ -79,6 +88,7 @@ "helpInfo": "Behöver du hjälp? {0}.", "help": "Läs vår readme", "upgradesHeader": "Uppgraderingar", + "updateFeedHeader": "News Feed", "installComplete": "Installationen är klar", "alreadyInstalled": "Redan installerad", "download": "Ladda ned", @@ -103,7 +113,8 @@ "clearLog": "Rensa logg", "copy404Urls": "Kopiera 404 URLs", "uploadLog": "Ladda up loggar", - "copiedToClipboard": "Kopierad till Urklipp" + "copiedToClipboard": "Kopierad till Urklipp", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Start", @@ -176,10 +187,10 @@ "exportTagsDesc": "Skapar en fil full av taggar och tagg-kategorier som kan användas av 'Importera taggar'", "importTags": "Importera taggar", "importTagsDesc": "Importerar taggar från en fil som skapats av \"Exportera taggar\"", - "migrateExtremeGames": "Migrate Extreme Games", - "migrateExtremeGamesDesc": "Removes 'Extreme' from any games and assigns the 'LEGACY-Extreme' tag to them", - "massImportGameData": "Mass Import Game Data", - "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", + "migrateExtremeGames": "Migrera extrema spel", + "migrateExtremeGamesDesc": "Tar bort 'Extreme' från alla spel och tilldelar dem 'LEGACY-Extreme' taggen", + "massImportGameData": "Massa importera speldata", + "massImportGameDataDesc": "Massa importerar spel datapaket, försöker matcha deras filnamn till ett spel UUID.", "servicesHeader": "Bakgrundsprocesser", "servicesMissing": "Det finns inga processer.", "running": "Igång", @@ -258,15 +269,15 @@ "searching": "Söker...", "library": "Bibliotek", "defaultLibrary": "Standard-bibliotek", - "uninstallGame": "Uninstall Game", - "installed": "Installed", - "notInstalled": "Not Installed", - "legacyGame": "Legacy Game", - "download": "Download", + "uninstallGame": "Avinstallera Spel", + "installed": "Installerad", + "notInstalled": "Inte installerad", + "legacyGame": "Äldre spel", + "download": "Ladda ner", "thumbnail": "Miniatyr", "screenshot": "Skärmdump", "dropImageHere": "Släpp en bild här för att lägga in den", - "noGameSelected": "Inget spel valt", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Klicka på ett spel för att välja det.", "deleteAdditionalApplication": "Ta bort extra program", "deleteGameAndAdditionalApps": "Radera spel (inklusive extra program)", @@ -288,7 +299,10 @@ "saveAndRestart": "Spara & starta om", "thereAreNoGames": "Det finns inga spel.", "noGameMatchedDesc": "Inga spel matchade din sökning.", - "noGameMatchedSearch": "Pröva att söka efter någonting mindre specifikt." + "noGameMatchedSearch": "Pröva att söka efter någonting mindre specifikt.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Namn", @@ -321,7 +335,7 @@ "saveImportedCurations": "Spara importerade kurationer", "keepArchiveKey": "Behåll UUUID för Curation Archive", "symlinkCurationContent": "Symlink Curation Content Folder (Admin prompts i Windows, krävs för MAD4FP)", - "useTagFilters": "Use Tag Filters in Suggestions", + "useTagFilters": "Använd taggfilter i förslag", "openCurationsFolder": "Kurationsmapp", "openCurationsFolderDesc": "Öppna mappen med kurationer i utforskaren", "openExportsFolder": "Exporterade kurationer", @@ -336,8 +350,8 @@ "loadArchiveDesc": "Läs in ett eller flera kurationsarkiv", "loadFolder": "Ladda mapp", "loadFolderDesc": "Läs in en eller flera kurationsmappar", - "scanNewCurationFolders": "Scan For New Curations", - "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", + "scanNewCurationFolders": "Skanna efter nya Curations", + "scanNewCurationFoldersDesc": "Skannar efter curations som har lagts till efter den första skanningen", "noCurations": "Ny kuration", "id": "Kurationsmapp", "heading": "Rubrik", @@ -370,14 +384,14 @@ "unusedPlatform": "\"Plattform\" har ett oanvänt värde. Se till att det är rättstavat!", "nonExistingLibrary": "\"Samling\" är inte satt till namnet på en samling som finns! (Standardvärdet kommer att användas)", "nonContentFolders": "Kurationsmappen innehåller andra mappar än bara content. Ska de vara där?", - "noTags": "There are no tags on this curation.", - "noSource": "'Source' is a required field.", - "unenteredTag": "The Tags text field hasn't been submitted.", - "noLogo": "There is no logo on this curation.", - "noScreenshot": "There is no screenshot on this curation.", + "noTags": "Det finns inga taggar på denna curation.", + "noSource": "'Källa' är ett obligatoriskt fält.", + "unenteredTag": "Textfältet Taggar har inte skickats in.", + "noLogo": "Det finns ingen logotyp på den här kurationen.", + "noScreenshot": "Det finns ingen skärmdump på den här kurationen.", "ilc_notHttp": "Använd HTTP.", "ilc_nonExistant": "Ange en befintlig fil i din kurations \"content\"-mapp.", - "sort": "Sort Curations (A-Z)" + "sort": "Sortera Curationer (A-Ö)" }, "playlist": { "enterDescriptionHere": "Ange en beskrivning här...", @@ -413,13 +427,15 @@ "installingFiles": "Installerar filer...", "complete": "Klar", "exportMetaEditTitle": "Exportera Meta redigering", - "exportMetaEditDesc": "Välj alla egenskaper att exportera:" + "exportMetaEditDesc": "Välj alla egenskaper att exportera:", + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Visa miniatyr i mapp", "viewScreenshotInFolder": "Visa skärmdump i mapp", "openFileLocation": "Öppna målmapp", - "addToPlaylist": "Add to Playlist", + "addToPlaylist": "Lägg till i spellista", "duplicateMetaOnly": "Duplicera (endast metadata)", "duplicateMetaAndImages": "Duplicera (metadata && bilder)", "copyGameUUID": "Kopiera spel-UUID", @@ -459,43 +475,46 @@ "restartNow": "Starta om nu?", "restartToApplyUpgrade": "Den här uppgraderingen kommer inte att tillämpas förrän du startar om.\nVill du göra det nu?", "areYouSure": "Är du säker?", - "areYouSureDelete": "Are you sure you want to delete this entry?", - "areYouSurePlaylistRemove": "Are you sure you want to remove this entry from the playlist?", + "areYouSureDelete": "Är du säker på att du vill ta bort denna post?", + "areYouSurePlaylistRemove": "Är du säker på att du vill ta bort denna post från spellistan?", "cancel": "Avbryt", "mergePlaylists": "Slå ihop spellistor", - "newPlaylist": "Stay Separate", - "uploadPrivacyWarning": "This will make your Logs available publicly.\nA link will be copied to your clipboard.\n\nAre you sure?", + "newPlaylist": "Stanna separat", + "uploadPrivacyWarning": "Detta kommer att göra dina loggar tillgängliga offentligt.\nEn länk kommer att kopieras till ditt urklipp.\n\nÄr du säker?", "overwriteFileTitle": "Filen finns redan!", "overwriteFileMessage": "En fil med samma namn finns redan. Vill du skriva över den?", "overwriteFileDetail": "Sökväg till fil:", - "deleteTagFilterGroup": "Delete this Tag Filter Group?", - "deleteCuration": "Delete this Curation?", - "importCuration": "Import this Curation?", - "deleteGameImage": "Delete this Game Image?", - "deletePlaylist": "Delete this Playlist?", - "importAllCurations": "Import all Curations?", - "deleteAllCurations": "Delete all Curations?", - "removePlaylistGame": "Remove this Game from the Playlist?", - "deleteGame": "Delete this Game? This cannot be undone.", - "deleteGameData": "Delete this Game Data? This cannot be undone.", - "deleteAddApp": "Delete this Add App?", - "deleteTagCategory": "Delete this Tag Category?", - "deleteTag": "Delete this Tag?", - "deleteTagAlias": "Delete this Tag Alias?", - "deleteSource": "Delete this Source?", - "uninstallGame": "Uninstall this Game?", - "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", - "unableToDeleteGame": "Unable to delete game. Is it in use? Try restarting the launcher.", - "downloadingGame": "Downloading Game...", - "verifyingGame": "Verifying Game...", - "aFewMinutes": "This may take a few minutes." + "deleteTagFilterGroup": "Ta bort denna tagg filtergrupp?", + "deleteCuration": "Ta bort denna Curation?", + "importCuration": "Importera denna Curation?", + "deleteGameImage": "Ta bort den här spelbilden?", + "deletePlaylist": "Ta bort denna spellista?", + "importAllCurations": "Importera alla Curations?", + "deleteAllCurations": "Ta bort alla Curations?", + "removePlaylistGame": "Ta bort detta spel från spellistan?", + "deleteGame": "Ta bort detta spel? Det går inte att ångra.", + "deleteGameData": "Ta bort denna speldata? Det går inte att ångra.", + "deleteAddApp": "Ta bort denna Lägg till app?", + "deleteTagCategory": "Ta bort denna etikettkategori?", + "deleteTag": "Ta bort denna tagg?", + "deleteTagAlias": "Ta bort denna tagg alias?", + "deleteSource": "Ta bort denna källa?", + "uninstallGame": "Avinstallera detta spel?", + "unableToUninstallGameData": "Det går inte att ta bort datapaket. Används det? Prova att starta om launchern.", + "unableToDeleteGame": "Det går inte att ta bort spelet. Är det i bruk? Prova att starta om launchern.", + "downloadingGame": "Laddar ner spelet...", + "verifyingGame": "Verifierar spelet...", + "aFewMinutes": "Detta kan ta några minuter." }, "libraries": { "arcade": "Spel", + "arcadeSingular": "Game", "arcadePlural": "Alla Spel", "theatre": "Animationer", + "theatreSingular": "Animation", "theatrePlural": "Alla Animationer", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Alla NG animationer" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logotyper och skärmdumpar", "screenshotsDesc": "Lägger till logotyper som används i rutnätsvyn samt skärmdumpar för alla spel." } -} \ No newline at end of file +} diff --git a/lang/tr-TR.json b/lang/tr-TR.json index 045bc58b2..0fca52a3c 100644 --- a/lang/tr-TR.json +++ b/lang/tr-TR.json @@ -2,10 +2,18 @@ "name": "Türkçe", "config": { "configHeader": "Yapılandırma", - "configDesc": "(Bazı değişikliklerin meydana gelmesi için 'Kaydet & Yeniden Başlat'a basmanız gerekir)", + "configDesc": "(Bazı değişikliklerin güncellenmesi için 'Kaydet & Yeniden Başlat'a basmanız gerekiyor)", "preferencesHeader": "Tercihler", - "extremeGames": "Ekstrem İçerik", - "extremeGamesDesc": "'Ekstrem' etiket filtreleme grubu ve 'ekstrem' etiket filtreleme grubuna karşılık gelen oyunların gösterilmesine izin ver.", + "extremeGames": "Ekstrem Filtreleri Göster", + "extremeGamesDesc": "Çocuklar için uygun olmayan içeriği kontrol etmek için Ekstrem etiket filtrelerinin değiştirilmesine, oluşturulmasına ve değiştirilmesine izin verir.", + "hideExtremeScreenshots": "Ekstrem Ekran Görüntülerini Gizle", + "hideExtremeScreenshotsDesc": "Ekstrem olarak belirtilmiş içeriğin ekran görüntüleri gizlenir, fotoğrafa tıklanınca gizlenme kaldırılır.", + "fancyAnimations": "Süslü Animasyonlar", + "fancyAnimationsDesc": "Başlatıcıda süslü animasyonları etkinleştirin.", + "searchLimit": "Arama Limiti", + "searchLimitDesc": "Herhangi bir aramada döndürülen sonuç sayısını sınırlayın", + "searchLimitUnlimited": "Sınırsız", + "searchLimitValue": "{0} Sonuçlar", "enableEditing": "Değişikliklere İzin Ver", "enableEditingDesc": "Oyunların ve fazladan eklentilerin düzenlenmesine izin verir. Ayrıca düzenlemeye bağlı seçenekleri gösterir.", "onDemandImages": "İsteğe Bağlı Görüntüler", @@ -16,10 +24,11 @@ "fallbackLanguageDesc": "Tamamlanmamış mevcut dilin yerine hangi dilin kullanılacağını gösterir.", "auto": "Otomatik ({0})", "none": "Yok", + "contentFiltersHeader": "İçerik Filtreleri", "flashpointHeader": "Flashpoint", "flashpointPath": "Flashpoint Yolu", "flashpointPathDesc": "Flashpoint klasörüne yol (komşu olabilir)", - "browse": "Ara", + "browse": "Gözat", "libraries": "Kütüphaneler", "randomLibraries": "Rastgele Kütüphaneler", "randomLibrariesDesc": "Ana sayfada Rastgele Seçimler için kullanılabilen kütüphaneler.", @@ -32,7 +41,7 @@ "editTagFilter": "Etiket Filtreleme Grubunu Düzenle", "duplicateTagFilter": "Etiket Filtreleme Grubunu Kopyala", "deleteTagFilter": "Etiket Filtreleme Grubunu Sil", - "appPathOverrides": "Uygulama Yolu Çiğnemeleri", + "appPathOverrides": "Geçersiz Kılınmış Uygulama Yolları", "appPathOverridesDesc": "Oyunları çalıştırırken soldaki uygulama yolunu sağdakiyle geçersiz kılar.", "visualsHeader": "Görseller", "useCustomTitleBar": "Özel bir Başlık kullan", @@ -67,7 +76,7 @@ "updateAvailable": "Güncelleme Mevcut", "upToDate": "Güncel.", "downloadingUpdate": "Güncelleme İndiriliyor...", - "quickStartHeader": "Hızlı Başlatma", + "quickStartHeader": "Hızlı Başlat", "hallOfFameInfo": "En iyisinin en iyisini mi istiyorsun? {0}'a göz at!", "hallOfFame": "En İyiler", "allGamesInfo": "Oynayacak bir şeyler mi bakıyorsun? {0}'a bak.", @@ -79,6 +88,7 @@ "helpInfo": "Yardım mı lazım? {0}.", "help": "Kullanım kılavuzunu oku", "upgradesHeader": "Güncellemeler", + "updateFeedHeader": "Haber Akışı", "installComplete": "İndirme Tamamlandı", "alreadyInstalled": "Zaten Yüklü", "download": "İndir", @@ -91,7 +101,7 @@ "plannedFeatures": "Planladığımız içeriklere göz at!", "notesHeader": "Notlar", "notes": "Eğer sıkıntılarınız varsa kullanım kılavuzunu okumayı unutmayın.", - "linuxSupport": "Linux'ta Sıkıntı? {0}", + "linuxSupport": "Linux'taki Sıkıntılar? {0}", "linuxSupportLinkText": "SSS'i Aç", "randomPicks": "Rastgele Seçimler", "rerollPicks": "Seçimleri Yeniden Yuvarla" @@ -103,7 +113,8 @@ "clearLog": "Kayıtları Temizle", "copy404Urls": "404 URL'lerini kopyala", "uploadLog": "Kayıtları Yükle", - "copiedToClipboard": "Panoya Kopyalandı" + "copiedToClipboard": "Panoya Kopyalandı", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Ana Sayfa", @@ -127,7 +138,7 @@ "searchResults": "Arama Sonuçları" }, "filter": { - "dateAdded": "Eklenen Tarih", + "dateAdded": "Eklenme Tarihi", "dateModified": "Düzenlenme Tarihi", "platform": "Platform", "series": "Seriler", @@ -140,17 +151,17 @@ "developer": { "developerHeader": "Geliştirici", "developerDesc": "Bu tüm kullanışlı geliştirici aletlerinin gideceği yerdir.", - "checkMissingImages": "Eksik Resimleri Göster", + "checkMissingImages": "Eksik Resimleri Kontrol Et", "checkMissingImagesDesc": "Ekran görüntüsü ya da kapak resmi olmayan oyunları listele", - "checkGameIds": "Oyun IDlerini Göster", + "checkGameIds": "Oyun ID'lerini Kontrol Et", "checkGameIdsDesc": "Tüm geçersiz veya aynı IDlere sahip oyunları listele", - "checkGameTitles": "Oyun Başlıklarını Göster", + "checkGameTitles": "Oyun Başlıklarını Kontrol Et", "checkGameTitlesDesc": "Aynı platformdaki aynı başlıklara sahip olan oyunları listele", - "checkGameFields": "Oyun Alanlarını Göster", + "checkGameFields": "Oyun Alanlarını Kontrol Et", "checkGameFieldsDesc": "Boş alana sahip olan oyunları listele (ya da boş olmaması gereken alanları)", - "checkPlaylists": "Oyun Listelerini Göster", + "checkPlaylists": "Oyun Listelerini Kontrol Et", "checkPlaylistsDesc": "Eksik, geçersiz ya da aynı IDlere sahip oyun listelerini, ya da eksik, geçersiz ya da aynı IDlere sahip oyun girişlerini listele", - "checkGameFileLocation": "Oyun Dosya Konumunu Göster", + "checkGameFileLocation": "Oyun Dosya Konumunu Kontrol Et", "checkGameFileLocationDesc": "Dosya yollarına ayrıştırılamayan başlatma komutlarına sahip tüm oyunları listeleyin (Bu 'Dosya Konumunu Aç' fonksiyonu ile alakalı, oyunu başlatma ile değil)", "checkMissingExecMappings": "Eksik yürütme haritalarını denetle", "checkMissingExecMappingsDesc": "Win32, linux ve darwin için yürütme haritası eksiz olan eşsiz yürütücüleri listele", @@ -175,7 +186,7 @@ "exportTags": "Etiketleri Dışa Aktar", "exportTagsDesc": "'Etiketleri İçe Aktar' için kullanılabilen tamamen etiketler ve etiket kategorileriyle dolu bir dosya yaratır", "importTags": "Etiketleri İçe Aktar", - "importTagsDesc": "Etiketleri 'Etiketleri Dışa Aktar' dan yaratılan bir dosyadan içe aktarır", + "importTagsDesc": "\"Etiketleri Dışa Aktar\" ın Oluşturduğu bir Dosyadan Etiketleri İçe Aktarır", "migrateExtremeGames": "Ekstrem Oyunları Göç Ettir", "migrateExtremeGamesDesc": "Oyunlardan 'Ekstrem' etiketini kaldırır ve 'ESKİ-Ekstrem' etiketini atar", "massImportGameData": "Oyun Verilerini Topluca İçeri Aktar", @@ -199,11 +210,11 @@ "flashpoint": "BlueMaxima's Flashpoint", "flashpointDesc": "Bu saklama projesi başlatıcısının amacı, bir arşiv, müze ve ağ-tabanlı İnternet oyunlarının oynanabilir bir koleksiyonu olmaktır.", "website": "Web sitesi", - "flashpointLauncher": "Flashpoint Başlatıcı", + "flashpointLauncher": "Flashpoint Başlatıcısı", "flashpointLauncherDesc": "Flashpoint projesinden oyunlar aramak, düzenlemek ve oynamak için kullanılan açık-kaynak bir bilgisayar uygulamasıdır.", "version": "Sürüm", "license": "Lisans", - "licenseInfo": "MIT (daha fazla bilgi için 'LICENSE' dosyasını oku)", + "licenseInfo": "MIT (Daha fazla bilgi için 'LICENSE' dosyasını oku)", "creditsHeader": "Teşekkürler", "specialThanks": "Özel Teşekkürler" }, @@ -266,7 +277,7 @@ "thumbnail": "Küçükresim", "screenshot": "Ekran görüntüsü", "dropImageHere": "Eklemek için bir resmi buraya bırakın", - "noGameSelected": "Seçili Oyun Yok", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Seçmek için bir oyuna tıklayın.", "deleteAdditionalApplication": "Ek Uygulamaları Sil", "deleteGameAndAdditionalApps": "Oyunu Sil (ve Ek Uygulamaları)", @@ -288,7 +299,10 @@ "saveAndRestart": "Kaydet & Yeniden Başlat", "thereAreNoGames": "Oyun yok.", "noGameMatchedDesc": "Aramanızla eşleşen bir oyun yok.", - "noGameMatchedSearch": "Daha az kısıtlayıcı bir şey aramayı deneyin." + "noGameMatchedSearch": "Daha az kısıtlayıcı bir şey aramayı deneyin.", + "mountParameters": "Bağlama Parametresi", + "noMountParameters": "Bağlama Parametresi yok", + "showExtremeScreenshot": "Ekstrem Ekran Görüntüsünü Göster" }, "tags": { "name": "Adı", @@ -321,7 +335,7 @@ "saveImportedCurations": "İçe Aktarılmış Kürasyonları Kaydet", "keepArchiveKey": "Kürasyon Arşiv UUID'sini Sakla", "symlinkCurationContent": "Symlink Kürasyon İçeriği Klasörü (Windows'ta yönetici istemleri, MAD4FP için gerekli)", - "useTagFilters": "Önerilerde Etiket Filtreleri Kullan", + "useTagFilters": "Önerilerde Etiket Filtrelerini Kullan", "openCurationsFolder": "Kürasyonlar Klasörü", "openCurationsFolderDesc": "Kürasyonlar klasörünü dosya gezgininde aç", "openExportsFolder": "Dışa Aktarılmış Kürasyonlar", @@ -365,7 +379,7 @@ "noLaunchCommand": "'Çalışma Komudu' zorunlu bir alandır. İçe aktarmadan lütfen doldurunuz.", "invalidLaunchCommand": "'Çalışma Komudu' şu URL gereksinimlerini karşılamıyor:", "releaseDateInvalid": "'Yayınlanma Tarihi' YYYY-AA-GG şeklinde olmalıdır (ay ve günler isteğe bağlı).", - "unusedApplicationPath": "'Uygulama Yolu' kullanılmayan bir değer. Doğru yazıldığından emin olun!", + "unusedApplicationPath": "'Uygulama Yolu' kullanılmayan bir değere sahip. Doğru yazıldığından emin olun!", "unusedTags": "Bu etiketler başka bir oyunda yok. Doğru mu?", "unusedPlatform": "'Platform' kullanılmayan bir değere sahip. Doğru yazıldığından emin olun!", "nonExistingLibrary": "'Library' var olan bir arşivin ismi değil! (Varsayılan kullanılacak)", @@ -413,7 +427,9 @@ "installingFiles": "Dosyalar Yükleniyor...", "complete": "Tamamlandı", "exportMetaEditTitle": "Meta Düzenlemesini Dışa Aktar", - "exportMetaEditDesc": "Dışa aktarılacak tüm özellikleri seç:" + "exportMetaEditDesc": "Dışa aktarılacak tüm özellikleri seç:", + "showImage": "Resmi Göster", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Klasördeki küçükresmi İncele", @@ -447,9 +463,9 @@ "selectFolder": "Klasör Seç", "selectScreenshot": "Ekran Görüntüsü Seç", "selectThumbnail": "Küçükresim Seç", - "selectCurationFolder": "Kürasyon Klasörü ya da Klasörleri Seç", - "selectCurationArchive": "Kürasyon Arşivi ya da Arşivleri Seç", - "selectCurationMeta": "Kürasyon Metası ya da Metaları Seç", + "selectCurationFolder": "Kürasyon Klasörü ya da Klasörlerini Seç", + "selectCurationArchive": "Kürasyon Arşivi ya da Arşivlerini Seç", + "selectCurationMeta": "Kürasyon Metası ya da Metalarını Seç", "selectPlaylistToImport": "İçe Aktarılacak Oynama Listesini Seç", "selectFileToExportPlaylist": "Oynama Listesinin kaydedileceği dosyayı seç", "dataRequired": "Ek Bilgi Gerekli", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "Oyunlar", + "arcadeSingular": "Game", "arcadePlural": "Tüm Oyunlar", "theatre": "Animasyonlar", + "theatreSingular": "Animation", "theatrePlural": "Tüm Animasyonlar", "auditorium": "NG Auditorium", + "auditoriumSingular": "Animation", "auditoriumPlural": "Tüm NG Animasyonları" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Logolar & Ekran Görüntüleri", "screenshotsDesc": "Tüm oyunlarda Izgara görünüm ve ekran görüntüsü için logolar ekler." } -} \ No newline at end of file +} diff --git a/lang/uk-UA.json b/lang/uk-UA.json index 2920a79b0..2253a6c14 100644 --- a/lang/uk-UA.json +++ b/lang/uk-UA.json @@ -4,8 +4,16 @@ "configHeader": "Налаштування", "configDesc": "(Ви повинні натиснути 'Зберегти і перезапуск', щоб деякі зміни набрали сили)", "preferencesHeader": "Властивості", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Show Extreme Filters", + "extremeGamesDesc": "Allows Extreme tag filters to be toggled, created and modified to control content unsuitable for children.", + "hideExtremeScreenshots": "Hide Extreme Screenshots", + "hideExtremeScreenshotsDesc": "Hides screenshots of Extreme tagged content, can be unhidden with a click on the image box.", + "fancyAnimations": "Fancy Animations", + "fancyAnimationsDesc": "Enable fancy animations in the launcher.", + "searchLimit": "Search Limit", + "searchLimitDesc": "Limit the number of results returned in any search", + "searchLimitUnlimited": "Unlimited", + "searchLimitValue": "{0} Results", "enableEditing": "Увімкнути редагування", "enableEditingDesc": "Увімкніть редагування ігор та додаткових програм. Також показує вкладки, пов'язані з редагуванням.", "onDemandImages": "За запитами зображення", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Яку мову для використання замість неповної поточної мови.", "auto": "Авто ({0})", "none": "Без ефекту", + "contentFiltersHeader": "Content Filters", "flashpointHeader": "Flashpoint", "flashpointPath": "Шлях до точки прошивки", "flashpointPathDesc": "Шлях до папки Flashpoint (може бути відносним)", @@ -23,15 +32,15 @@ "libraries": "Бібліотеки", "randomLibraries": "Випадкові бібліотеки", "randomLibrariesDesc": "Бібліотеки для використання випадкових птахів на головній сторінці.", - "updateSource": "Update Source", + "updateSource": "Джерело оновлення", "platforms": "Платформи", "nativePlatforms": "Рідні платформи", "nativePlatformsDesc": "Використовувати нативні версії цих платформ. (Якщо не доступно, то вона використовуватиме Перемогу)", - "tagFilterGroups": "Tag Filter Groups", - "tagFilterGroupsDesc": "These groups will filter suggestions and games out of search results", - "editTagFilter": "Edit Tag Filter Group", - "duplicateTagFilter": "Duplicate Tag Filter Group", - "deleteTagFilter": "Delete Tag Filter Group", + "tagFilterGroups": "Групи фільтрів міток", + "tagFilterGroupsDesc": "Ці групи будуть фільтрувати пропозиції та імітувати результати пошуку", + "editTagFilter": "Редагувати групу фільтрів міток", + "duplicateTagFilter": "Дублювати групу фільтрів міток", + "deleteTagFilter": "Видалити групу фільтрів міток", "appPathOverrides": "Перевизначення шляху програми", "appPathOverridesDesc": "Перевизначає контур програми ліворуч під час запуску ігор.", "visualsHeader": "Візуали", @@ -57,8 +66,8 @@ "extLogoSets": "Логотип сет(и)", "extApplications": "Застосунок(и)", "saveAndRestart": "Зберегти і перезавантажити", - "saveAndClose": "Save and Close", - "tagFilterGroupEditor": "Tag Filter Group Editor" + "saveAndClose": "Зберегти та закрити", + "tagFilterGroupEditor": "Редактор груп теґів" }, "home": { "updateHeader": "Оновити лаунчер", @@ -79,6 +88,7 @@ "helpInfo": "Потрібна допомога? {0}.", "help": "Прочитати інструкцію", "upgradesHeader": "Покращення", + "updateFeedHeader": "News Feed", "installComplete": "Встановлення завершено", "alreadyInstalled": "Вже встановлено", "download": "Звантажити", @@ -103,7 +113,8 @@ "clearLog": "Очистити журнал", "copy404Urls": "Копіювати 404 адреси", "uploadLog": "Завантажити журнал", - "copiedToClipboard": "Скопійовано до буфера обміну" + "copiedToClipboard": "Скопійовано до буфера обміну", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "Домашній екран", @@ -176,10 +187,10 @@ "exportTagsDesc": "Створює файл повного тегів і категорій тегів, що можуть бути використані 'Імпорт тегів'", "importTags": "Імпорт тегів", "importTagsDesc": "Імпорт тегів з файлу, створеного 'Export Tags'", - "migrateExtremeGames": "Migrate Extreme Games", - "migrateExtremeGamesDesc": "Removes 'Extreme' from any games and assigns the 'LEGACY-Extreme' tag to them", - "massImportGameData": "Mass Import Game Data", - "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", + "migrateExtremeGames": "Мігрувати ігри екстремальних", + "migrateExtremeGamesDesc": "Видаляє \"Extreme\" з усіх ігор і призначає тег \"LEGACY-Extreme\"", + "massImportGameData": "Масовий імпорт ігрових даних", + "massImportGameDataDesc": "Масове імпортування ігрових пакетів, намагаючись знайти назву файлу в UUID ігор.", "servicesHeader": "Фонові послуги", "servicesMissing": "Там не немає послуг.", "running": "Біг", @@ -258,15 +269,15 @@ "searching": "Пошук...", "library": "Бібліотека", "defaultLibrary": "Типова бібліотека", - "uninstallGame": "Uninstall Game", - "installed": "Installed", - "notInstalled": "Not Installed", - "legacyGame": "Legacy Game", - "download": "Download", + "uninstallGame": "Видалити гру", + "installed": "Встановлено", + "notInstalled": "Не встановлено", + "legacyGame": "Застаріла гра", + "download": "Звантажити", "thumbnail": "Ескіз", "screenshot": "Знімок екрана", "dropImageHere": "Перетягніть зображення тут, щоб додати його", - "noGameSelected": "Жодна гра не вибрана", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "Клацни на гру, щоб вибрати її.", "deleteAdditionalApplication": "Видалити додаткову програму", "deleteGameAndAdditionalApps": "Видалити гру (та додаткові програми)", @@ -288,7 +299,10 @@ "saveAndRestart": "Зберегти і перезапустити", "thereAreNoGames": "Немає ігор.", "noGameMatchedDesc": "Жодна гра не відповідає назві вашого пошуку.", - "noGameMatchedSearch": "Спробуйте пошукати менше обмежуючих." + "noGameMatchedSearch": "Спробуйте пошукати менше обмежуючих.", + "mountParameters": "Mount Parameters", + "noMountParameters": "No Mount Parameters", + "showExtremeScreenshot": "Show Extreme Screenshot" }, "tags": { "name": "Ім'я", @@ -321,7 +335,7 @@ "saveImportedCurations": "Зберегти імпортовані завіси", "keepArchiveKey": "Залишити архів з кураторами UID", "symlinkCurationContent": "Тека символічного вмісту (запит адміністратора на Windows, необхідно для MAD4FP)", - "useTagFilters": "Use Tag Filters in Suggestions", + "useTagFilters": "Використовувати фільтри тегів у пропозиціях", "openCurationsFolder": "Папка курсорів", "openCurationsFolderDesc": "Відкрити папку Curations у провіднику", "openExportsFolder": "Експортована крива", @@ -336,8 +350,8 @@ "loadArchiveDesc": "Завантажити один або кілька архівів Curation", "loadFolder": "Завантажити теку", "loadFolderDesc": "Завантажте один або кілька папок кур'єру", - "scanNewCurationFolders": "Scan For New Curations", - "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", + "scanNewCurationFolders": "Сканувати за новими курсами", + "scanNewCurationFoldersDesc": "Сканування додаткового меню після початкового сканування", "noCurations": "Немає Куратій", "id": "Папка Куратії", "heading": "Напрямок", @@ -370,14 +384,14 @@ "unusedPlatform": "'Платформа' має невикористовуване значення. Переконайтеся, що воно написано правильно!", "nonExistingLibrary": "'бібліотека' не є назвою існуючої бібліотеки! (За замовчуванням буде використовуватися)", "nonContentFolders": "Не-теки, які були знайдені в папці Куратація. Чи повинні вони там бути?", - "noTags": "There are no tags on this curation.", - "noSource": "'Source' is a required field.", - "unenteredTag": "The Tags text field hasn't been submitted.", - "noLogo": "There is no logo on this curation.", - "noScreenshot": "There is no screenshot on this curation.", + "noTags": "На цьому проклятті немає тегів.", + "noSource": "'Джерело' є обов'язковим полем.", + "unenteredTag": "Текстове поле тегів не було надіслано.", + "noLogo": "На цій програмі немає логотипу.", + "noScreenshot": "На цій куратації немає скріншоту.", "ilc_notHttp": "Використовувати HTTP.", "ilc_nonExistant": "Перейдіть на наявний файл у теці 'content' вашого куратора.", - "sort": "Sort Curations (A-Z)" + "sort": "Сортувати куратори (A-Z)" }, "playlist": { "enterDescriptionHere": "Введіть опис...", @@ -413,13 +427,15 @@ "installingFiles": "Встановлення файлів...", "complete": "Complete", "exportMetaEditTitle": "Експорт мета-редагування", - "exportMetaEditDesc": "Виберіть усі властивості для експорту:" + "exportMetaEditDesc": "Виберіть усі властивості для експорту:", + "showImage": "Show Image", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "Переглянути мініатюру в папці", "viewScreenshotInFolder": "Переглянути скріншот в папці", "openFileLocation": "Відкрити розташування файлу", - "addToPlaylist": "Add to Playlist", + "addToPlaylist": "Додати до списку відтворення", "duplicateMetaOnly": "Duplicate (Meta Only)", "duplicateMetaAndImages": "Duplicate (Meta && Images)", "copyGameUUID": "Копіювати UUID Гри", @@ -459,43 +475,46 @@ "restartNow": "Перезавантажити зараз?", "restartToApplyUpgrade": "Це оновлення не буде застосовано до перезапуску.\nБажаєте зробити це зараз?", "areYouSure": "Ви впевнені?", - "areYouSureDelete": "Are you sure you want to delete this entry?", - "areYouSurePlaylistRemove": "Are you sure you want to remove this entry from the playlist?", + "areYouSureDelete": "Ви впевнені у тому, що хочете видалити цей запис?", + "areYouSurePlaylistRemove": "Ви впевнені, що хочете видалити цей запис зі списку відтворення?", "cancel": "Скасувати", "mergePlaylists": "Об'єднати списки відтворення", - "newPlaylist": "Stay Separate", - "uploadPrivacyWarning": "This will make your Logs available publicly.\nA link will be copied to your clipboard.\n\nAre you sure?", + "newPlaylist": "Залишатись окремо", + "uploadPrivacyWarning": "Це зробить ваші логи доступним публічно.\nПосилання буде скопійовано до вашого буфера обміну.\n\nВи впевнені?", "overwriteFileTitle": "Файл вже існує!", "overwriteFileMessage": "Файл з таким ім'ям вже існує. Ви хочете перезаписати його?", "overwriteFileDetail": "Шлях до файлу:", - "deleteTagFilterGroup": "Delete this Tag Filter Group?", - "deleteCuration": "Delete this Curation?", - "importCuration": "Import this Curation?", - "deleteGameImage": "Delete this Game Image?", - "deletePlaylist": "Delete this Playlist?", - "importAllCurations": "Import all Curations?", - "deleteAllCurations": "Delete all Curations?", - "removePlaylistGame": "Remove this Game from the Playlist?", - "deleteGame": "Delete this Game? This cannot be undone.", - "deleteGameData": "Delete this Game Data? This cannot be undone.", - "deleteAddApp": "Delete this Add App?", - "deleteTagCategory": "Delete this Tag Category?", - "deleteTag": "Delete this Tag?", - "deleteTagAlias": "Delete this Tag Alias?", - "deleteSource": "Delete this Source?", - "uninstallGame": "Uninstall this Game?", - "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", - "unableToDeleteGame": "Unable to delete game. Is it in use? Try restarting the launcher.", - "downloadingGame": "Downloading Game...", - "verifyingGame": "Verifying Game...", - "aFewMinutes": "This may take a few minutes." + "deleteTagFilterGroup": "Видалити цю групу фільтрів тегів?", + "deleteCuration": "Видалити цю керування?", + "importCuration": "Видалити цю керування?", + "deleteGameImage": "Видалити це зображення для гри?", + "deletePlaylist": "Видалити цей список відтворення?", + "importAllCurations": "Імпортувати всі курси?", + "deleteAllCurations": "Видалити всі курси?", + "removePlaylistGame": "Видалити цю гру зі списку відтворення?", + "deleteGame": "Видалити гру? Цю дію неможливо відмінити.", + "deleteGameData": "Видалити дані цієї гри? Цю дію неможливо відмінити.", + "deleteAddApp": "Видалити цей додаток додавання?", + "deleteTagCategory": "Видалити цей розділ тегів?", + "deleteTag": "Видалити цей тег?", + "deleteTagAlias": "Видалити цей тег?", + "deleteSource": "Видалити це джерело?", + "uninstallGame": "Видалити цю гру?", + "unableToUninstallGameData": "Не вдалося видалити пакет даних. Використовується повторний запуск програми.", + "unableToDeleteGame": "Не вдалося видалити гру. Використовується для запуску? Спробуйте перезапустити лаунчер.", + "downloadingGame": "Завантаження Гри...", + "verifyingGame": "Перевірка ігри...", + "aFewMinutes": "Це може зайняти кілька хвилин." }, "libraries": { "arcade": "Ігри", + "arcadeSingular": "Game", "arcadePlural": "Всі ігри", "theatre": "Анімація", + "theatreSingular": "Animation", "theatrePlural": "Всі анімації", "auditorium": "Аудиторія", + "auditoriumSingular": "Animation", "auditoriumPlural": "Анімація всіх NG" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "Логотипи та скріншоти", "screenshotsDesc": "Додає логотипи для перегляду таблиць і знімків екрану для всіх ігор." } -} \ No newline at end of file +} diff --git a/lang/vi-VN.json b/lang/vi-VN.json index 9b57164c2..efaa56f79 100644 --- a/lang/vi-VN.json +++ b/lang/vi-VN.json @@ -1,11 +1,19 @@ { - "name": "tiếng Việt", + "name": "Tiếng Việt", "config": { "configHeader": "Cài đặt", - "configDesc": "(Bạn phải nhấn 'Lưu và Khởi động lại' thì vài thay đổi mới có hiệu lực)", + "configDesc": "(Bạn phải nhấn 'Lưu & Khởi động lại' thì vài thay đổi mới có hiệu lực)", "preferencesHeader": "Tuỳ chọn", - "extremeGames": "Extreme Content", - "extremeGamesDesc": "Allow tags inside 'Extreme' tag filter groups and corresponding games to be shown.", + "extremeGames": "Hiện bộ lọc nội dung phản cảm", + "extremeGamesDesc": "Cho phép bật - tắt, tạo và sửa các bộ lọc nhãn Phản cảm để kiểm soát nội dung không phù hợp với trẻ em.", + "hideExtremeScreenshots": "Ẩn ảnh chụp màn hình phản cảm", + "hideExtremeScreenshotsDesc": "Ẩn ảnh chụp màn hình của nội dung có nhãn Phản cảm, có thể hiện chỉ với một cái nhấp chuột lên khung ảnh.", + "fancyAnimations": "Hoạt ảnh bắt mắt", + "fancyAnimationsDesc": "Bật hoạt ảnh bắt mắt trong trình khởi chạy.", + "searchLimit": "Giới hạn tìm kiếm", + "searchLimitDesc": "Giới hạn số kết quả đã trả về trong bất cứ lần tìm kiếm nào", + "searchLimitUnlimited": "Không giới hạn", + "searchLimitValue": "{0} kết quả", "enableEditing": "Bật khả năng chỉnh sửa", "enableEditingDesc": "Cho phép sửa trò chơi và phần mềm đi kèm. Cũng hiện thêm các mục liên quan đến việc chỉnh sửa.", "onDemandImages": "Tải hình khi cần", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "Ngôn ngữ nào sẽ được sử dụng để thay thế cho ngôn ngữ chưa dịch xong.", "auto": "Tự động ({0})", "none": "Không có", + "contentFiltersHeader": "Bộ lọc nội dung", "flashpointHeader": "Flashpoint", "flashpointPath": "Đường dẫn Flashpoint", "flashpointPathDesc": "Đường dẫn đến thư mục Flashpoint (can be relative)", @@ -27,11 +36,11 @@ "platforms": "Nền tảng", "nativePlatforms": "Nền tảng gốc", "nativePlatformsDesc": "Dùng bản gốc của các nền tảng này. (Nếu không có sẵn thì sẽ dùng Wine)", - "tagFilterGroups": "Tag Filter Groups", - "tagFilterGroupsDesc": "These groups will filter suggestions and games out of search results", - "editTagFilter": "Edit Tag Filter Group", - "duplicateTagFilter": "Duplicate Tag Filter Group", - "deleteTagFilter": "Delete Tag Filter Group", + "tagFilterGroups": "Nhóm bộ lọc nhãn", + "tagFilterGroupsDesc": "Các nhóm này sẽ ẩn các đề xuất và trò chơi khỏi kết quả tìm kiếm", + "editTagFilter": "Chỉnh sửa nhóm bộ lọc nhãn", + "duplicateTagFilter": "Tạo bản sao của nhóm bộ lọc nhãn", + "deleteTagFilter": "Xóa nhóm bộ lọc nhãn", "appPathOverrides": "Ghi đè đường dẫn phần mềm", "appPathOverridesDesc": "Ghi đè đường dẫn phần mềm ở bên trái bằng đường dẫn ở bên phải khi khởi chạy trò chơi.", "visualsHeader": "Đồ hoạ", @@ -47,7 +56,7 @@ "showDeveloperTab": "Hiện mục Nhà phát triển", "showDeveloperTabDesc": "Hiện mục 'Nhà phát triển'. Hữu dụng nhất cho lập trình viên và người đóng góp.", "server": "Máy chủ", - "serverDesc": "Tiến trình máy chủ nào sẽ được khởi động khi mở trình khởi chạy.", + "serverDesc": "Tiến trình máy chủ sẽ khởi động khi mở trình khởi chạy.", "metadataServerHost": "Máy chủ lưu trữ siêu dữ liệu", "metadataServerHostDesc": "Máy chủ của máy chủ siêu dữ liệu. Được sử dụng để đồng bộ hóa thông tin trò chơi mới và cập nhật với trình khởi chạy.", "extensionsHeader": "Phần mở rộng", @@ -58,7 +67,7 @@ "extApplications": "Phần mềm", "saveAndRestart": "Lưu và Khởi động lại", "saveAndClose": "Lưu và thoát", - "tagFilterGroupEditor": "Tag Filter Group Editor" + "tagFilterGroupEditor": "Trình chỉnh sửa nhóm bộ lọc nhãn" }, "home": { "updateHeader": "Cập nhật trình khởi chạy", @@ -73,12 +82,13 @@ "allGamesInfo": "Bạn muốn chơi trò gì đó? Xem {0}.", "allGames": "Tất cả trò chơi", "allAnimationsInfo": "Chỉ muốn coi thứ gì đó? Xem {0}.", - "allAnimations": "Tất cả hoạt hình", + "allAnimations": "Tất cả Hoạt hình", "configInfo": "Muốn thay đổi gì đó? Hãy sang {0}.", "config": "Cài đặt", "helpInfo": "Cần được giúp? {0}.", "help": "Đọc hướng dẫn", "upgradesHeader": "Nâng cấp", + "updateFeedHeader": "Bảng tin", "installComplete": "Cài đặt hoàn tất", "alreadyInstalled": "Đã cài rồi", "download": "Tải về", @@ -103,7 +113,8 @@ "clearLog": "Xoá bản ghi", "copy404Urls": "Chép URL 404", "uploadLog": "Tải lên bản ghi", - "copiedToClipboard": "Đã chép vào bộ nhớ tạm" + "copiedToClipboard": "Đã chép vào bộ nhớ tạm", + "openLogsWindow": "Mở cửa sổ nhật kí" }, "app": { "home": "Trang chủ", @@ -176,10 +187,10 @@ "exportTagsDesc": "Tạo một tệp chứa tất cả nhãn và danh mục nhãn mà có thể dùng tính năng 'Nhập nhãn' để nhập vào phần mềm được", "importTags": "Nhập nhãn", "importTagsDesc": "Nhập nhãn từ một tệp được tạo bởi 'Xuất nhãn'", - "migrateExtremeGames": "Migrate Extreme Games", - "migrateExtremeGamesDesc": "Removes 'Extreme' from any games and assigns the 'LEGACY-Extreme' tag to them", - "massImportGameData": "Mass Import Game Data", - "massImportGameDataDesc": "Mass imports Game Data packs, attempting to match their filename to a games UUID.", + "migrateExtremeGames": "Dịch chuyển trò chơi phản cảm", + "migrateExtremeGamesDesc": "Xóa 'Phản cảm' khỏi mọi trò chơi và gán thẻ 'LEGACY-Extreme' cho chúng", + "massImportGameData": "Nhập hàng loạt dữ liệu trò chơi", + "massImportGameDataDesc": "Nhập hàng loạt gói Dữ liệu trò chơi, cố gắng khớp tên tệp của chúng với UUID trò chơi.", "servicesHeader": "Dịch vụ ngầm", "servicesMissing": "Không có dịch vụ nào.", "running": "Đang chạy", @@ -239,7 +250,7 @@ "dateAdded": "Ngày thêm", "dateModified": "Ngày sửa", "brokenInInfinity": "Hỏng (trong Infinity)", - "extreme": "Extreme", + "extreme": "Phản cảm", "playlistNotes": "Ghi chú của danh sách", "noPlaylistNotes": "Không có", "notes": "Ghi chú", @@ -261,12 +272,12 @@ "uninstallGame": "Gỡ trò chơi", "installed": "Đã cài", "notInstalled": "Chưa cài", - "legacyGame": "Legacy Game", + "legacyGame": "Trò chơi kế thừa", "download": "Tải về", "thumbnail": "Ảnh thu nhỏ", "screenshot": "Ảnh chụp màn hình", "dropImageHere": "Thả ảnh vào đây để thêm", - "noGameSelected": "Chưa chọn trò chơi", + "noGameSelected": "Không có {0} nào được chọn", "clickToSelectGame": "Nhấn một trò chơi để chọn.", "deleteAdditionalApplication": "Xoá phần mềm đi kèm", "deleteGameAndAdditionalApps": "Xoá trò chơi (và phần mềm đi kèm)", @@ -282,13 +293,16 @@ "dropGameOnLeft": "Kéo thả trò chơi vào một danh sách ở {0} để thêm vào danh sách đó.", "leftSidebar": "thanh bên trái", "setFlashpointPathQuestion": "Bạn đã chỉnh đường dẫn sang {0} ở mục {1} chưa?", - "flashpointPath": "đường dẫn Flashpoint", + "flashpointPath": "Đường dẫn Flashpoint", "config": "Cài đặt", "noteSaveAndRestart": "Lưu ý: Bạn phải nhấn {0} để thay đổi có hiệu lực.", "saveAndRestart": "Lưu & Khởi động lại", "thereAreNoGames": "Không có trò chơi nào cả.", "noGameMatchedDesc": "Không tìm thấy trò chơi phù hợp.", - "noGameMatchedSearch": "Hãy tìm từ khoá chung chung hơn." + "noGameMatchedSearch": "Hãy tìm từ khoá chung chung hơn.", + "mountParameters": "Tham Số Khởi Động Trò Chơi", + "noMountParameters": "Không có Tham Số Khởi Động Trò Chơi", + "showExtremeScreenshot": "Hiện ảnh chụp màn hình phản cảm" }, "tags": { "name": "Tên", @@ -321,7 +335,7 @@ "saveImportedCurations": "Lưu bản đóng góp đã nhập", "keepArchiveKey": "Giữ UUID của tập tin nén của bản đóng góp", "symlinkCurationContent": "Bật Symlink cho thư mục chứa nội dung bản đóng góp (sẽ hiện thông báo yêu cầu quyền quản trị trên Windows, phải bật khi dùng MAD4FP)", - "useTagFilters": "Use Tag Filters in Suggestions", + "useTagFilters": "Sử dụng Bộ lọc thẻ trong Đề xuất", "openCurationsFolder": "Thư mục chứa bản đóng góp", "openCurationsFolderDesc": "Mở thư mục chứa bản đóng góp trong Explorer", "openExportsFolder": "Bản đóng góp đã xuất", @@ -336,8 +350,8 @@ "loadArchiveDesc": "Nạp một hay nhiều tập tin nén chứa bản đóng góp", "loadFolder": "Nạp thư mục", "loadFolderDesc": "Nạp một hay nhiều thư mục chứa bản đóng góp", - "scanNewCurationFolders": "Scan For New Curations", - "scanNewCurationFoldersDesc": "Scans for curations that have been added after the initial scan", + "scanNewCurationFolders": "Quét các cuộc tuyển chọn mới", + "scanNewCurationFoldersDesc": "Quét tìm kiếm đã được thêm vào sau lần quét đầu tiên", "noCurations": "Không có bản đóng góp nào", "id": "Thư mục chứa bản đóng góp", "heading": "Tiêu đề", @@ -371,8 +385,8 @@ "nonExistingLibrary": "'Thư viện' không phải là tên của một thư viện hiện có! (Sẽ dùng mặc định)", "nonContentFolders": "Tìm thấy thư mục không chứa nội dung trong thư mục chứa bản đóng góp. Thư mục đó có cần thiết không?", "noTags": "Bản đóng góp này không có nhãn.", - "noSource": "'Source' is a required field.", - "unenteredTag": "The Tags text field hasn't been submitted.", + "noSource": "'Nguồn' là trường bắt buộc.", + "unenteredTag": "Trường văn bản Thẻ chưa được gửi.", "noLogo": "Bản đóng góp này không có biểu trưng.", "noScreenshot": "Bản đóng góp này không có ảnh chụp màn hình.", "ilc_notHttp": "Dùng HTTP.", @@ -400,7 +414,7 @@ "authorPlaceholder": "Tác giả...", "id": "ID", "by": "của", - "extreme": "extreme" + "extreme": "phản cảm" }, "misc": { "noBlankFound": "Không tìm thấy {0}", @@ -413,7 +427,9 @@ "installingFiles": "Đang cài đặt tập tin...", "complete": "Hoàn thành", "exportMetaEditTitle": "Xuất sửa đổi meta", - "exportMetaEditDesc": "Chọn tất cả thuộc tính cần xuất:" + "exportMetaEditDesc": "Chọn tất cả thuộc tính cần xuất:", + "showImage": "Hiện ảnh", + "searching": "Đang tìm kiếm..." }, "menu": { "viewThumbnailInFolder": "Xem ảnh thu nhỏ trong thư mục", @@ -442,13 +458,13 @@ "importPlaylistAs": "Nhập danh sách dưới dạng", "selectFileToExportMeta": "Chọn tệp để lưu meta vào", "selectFolderToExportMetaAndImages": "Chọn thư mục để lưu meta và hình ảnh vào", - "replaceFilesQuestion": "Ghi đè tập tin?", - "exportedAlreadyExistsYesNo": "Một hoặc nhiều tệp được xuất đã tồn tại.\nBạn có muốn thay thế chúng không?", + "replaceFilesQuestion": "Ghi đè tệp?", + "exportedAlreadyExistsYesNo": "Một hoặc nhiều tệp được xuất đã tồn tại.\nBạn có muốn ghi đè chúng không?", "selectFolder": "Chọn thư mục", "selectScreenshot": "Chọn ảnh chụp màn hình", "selectThumbnail": "Chọn ảnh thu nhỏ", "selectCurationFolder": "Chọn (các) thư mục chứa bản đóng góp", - "selectCurationArchive": "Chọn (các) tập tin nén chứa bản đóng góp", + "selectCurationArchive": "Chọn (các) tệp nén chứa bản đóng góp", "selectCurationMeta": "Chọn (các) tệp tin meta của bản đóng góp", "selectPlaylistToImport": "Chọn danh sách muốn nhập", "selectFileToExportPlaylist": "Chọn tập tin để lưu danh sách vào", @@ -459,51 +475,54 @@ "restartNow": "Khởi động lại ngay?", "restartToApplyUpgrade": "Nâng cấp này sẽ không được áp dụng đến khi khởi động lại.\nBạn muốn khởi động lại ngay không?", "areYouSure": "Bạn có chắc?", - "areYouSureDelete": "Are you sure you want to delete this entry?", - "areYouSurePlaylistRemove": "Are you sure you want to remove this entry from the playlist?", + "areYouSureDelete": "Bạn có chắc chắn muốn xóa mục này không?", + "areYouSurePlaylistRemove": "Bạn có chắc chắn muốn xóa mục nhập này khỏi danh sách phát không?", "cancel": "Huỷ", "mergePlaylists": "Nhập danh sách", "newPlaylist": "Giữ riêng", "uploadPrivacyWarning": "Làm thế này sẽ công khai Bản ghi của bạn.\nMột liên kết sẽ được sao chép vào khay nhớ tạm.\n\nBạn có chắc không?", "overwriteFileTitle": "Tập tin đã tồn tại!", - "overwriteFileMessage": "Tồn tại một tệp cùng tên. Bạn có muốn ghi đè?", + "overwriteFileMessage": "Đã có tệp cùng tên. Bạn có muốn ghi đè?", "overwriteFileDetail": "Đường dẫn tập tin:", - "deleteTagFilterGroup": "Delete this Tag Filter Group?", + "deleteTagFilterGroup": "Xóa nhóm bộ lọc nhãn này?", "deleteCuration": "Xoá bản đóng góp này?", "importCuration": "Nhập bản đóng góp này?", - "deleteGameImage": "Delete this Game Image?", - "deletePlaylist": "Delete this Playlist?", + "deleteGameImage": "Xóa hình ảnh này?", + "deletePlaylist": "Xoá danh sách này?", "importAllCurations": "Nhập tất cả bản đóng góp?", "deleteAllCurations": "Xoá tất cả bản đóng góp?", - "removePlaylistGame": "Remove this Game from the Playlist?", + "removePlaylistGame": "Xoá trò chơi này khỏi danh sách?", "deleteGame": "Xoá trò chơi này? Không hoàn tác được đâu nhé.", "deleteGameData": "Xoá dữ liệu của trò chơi này? Không hoàn tác được đâu nhé.", "deleteAddApp": "Xoá ứng dụng đi kèm này?", - "deleteTagCategory": "Delete this Tag Category?", + "deleteTagCategory": "Xoá danh mục nhãn này?", "deleteTag": "Xoá nhãn này?", "deleteTagAlias": "Xoá tên khác của nhãn?", "deleteSource": "Xoá nguồn này?", "uninstallGame": "Gỡ trò chơi?", - "unableToUninstallGameData": "Unable to delete data pack. Is it in use? Try restarting the launcher.", + "unableToUninstallGameData": "Không thể xóa gói dữ liệu. Nó có được sử dụng không? Thử khởi động lại trình khởi chạy.", "unableToDeleteGame": "Không thể xoá trò chơi. Bạn có đang mở trò này không? Hãy thử mở lại trình khởi chạy.", "downloadingGame": "Đang tải trò chơi...", - "verifyingGame": "Verifying Game...", + "verifyingGame": "Đang xác nhận trò chơi...", "aFewMinutes": "Có thể mất vài phút." }, "libraries": { "arcade": "Trò chơi", + "arcadeSingular": "Trò chơi", "arcadePlural": "Tất cả trò chơi", "theatre": "Hoạt hình", - "theatrePlural": "Tất cả hoạt hình", + "theatreSingular": "Hoạt hình", + "theatrePlural": "Tất cả Hoạt hình", "auditorium": "Khán phòng NG", - "auditoriumPlural": "Tất cả hoạt hình NG" + "auditoriumSingular": "Hoạt hình", + "auditoriumPlural": "Tất cả Hoạt hình Newgrounds" }, "upgrades": { "infinity": "Flashpoint Infinity", - "infinityDesc": "Các tệp bắt buộc. Thêm hỗ trợ cho các trò chơi Flash.", + "infinityDesc": "Các tệp cần thiết. Hỗ trợ thêm cho các trò chơi Flash.", "tech": "Công nghệ khác", "techDesc": "Hỗ trợ các công nghệ khác - Shockwave, Unity, Java, HTML5, v.v.", "screenshots": "Biểu trưng & Ảnh chụp màn hình", "screenshotsDesc": "Thêm biểu trưng ở chế độ xem Lưới và ảnh chụp màn hình cho tất cả trò chơi." } -} \ No newline at end of file +} diff --git a/lang/zh-CN.json b/lang/zh-CN.json index cd726dbc9..3a2f9a3a8 100644 --- a/lang/zh-CN.json +++ b/lang/zh-CN.json @@ -4,8 +4,16 @@ "configHeader": "设置", "configDesc": "(您必须点击 '保存并重启' 才能使部分设置生效)", "preferencesHeader": "首选项", - "extremeGames": "成人内容", - "extremeGamesDesc": "允许显示包含于 'Extreme' 标签筛选器组内的标签和游戏。", + "extremeGames": "显示成人过滤器", + "extremeGamesDesc": "允许切换、创建和修改成人标签过滤器,以控制不适合未成年人的内容。", + "hideExtremeScreenshots": "隐藏成人截图", + "hideExtremeScreenshotsDesc": "隐藏成人内容的截图,点击图像框即可取消隐藏。", + "fancyAnimations": "动画效果", + "fancyAnimationsDesc": "在启动器中启用动画效果。", + "searchLimit": "搜索限制", + "searchLimitDesc": "限制搜索返回的结果数量", + "searchLimitUnlimited": "无限制", + "searchLimitValue": "{0} 个结果", "enableEditing": "启用编辑", "enableEditingDesc": "启用对游戏与附加应用的编辑。也显示与编辑有关的标签页。", "onDemandImages": "按需显示图像", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "若首选语言不完整,则以第二语言显示。", "auto": "自动 ({0})", "none": "无", + "contentFiltersHeader": "内容过滤器", "flashpointHeader": "Flashpoint", "flashpointPath": "Flashpoint 路径", "flashpointPathDesc": "Flashpoint 文件夹路径(可以是相对路径)", @@ -79,6 +88,7 @@ "helpInfo": "需要帮助?请{0}。", "help": "阅读手册", "upgradesHeader": "升级", + "updateFeedHeader": "新闻流", "installComplete": "安装完成", "alreadyInstalled": "已安装", "download": "下载", @@ -103,7 +113,8 @@ "clearLog": "清除日志", "copy404Urls": "复制 404 URL", "uploadLog": "上传日志", - "copiedToClipboard": "复制到剪贴板" + "copiedToClipboard": "复制到剪贴板", + "openLogsWindow": "打开日志窗口" }, "app": { "home": "主页", @@ -177,7 +188,7 @@ "importTags": "导入标签", "importTagsDesc": "从“导出标签”创建的文件中导入标签", "migrateExtremeGames": "迁移成人游戏", - "migrateExtremeGamesDesc": "删除所有游戏的“成人”标记,并为它们打上“LEGACY-Extreme”标签", + "migrateExtremeGamesDesc": "删除所有游戏的“Extreme”(成人)标记,并为它们打上“LEGACY-Extreme”标签", "massImportGameData": "批量导入游戏数据", "massImportGameDataDesc": "批量导入游戏数据包,尝试将其文件名匹配到游戏 UUID。", "servicesHeader": "后台服务", @@ -266,7 +277,7 @@ "thumbnail": "缩略图", "screenshot": "屏幕截图", "dropImageHere": "拖动图片到此处以添加", - "noGameSelected": "未选择游戏", + "noGameSelected": "未选择{0}", "clickToSelectGame": "点击游戏以选择", "deleteAdditionalApplication": "删除附加应用程序", "deleteGameAndAdditionalApps": "删除游戏(及附加应用程序)", @@ -288,7 +299,10 @@ "saveAndRestart": "保存并重启", "thereAreNoGames": "无游戏。", "noGameMatchedDesc": "未找到匹配您搜索词的游戏名。", - "noGameMatchedSearch": "请尝试用更宽泛的关键词搜索。" + "noGameMatchedSearch": "请尝试用更宽泛的关键词搜索。", + "mountParameters": "挂载参数", + "noMountParameters": "无挂载参数", + "showExtremeScreenshot": "显示成人截图" }, "tags": { "name": "名称", @@ -413,7 +427,9 @@ "installingFiles": "正在安装文件...", "complete": "已完成", "exportMetaEditTitle": "导出元数据变更", - "exportMetaEditDesc": "选择所有要导出的属性:" + "exportMetaEditDesc": "选择所有要导出的属性:", + "showImage": "显示图像", + "searching": "正在搜索…" }, "menu": { "viewThumbnailInFolder": "在文件夹中查看缩略图", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "游戏", + "arcadeSingular": "游戏", "arcadePlural": "所有游戏", "theatre": "动画", + "theatreSingular": "动画", "theatrePlural": "所有动画", "auditorium": "NG Auditorium", + "auditoriumSingular": "动画", "auditoriumPlural": "所有 NG 动画" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "图标与屏幕截图", "screenshotsDesc": "为所有游戏添加网格视图图标与屏幕截图。" } -} \ No newline at end of file +} diff --git a/lang/zh-TW.json b/lang/zh-TW.json index de14b8c7f..b83f6e708 100644 --- a/lang/zh-TW.json +++ b/lang/zh-TW.json @@ -4,8 +4,16 @@ "configHeader": "設定", "configDesc": "(必須點擊最下方的「儲存並重新啟動」始得生效)", "preferencesHeader": "偏好設定", - "extremeGames": "成人内容", - "extremeGamesDesc": "允許顯示包含於 'Extreme' 標籤篩選器組內的標籤和遊戲。", + "extremeGames": "顯示成人篩選器", + "extremeGamesDesc": "允許切換、創建和修改成人標籤過濾器,以控制不適合未成年人的內容。", + "hideExtremeScreenshots": "隱藏成人截圖", + "hideExtremeScreenshotsDesc": "隱藏成人內容的截圖,點擊圖像框即可取消隱藏。", + "fancyAnimations": "動畫效果", + "fancyAnimationsDesc": "在啟動器中啟用動畫效果。", + "searchLimit": "搜尋限制", + "searchLimitDesc": "限制搜索返回的結果數量", + "searchLimitUnlimited": "無限制", + "searchLimitValue": "{0} 個結果", "enableEditing": "允許修改", "enableEditingDesc": "開啟修改遊戲與額外應用程式的功能,同時顯示其他修改相關之頁籤", "onDemandImages": "依需求下載圖片", @@ -16,6 +24,7 @@ "fallbackLanguageDesc": "若主要語言內容有缺失時所使用之語言", "auto": "自動 ({0})", "none": "無", + "contentFiltersHeader": "內容篩選器", "flashpointHeader": "Flashpoint", "flashpointPath": "Flashpoint 路徑", "flashpointPathDesc": "指向含有Flashpoint程式之路徑(可為相對路徑)", @@ -79,6 +88,7 @@ "helpInfo": "需要協助嗎? 請{0}。", "help": "查閱說明手冊", "upgradesHeader": "升級項目", + "updateFeedHeader": "新聞流", "installComplete": "安裝完成", "alreadyInstalled": "已安裝", "download": "下載", @@ -103,7 +113,8 @@ "clearLog": "清除紀錄", "copy404Urls": "複製 404 路徑", "uploadLog": "上傳紀錄", - "copiedToClipboard": "已複製到剪貼簿" + "copiedToClipboard": "已複製到剪貼簿", + "openLogsWindow": "Open Logs Window" }, "app": { "home": "首頁", @@ -266,7 +277,7 @@ "thumbnail": "縮圖", "screenshot": "截圖", "dropImageHere": "將圖片拖曳至此處", - "noGameSelected": "未選取任何遊戲", + "noGameSelected": "No {0} Selected", "clickToSelectGame": "點擊以選取遊戲", "deleteAdditionalApplication": "刪除額外應用程式", "deleteGameAndAdditionalApps": "刪除遊戲(與額外應用程式)", @@ -288,7 +299,10 @@ "saveAndRestart": "儲存並重新啟動", "thereAreNoGames": "查無結果", "noGameMatchedDesc": "無符合搜尋條件之遊戲", - "noGameMatchedSearch": "嘗試以較為寬鬆的關鍵字進行搜尋" + "noGameMatchedSearch": "嘗試以較為寬鬆的關鍵字進行搜尋", + "mountParameters": "掛載參數", + "noMountParameters": "無掛載參數", + "showExtremeScreenshot": "顯示成人截圖" }, "tags": { "name": "名稱", @@ -413,7 +427,9 @@ "installingFiles": "正在安裝檔案...", "complete": "已完成", "exportMetaEditTitle": "匯出元資料檔修改", - "exportMetaEditDesc": "選擇所有要匯出的屬性:" + "exportMetaEditDesc": "選擇所有要匯出的屬性:", + "showImage": "顯示圖片", + "searching": "Searching..." }, "menu": { "viewThumbnailInFolder": "於資料夾中查看縮圖", @@ -492,10 +508,13 @@ }, "libraries": { "arcade": "遊戲", + "arcadeSingular": "Game", "arcadePlural": "所有遊戲", "theatre": "動畫", + "theatreSingular": "Animation", "theatrePlural": "所有動畫", "auditorium": "Newgrounds 劇院", + "auditoriumSingular": "Animation", "auditoriumPlural": "所有Newgrounds 動畫" }, "upgrades": { @@ -506,4 +525,4 @@ "screenshots": "縮圖與截圖", "screenshotsDesc": "新增格狀顯示的縮圖與遊戲截圖" } -} \ No newline at end of file +} From 4b956510b3bac5933286e94d57cd5ce354825047 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sat, 23 Jul 2022 06:52:01 +0100 Subject: [PATCH 58/62] feat: Show Busy play button when launching --- lang/en.json | 3 +- src/back/GameLauncher.ts | 39 +++---- src/back/responses.ts | 4 + src/renderer/app.tsx | 13 ++- .../components/RightBrowseSidebar.tsx | 110 ++++++++++-------- src/renderer/index.tsx | 1 + src/renderer/store/main/enums.ts | 6 +- src/renderer/store/main/reducer.ts | 26 ++++- src/renderer/store/main/types.ts | 8 ++ src/shared/lang.ts | 1 + static/window/styles/core.css | 1 + static/window/styles/fancy.css | 4 + 12 files changed, 140 insertions(+), 76 deletions(-) diff --git a/lang/en.json b/lang/en.json index 8a7091e14..a51b08139 100644 --- a/lang/en.json +++ b/lang/en.json @@ -303,7 +303,8 @@ "noGameMatchedSearch": "Try searching for something less restrictive.", "mountParameters": "Mount Parameters", "noMountParameters": "No Mount Parameters", - "showExtremeScreenshot": "Show Extreme Screenshot" + "showExtremeScreenshot": "Show Extreme Screenshot", + "busy": "Busy" }, "tags": { "name": "Name", diff --git a/src/back/GameLauncher.ts b/src/back/GameLauncher.ts index bb4be5158..46f0f3a01 100644 --- a/src/back/GameLauncher.ts +++ b/src/back/GameLauncher.ts @@ -146,7 +146,21 @@ export namespace GameLauncher { } } } - log.debug('TEST', 'Run required add apps'); + // Launch game callback + const launchCb = async (launchInfo: GameLaunchInfo): Promise => { + await onWillEvent.fire(launchInfo) + .then(() => { + const command: string = createCommand(launchInfo.launchInfo); + const managedProc = opts.runGame(launchInfo); + log.info(logSource,`Launch Game "${opts.game.title}" (PID: ${managedProc.getPid()}) [\n`+ + ` applicationPath: "${opts.game.applicationPath}",\n`+ + ` launchCommand: "${opts.game.launchCommand}",\n`+ + ` command: "${command}" ]`); + }) + .catch((error) => { + log.info('Game Launcher', `Game Launch Aborted: ${error}`); + }); + }; // Launch game let appPath: string = getApplicationPath(opts.game.applicationPath, opts.execMappings, opts.native); let appArgs: string[] = []; @@ -193,7 +207,7 @@ export namespace GameLauncher { noshell: true } }; - onWillEvent.fire(gameLaunchInfo) + await onWillEvent.fire(gameLaunchInfo) .then(() => { const managedProc = opts.runGame(gameLaunchInfo); log.info(logSource, `Launch Game "${opts.game.title}" (PID: ${managedProc.getPid()}) [\n`+ @@ -214,21 +228,17 @@ export namespace GameLauncher { log.error('Launcher', `Error running provider for game.\n${error}`); } } - log.debug('TEST', 'Trying launch'); // Continue with launching normally const gamePath: string = path.isAbsolute(appPath) ? fixSlashes(appPath) : fixSlashes(path.join(opts.fpPath, appPath)); const gameArgs: string[] = [...appArgs, opts.game.launchCommand]; - log.debug('TEST', 'Gotten game path and args'); const useWine: boolean = process.platform != 'win32' && gamePath.endsWith('.exe'); const env = getEnvironment(opts.fpPath, opts.proxy); - log.debug('TEST', 'Gotten environment'); try { await GameManager.findGame(opts.game.id); } catch (err: any) { - log.error('TEST', 'Error Game - ' + err.toString()); + log.error('Launcher', 'Error Finding Game - ' + err.toString()); } const gameData = opts.game.activeDataId ? await GameDataManager.findOne(opts.game.activeDataId) : null; - log.debug('TEST', 'Found game data'); const gameLaunchInfo: GameLaunchInfo = { game: opts.game, activeData: gameData, @@ -239,20 +249,7 @@ export namespace GameLauncher { env, } }; - log.debug('TEST', 'Gathered info'); - onWillEvent.fire(gameLaunchInfo) - .then(() => { - const command: string = createCommand(gameLaunchInfo.launchInfo); - log.debug('TEST', 'All info gathered, running'); - const managedProc = opts.runGame(gameLaunchInfo); - log.info(logSource,`Launch Game "${opts.game.title}" (PID: ${managedProc.getPid()}) [\n`+ - ` applicationPath: "${opts.game.applicationPath}",\n`+ - ` launchCommand: "${opts.game.launchCommand}",\n`+ - ` command: "${command}" ]`); - }) - .catch((error) => { - log.info('Game Launcher', `Game Launch Aborted: ${error}`); - }); + await launchCb(gameLaunchInfo); } /** diff --git a/src/back/responses.ts b/src/back/responses.ts index d72ee9f60..495b957fe 100644 --- a/src/back/responses.ts +++ b/src/back/responses.ts @@ -266,6 +266,10 @@ export function registerRequestCallbacks(state: BackState): void { state.socketServer.register(BackIn.LAUNCH_GAME, async (event, id) => { const game = await GameManager.findGame(id); + await new Promise((resolve) => { + setTimeout(resolve, 5000); + }); + if (game) { // Make sure Server is set to configured server - Curations may have changed it const configServer = state.serviceInfo ? state.serviceInfo.server.find(s => s.name === state.config.server) : undefined; diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index ab1a1d812..0ebcd7df2 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -729,7 +729,17 @@ export class App extends React.Component { onGameLaunch = async (gameId: string): Promise => { log.debug('Launcher', 'Launching Game - ' + gameId); - await window.Shared.back.request(BackIn.LAUNCH_GAME, gameId); + this.props.dispatchMain({ + type: MainActionType.BUSY_GAME, + gameId + }); + await window.Shared.back.request(BackIn.LAUNCH_GAME, gameId) + .finally(() => { + this.props.dispatchMain({ + type: MainActionType.UNBUSY_GAME, + gameId + }); + }); } onDeleteSelectedGame = async (): Promise => { @@ -1137,6 +1147,7 @@ export class App extends React.Component { onSaveGame={this.onSaveEditClick} tagCategories={this.props.tagCategories} suggestions={this.props.main.suggestions} + busyGames={this.props.main.busyGames} onOpenExportMetaEdit={this.onOpenExportMetaEdit} /> )} diff --git a/src/renderer/components/RightBrowseSidebar.tsx b/src/renderer/components/RightBrowseSidebar.tsx index 2a0f87158..5404d3bc3 100644 --- a/src/renderer/components/RightBrowseSidebar.tsx +++ b/src/renderer/components/RightBrowseSidebar.tsx @@ -12,6 +12,7 @@ import { GamePropSuggestions, PickType, ProcessAction } from '@shared/interfaces import { LangContainer } from '@shared/lang'; import { deepCopy, generateTagFilterGroup, sizeToString } from '@shared/Util'; import { formatString } from '@shared/utils/StringFormatter'; +import { uuid } from '@shared/utils/uuid'; import { clipboard, Menu, MenuItemConstructorOptions } from 'electron'; import { GameData } from 'flashpoint-launcher'; import * as fs from 'fs'; @@ -20,7 +21,6 @@ import { WithPreferencesProps } from '../containers/withPreferences'; import { WithSearchProps } from '../containers/withSearch'; import { getGameImagePath, getGameImageURL } from '../Util'; import { LangContext } from '../util/lang'; -import { uuid } from '@shared/utils/uuid'; import { CheckBox } from './CheckBox'; import { ConfirmElement, ConfirmElementArgs } from './ConfirmElement'; import { DropdownInputField } from './DropdownInputField'; @@ -62,6 +62,8 @@ type OwnProps = { suggestions: Partial; /** Tag Categories info */ tagCategories: TagCategory[]; + /** List of busy games */ + busyGames: string[]; onEditClick: () => void; onDiscardClick: () => void; @@ -82,7 +84,7 @@ type RightBrowseSidebarState = { currentTagInput: string; tagSuggestions: TagSuggestion[]; gameDataBrowserOpen: boolean; - activeData?: GameData; + activeData: GameData | null; showExtremeScreenshots: boolean; middleScrollRef: React.RefObject; }; @@ -143,6 +145,7 @@ export class RightBrowseSidebar extends React.Component {/* -- Play Button -- */} { isPlaceholder ? undefined : - this.props.gameRunning ? ( -
    { - if (this.props.currentGame) { - window.Shared.back.send(BackIn.SERVICE_ACTION, ProcessAction.STOP, `game.${this.props.currentGame.id}`); - } - }}> - {strings.stop} -
    - ) : (this.state.activeData && !this.state.activeData.presentOnDisk) ? ( -
    { - this.props.currentGame && this.props.onGameLaunch(this.props.currentGame.id) - .then(this.onForceUpdateGameData); - }}> - {strings.download} + (this.props.currentGame && this.props.busyGames.includes(this.props.currentGame.id)) ? ( +
    + {strings.busy}
    ) - : ( -
    -
    this.props.currentGame && this.props.onGameLaunch(this.props.currentGame.id)} > - {strings.play} -
    - { this.state.activeData ? ( -
    - openContextMenu([{ - label: strings.uninstallGame, - click: async () => { - if (this.state.activeData) { - const res = await this.props.openConfirmDialog(allStrings.dialog.uninstallGame, [allStrings.misc.yes, allStrings.misc.no], 1); - if (res === 0) { - window.Shared.back.request(BackIn.UNINSTALL_GAME_DATA, this.state.activeData.id) - .then((game) => { - this.onForceUpdateGameData(); - }) - .catch((error) => { - alert(allStrings.dialog.unableToUninstallGameData); - }); + : this.props.gameRunning ? ( +
    { + if (this.props.currentGame) { + window.Shared.back.send(BackIn.SERVICE_ACTION, ProcessAction.STOP, `game.${this.props.currentGame.id}`); + } + }}> + {strings.stop} +
    + ) : (this.state.activeData && !this.state.activeData.presentOnDisk) ? ( +
    { + this.props.currentGame && this.props.onGameLaunch(this.props.currentGame.id) + .then(this.onForceUpdateGameData); + }}> + {strings.download} +
    + ) + : ( +
    +
    this.props.currentGame && this.props.onGameLaunch(this.props.currentGame.id)} > + {strings.play} +
    + { this.state.activeData ? ( +
    + openContextMenu([{ + label: strings.uninstallGame, + click: async () => { + if (this.state.activeData) { + const res = await this.props.openConfirmDialog(allStrings.dialog.uninstallGame, [allStrings.misc.yes, allStrings.misc.no], 1); + if (res === 0) { + window.Shared.back.request(BackIn.UNINSTALL_GAME_DATA, this.state.activeData.id) + .then((game) => { + this.onForceUpdateGameData(); + }) + .catch((error) => { + alert(allStrings.dialog.unableToUninstallGameData); + }); + } } } - } - }]) - }> - -
    - ) : undefined } + }]) + }> + +
    + ) : undefined } -
    - ) +
    + ) }
    i === action.gameId); + if (idx > -1) { + nextBusy.splice(idx, 1); + } + return { + ...state, + busyGames: nextBusy + }; + } + case MainActionType.FORCE_UPDATE_GAME_DATA: { const { gameData } = action; if (state.currentGame) { @@ -449,6 +472,7 @@ function createInitialState(): MainState { downloadPercent: 0, downloadSize: 0, downloadVerifying: false, - isEditingGame: false + isEditingGame: false, + busyGames: [], }; } diff --git a/src/renderer/store/main/types.ts b/src/renderer/store/main/types.ts index 7d9ad1cf5..ba6378e07 100644 --- a/src/renderer/store/main/types.ts +++ b/src/renderer/store/main/types.ts @@ -131,6 +131,8 @@ export type MainState = { currentPlaylist?: Playlist; currentPlaylistEntry?: PlaylistGame; isEditingGame: boolean; + /** Games which are in the middle of a busy operation */ + busyGames: string[]; } export type MainAction = { @@ -234,4 +236,10 @@ export type MainAction = { } | { type: MainActionType.FORCE_UPDATE_GAME_DATA; gameData: GameData; +} | { + type: MainActionType.BUSY_GAME; + gameId: string; +} | { + type: MainActionType.UNBUSY_GAME; + gameId: string; } diff --git a/src/shared/lang.ts b/src/shared/lang.ts index 9b43f9036..dedd2cb14 100644 --- a/src/shared/lang.ts +++ b/src/shared/lang.ts @@ -316,6 +316,7 @@ const langTemplate = { 'mountParameters', 'noMountParameters', 'showExtremeScreenshot', + 'busy', ] as const, tags: [ 'name', diff --git a/static/window/styles/core.css b/static/window/styles/core.css index c78fe9a24..9e5a7c9e1 100644 --- a/static/window/styles/core.css +++ b/static/window/styles/core.css @@ -1327,6 +1327,7 @@ body { /* Browse-Right-Sidebar Play Button */ .browse-right-sidebar__play-button, +.browse-right-sidebar__play-button--busy, .browse-right-sidebar__play-button--running, .browse-right-sidebar__play-button--download { width: 100%; diff --git a/static/window/styles/fancy.css b/static/window/styles/fancy.css index 547a5267c..8b2b020e3 100644 --- a/static/window/styles/fancy.css +++ b/static/window/styles/fancy.css @@ -602,6 +602,7 @@ body { } /* Browse-Right-Sidebar Play Button */ .browse-right-sidebar__play-button, +.browse-right-sidebar__play-button--busy, .browse-right-sidebar__play-button--running, .browse-right-sidebar__play-button--download { border-width: 2px; @@ -644,6 +645,9 @@ body { .browse-right-sidebar__play-button--download:active { background-color: var(--layout__play-button--download--active); } +.browse-right-sidebar__play-button--busy { + background-color: var(--layout__secondary-background); +} .browse /* BrowseSidebar Title-Row Buttons */ .browse-right-sidebar__title-row__buttons .icon__use { From ca24c05fa26486f2c19755de94e4dadb4409b917 Mon Sep 17 00:00:00 2001 From: LindirQuenya <53021080+LindirQuenya@users.noreply.github.com> Date: Sat, 23 Jul 2022 22:19:52 -0400 Subject: [PATCH 59/62] Don't make fp path absolute, just chdir. --- src/back/index.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/back/index.ts b/src/back/index.ts index 6ec5683b4..bd42f6fea 100644 --- a/src/back/index.ts +++ b/src/back/index.ts @@ -283,10 +283,9 @@ async function onProcessMessage(message: any, sendHandle: any): Promise { console.log('Back - Loaded Config'); - // If we're on mac and the flashpoint path is relative, resolve it relative to the configFolder path. - state.config.flashpointPath = process.platform == 'darwin' && state.config.flashpointPath[0] != '/' - ? path.resolve(state.configFolder, state.config.flashpointPath) - : state.config.flashpointPath; + if (process.platform === 'darwin') { + process.chdir(state.configFolder); + } const loadPrefs = async (): Promise => { // @TODO Figure out why async loading isn't always working? From 48382ec2ad32b7fe2dc0be9b925a19fdd595178b Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sun, 24 Jul 2022 06:57:07 +0100 Subject: [PATCH 60/62] fix: Catch prefs rename and change to copy and unlink if needed refactor: Rename 'busy' string fix: Right sidebar loads active data on mount --- lang/en.json | 2 +- src/renderer/components/RightBrowseSidebar.tsx | 11 +++++++++++ src/shared/preferences/PreferencesFile.ts | 7 ++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lang/en.json b/lang/en.json index a51b08139..95d2b1a7d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -304,7 +304,7 @@ "mountParameters": "Mount Parameters", "noMountParameters": "No Mount Parameters", "showExtremeScreenshot": "Show Extreme Screenshot", - "busy": "Busy" + "busy": "Please Wait..." }, "tags": { "name": "Name", diff --git a/src/renderer/components/RightBrowseSidebar.tsx b/src/renderer/components/RightBrowseSidebar.tsx index 5404d3bc3..ecaa07f22 100644 --- a/src/renderer/components/RightBrowseSidebar.tsx +++ b/src/renderer/components/RightBrowseSidebar.tsx @@ -152,6 +152,17 @@ export class RightBrowseSidebar extends React.Component { + if (data) { + this.setState({ + activeData: data + }); + } + }); + } } componentWillUnmount() { diff --git a/src/shared/preferences/PreferencesFile.ts b/src/shared/preferences/PreferencesFile.ts index ab9968980..588d7f4e5 100644 --- a/src/shared/preferences/PreferencesFile.ts +++ b/src/shared/preferences/PreferencesFile.ts @@ -87,6 +87,11 @@ export namespace PreferencesFile { stat = await fs.promises.stat(temp); count++; } - await fs.promises.rename(temp, filePath); + try { + await fs.promises.rename(temp, filePath); + } catch { + await fs.promises.copyFile(temp, filePath); + await fs.promises.unlink(temp); + } } } From 427413e46b959fe2d05c53247040a6baacbca10c Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sun, 24 Jul 2022 07:11:10 +0100 Subject: [PATCH 61/62] github: Add dmg-license to mac workflow --- .github/workflows/latest.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml index 74d053efb..2a385cf8f 100644 --- a/.github/workflows/latest.yml +++ b/.github/workflows/latest.yml @@ -39,6 +39,9 @@ jobs: if: steps.cache.outputs.cache-hit != 'true' env: CI: true + - name: Install dmg-license + if: matrix.os == 'macOS-latest' + run: npm i dmg-license - name: Build and Release run: npm run release env: From 6d8c7f3e66a2b05d8d9b0218b60650e425585848 Mon Sep 17 00:00:00 2001 From: Colin Berry Date: Sun, 24 Jul 2022 07:12:46 +0100 Subject: [PATCH 62/62] github: Force install dmg-license on Mac --- .github/workflows/latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml index 2a385cf8f..e94a84e5c 100644 --- a/.github/workflows/latest.yml +++ b/.github/workflows/latest.yml @@ -41,7 +41,7 @@ jobs: CI: true - name: Install dmg-license if: matrix.os == 'macOS-latest' - run: npm i dmg-license + run: npm install dmg-license --force - name: Build and Release run: npm run release env: