Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fix: broken new installs #389

Merged
merged 1 commit into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"cmd-shim": "^6.0.3",
"decentraland-connect": "^7.1.0",
"decentraland-crypto-fetch": "^2.0.1",
"deepmerge": "^4.3.1",
"electron-log": "^5.1.6",
"electron-updater": "6.2.1",
"http-server": "^14.1.1",
Expand Down
29 changes: 26 additions & 3 deletions packages/main/src/modules/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
import path from 'node:path';
import { FileSystemStorage } from '/shared/types/storage';
import { SETTINGS_DIRECTORY } from '/shared/paths';
import { SETTINGS_DIRECTORY, CONFIG_FILE_NAME, getFullScenesPath } from '/shared/paths';
import { DEFAULT_CONFIG, mergeConfig } from '/shared/types/config';
import { getUserDataPath } from './electron';

export const CONFIG_PATH = path.join(getUserDataPath(), SETTINGS_DIRECTORY, 'config.json');
export const config = await FileSystemStorage.getOrCreate(CONFIG_PATH);
export const CONFIG_PATH = path.join(getUserDataPath(), SETTINGS_DIRECTORY, CONFIG_FILE_NAME);
const storage = await FileSystemStorage.getOrCreate(CONFIG_PATH);

// Initialize with default values if empty
const existingConfig = await storage.get<Record<string, any>>('');
const defaultConfig = { ...DEFAULT_CONFIG };
defaultConfig.settings.scenesPath = getFullScenesPath(getUserDataPath());

if (!existingConfig || Object.keys(existingConfig).length === 0) {
// Write the default config
for (const [key, value] of Object.entries(defaultConfig)) {
await storage.set(key, value);
}
} else {
// Deep merge with defaults if config exists but might be missing properties
const mergedConfig = mergeConfig(existingConfig, defaultConfig);
if (JSON.stringify(existingConfig) !== JSON.stringify(mergedConfig)) {
for (const [key, value] of Object.entries(mergedConfig)) {
await storage.set(key, value);
}
}
}

export const config = storage;
44 changes: 26 additions & 18 deletions packages/preload/src/modules/config.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
import fs from 'node:fs/promises';
import path from 'path';
import { produce, type WritableDraft } from 'immer';
import deepmerge from 'deepmerge';

import type { Config } from '/shared/types/config';
import { DEFAULT_DEPENDENCY_UPDATE_STRATEGY } from '/shared/types/settings';
import { DEFAULT_CONFIG, mergeConfig } from '/shared/types/config';

import { invoke } from './invoke';
import { getDefaultScenesPath } from './settings';
import { SETTINGS_DIRECTORY } from '/shared/paths';

const CONFIG_FILE_NAME = 'config.json';
import { SETTINGS_DIRECTORY, CONFIG_FILE_NAME, getFullScenesPath } from '/shared/paths';

let config: Config | undefined;

async function getDefaultConfig(): Promise<Config> {
return {
version: 1,
workspace: {
paths: [],
},
settings: {
scenesPath: await getDefaultScenesPath(),
dependencyUpdateStrategy: DEFAULT_DEPENDENCY_UPDATE_STRATEGY,
},
};
// Clone the default config to avoid modifying the shared constant
const defaultConfig = deepmerge({}, DEFAULT_CONFIG);
// Update the scenesPath to include the userDataPath
const userDataPath = await invoke('electron.getUserDataPath');
defaultConfig.settings.scenesPath = getFullScenesPath(userDataPath);
return defaultConfig;
}

/**
Expand All @@ -38,7 +32,13 @@ export async function getConfigPath(): Promise<string> {
} catch (_) {
await fs.mkdir(userDataPath);
}
return path.join(userDataPath, SETTINGS_DIRECTORY, CONFIG_FILE_NAME);
const settingsDir = path.join(userDataPath, SETTINGS_DIRECTORY);
try {
await fs.stat(settingsDir);
} catch (_) {
await fs.mkdir(settingsDir);
}
return path.join(settingsDir, CONFIG_FILE_NAME);
}

/**
Expand All @@ -50,12 +50,20 @@ export async function getConfigPath(): Promise<string> {
*/
export async function getConfig(): Promise<Readonly<Config>> {
if (!config) {
const defaultConfig = await getDefaultConfig();
try {
const configPath = await getConfigPath();
config = JSON.parse(await fs.readFile(configPath, 'utf-8')) as Config;
const existingConfig = JSON.parse(await fs.readFile(configPath, 'utf-8')) as Partial<Config>;
// Deep merge existing config with default config, preserving existing values
config = mergeConfig(existingConfig, defaultConfig);
// If the merged config is different from what's on disk, write it back
if (JSON.stringify(existingConfig) !== JSON.stringify(config)) {
await writeConfig(config);
}
} catch (_) {
try {
await writeConfig(await getDefaultConfig());
config = defaultConfig;
await writeConfig(config);
} catch (e) {
console.error('[Preload] Failed initializing config file', e);
}
Expand Down
7 changes: 7 additions & 0 deletions packages/shared/paths.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import path from 'node:path';

export const SCENES_DIRECTORY = 'Scenes';
export const SETTINGS_DIRECTORY = 'Settings';
export const CUSTOM_ASSETS_DIRECTORY = 'Custom Items';
export const CONFIG_FILE_NAME = 'config.json';

export function getFullScenesPath(userDataPath: string): string {
return path.join(userDataPath, SCENES_DIRECTORY);
}
22 changes: 22 additions & 0 deletions packages/shared/types/config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
import deepmerge from 'deepmerge';
import { type AppSettings } from './settings';
import { DEFAULT_DEPENDENCY_UPDATE_STRATEGY } from './settings';
import { SCENES_DIRECTORY } from '/shared/paths';

export type Config = {
version: number;
workspace: {
paths: string[];
};
settings: AppSettings;
userId?: string;
};

export const DEFAULT_CONFIG: Config = {
version: 1,
workspace: {
paths: [],
},
settings: {
scenesPath: SCENES_DIRECTORY, // Base directory name, will be joined with userDataPath by main/preload
dependencyUpdateStrategy: DEFAULT_DEPENDENCY_UPDATE_STRATEGY,
},
};

export function mergeConfig(target: Partial<Config>, source: Config): Config {
return deepmerge(source, target, {
// Clone arrays instead of merging them
arrayMerge: (_, sourceArray) => sourceArray,
});
}
Loading