Skip to content

Commit

Permalink
Merge pull request #5 from jdeniau/theme-in-config
Browse files Browse the repository at this point in the history
start registreting theme in config
  • Loading branch information
jdeniau authored Feb 20, 2024
2 parents 2403416 + 8022721 commit 2889c9f
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 136 deletions.
50 changes: 48 additions & 2 deletions src/Contexts.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createContext, useContext, useState } from 'react';
import { createContext, useContext, useEffect, useState } from 'react';
import { ThemeProvider } from 'styled-components';
import { Configuration } from './configuration/type';
import { DEFAULT_THEME, THEME_LIST } from './theme';

export interface ConnectToFunc {
Expand Down Expand Up @@ -56,9 +57,11 @@ export function ThemeContextProvider({
}: {
children: React.ReactNode;
}): React.ReactElement {
const [themeName, setThemeName] = useState(DEFAULT_THEME.name);
const config = useConfiguration();
const [themeName, setThemeName] = useState(config.theme);

const changeTheme = (newTheme: string) => {
window.config.changeTheme(newTheme);
setThemeName(newTheme);
};

Expand All @@ -70,3 +73,46 @@ export function ThemeContextProvider({
</ThemeProvider>
);
}

const ConfigurationContext = createContext<null | Configuration>(null);

export function ConfigurationContextProvider({
children,
}: {
children: React.ReactNode;
}) {
const [configuration, setConfiguration] = useState<null | Configuration>(
null
);

useEffect(() => {
window.config.getConfiguration().then((c) => {
console.log(c);
setConfiguration(c);
});
}, []);

console.log(configuration);

if (!configuration) {
return null;
}

return (
<ConfigurationContext.Provider value={configuration}>
{children}
</ConfigurationContext.Provider>
);
}

export function useConfiguration(): Configuration {
const value = useContext(ConfigurationContext);

if (!value) {
throw new Error(
'useConfiguration must be used within a ConfigurationContextProvider'
);
}

return value;
}
3 changes: 1 addition & 2 deletions src/component/Connection/ConnectionForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ConnectionContext, ConnectToFunc } from '../../Contexts';
import connections from './SavedConnections';
import { ConnectionObject } from './types';
import { ChangeEvent, FormEvent, PureComponent, useContext } from 'react';

Expand Down Expand Up @@ -53,7 +52,7 @@ class ConnectionForm extends PureComponent<
event.preventDefault();

if (save) {
connections.save(connection.name, connection);
window.config.addConnectionToConfig(connection);
}

this.props.connectTo(connection);
Expand Down
23 changes: 3 additions & 20 deletions src/component/Connection/ConnectionPage.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,10 @@
import { Link, Navigate } from 'react-router-dom';
import connections from './SavedConnections';
import { ConnectionObject } from './types';
import { ConnectionContext } from '../../Contexts';
import { useContext, useEffect, useState } from 'react';

function useRegisteredConnectionList(): null | Record<
string,
ConnectionObject
> {
const [registeredConnections, setRegisteredConnections] =
useState<null | Record<string, ConnectionObject>>(null);

useEffect(() => {
connections
.listConnections()
.then((data) => setRegisteredConnections(data ?? {}));
}, []);

return registeredConnections;
}
import { ConnectionContext, useConfiguration } from '../../Contexts';
import { useContext } from 'react';

function ConnectionPage() {
const registeredConnectionList = useRegisteredConnectionList();
const registeredConnectionList = useConfiguration().connections;
const { connectTo } = useContext(ConnectionContext);

if (registeredConnectionList === null) {
Expand Down
37 changes: 0 additions & 37 deletions src/component/Connection/SavedConnections.ts

This file was deleted.

7 changes: 7 additions & 0 deletions src/component/Connection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type ConnectionObject = {
name: string;
host: string;
port: number;
user: string;
password: string;
};
2 changes: 1 addition & 1 deletion src/component/ThemeSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { THEME_LIST } from '../../src/theme';
import { useTheme } from '..//Contexts';
import { useTheme } from '../Contexts';

export default function ThemeSelector() {
const { themeName, changeTheme } = useTheme();
Expand Down
52 changes: 43 additions & 9 deletions src/configuration.test.ts → src/configuration/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import { existsSync, mkdirSync, readFileSync, writeFile } from 'node:fs';
import envPaths from 'env-paths';
import { addConnectionToConfig, readConfigurationFile } from './configuration';
import {
addConnectionToConfig,
getConfiguration,
changeTheme,
testables,
} from '.';

const { getBaseConfig } = testables;

vi.mock('env-paths', () => ({
default: () => ({ config: 'config' }),
Expand Down Expand Up @@ -37,21 +43,21 @@ describe('read configuration from file', () => {
test('empty file', () => {
mockExistsSync.mockReturnValue(false);

expect(readConfigurationFile()).toBe(null);
expect(getConfiguration()).toStrictEqual(getBaseConfig());
});

test('existing file but empty', () => {
mockExistsSync.mockReturnValue(true);
mockReadFileSync.mockReturnValue('');

expect(readConfigurationFile()).toBe(null);
expect(getConfiguration()).toStrictEqual(getBaseConfig());
});

test('existing file without connexion key', () => {
mockExistsSync.mockReturnValue(true);
mockReadFileSync.mockReturnValue('{}');

expect(readConfigurationFile()).toStrictEqual({
expect(getConfiguration()).toStrictEqual({
connections: {},
});
});
Expand All @@ -60,7 +66,7 @@ describe('read configuration from file', () => {
mockExistsSync.mockReturnValue(true);
mockReadFileSync.mockReturnValue('{ "version": 1, "connections": {}}');

expect(readConfigurationFile()).toStrictEqual({
expect(getConfiguration()).toStrictEqual({
version: 1,
connections: {},
});
Expand Down Expand Up @@ -88,7 +94,7 @@ describe('read configuration from file', () => {
mockExistsSync.mockReturnValue(true);
mockReadFileSync.mockReturnValue(JSON.stringify(config));

expect(readConfigurationFile()).toStrictEqual({
expect(getConfiguration()).toStrictEqual({
version: 1,
connections: {
local: {
Expand All @@ -115,7 +121,7 @@ describe('add connection to config', () => {

test('empty file', async () => {
mockExistsSync.mockReturnValue(false);
await addConnectionToConfig({} as any, 'local', {
await addConnectionToConfig({
name: 'local',
host: 'localhost',
port: 3306,
Expand All @@ -128,6 +134,7 @@ describe('add connection to config', () => {
JSON.stringify(
{
version: 1,
theme: 'Dracula',
connections: {
local: {
name: 'local',
Expand Down Expand Up @@ -168,7 +175,7 @@ describe('add connection to config', () => {
mockExistsSync.mockReturnValue(true);
mockReadFileSync.mockReturnValue(JSON.stringify(config));

await addConnectionToConfig({} as any, 'test', {
await addConnectionToConfig({
name: 'test',
host: 'test',
port: 3306,
Expand Down Expand Up @@ -211,3 +218,30 @@ describe('add connection to config', () => {
);
});
});

describe('set theme', () => {
afterEach(() => {
vi.resetModules();
});

test('set theme', async () => {
mockExistsSync.mockReturnValue(false);

await changeTheme('test');

expect(mockWriteFile).toHaveBeenCalledWith(
'config/config.json',
JSON.stringify(
{
version: 1,
theme: 'test',
connections: {},
},
null,
2
),
'utf-8',
expect.any(Function)
);
});
});
78 changes: 42 additions & 36 deletions src/configuration.ts → src/configuration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,27 @@ import { dialog, safeStorage } from 'electron';
import { resolve } from 'node:path';
import { existsSync, mkdirSync, readFileSync, writeFile } from 'node:fs';
import envPaths from 'env-paths';
import { ConnectionObject } from './component/Connection/types';

export type Configuration = {
version: 1;
connections: Record<string, EncryptedConnectionObject>;
};

type EncryptedConnectionObject = {
password: string;
} & Omit<ConnectionObject, 'password'>;

type EncryptedConfiguration = {
connections: Record<string, EncryptedConnectionObject>;
} & Omit<Configuration, 'connections'>;
import { ConnectionObject } from '../component/Connection/types';
import { DEFAULT_THEME } from '../theme';
import {
Configuration,
EncryptedConnectionObject,
EncryptedConfiguration,
} from './type';

const envPath = envPaths('TianaTables', { suffix: '' });
const dataFilePath = resolve(envPath.config, 'config.json');

console.log('Configuration file path:', dataFilePath);

function getBaseConfig(): Configuration {
return {
version: 1,
theme: DEFAULT_THEME.name,
connections: {},
};
}

function encryptConnection(
connection: ConnectionObject
): EncryptedConnectionObject {
Expand All @@ -40,15 +43,15 @@ function decryptConnection(
};
}

export function readConfigurationFile(): null | Configuration {
export function getConfiguration(): Configuration {
if (!existsSync(dataFilePath)) {
return null;
return getBaseConfig();
}

const dataString = readFileSync(dataFilePath, 'utf-8');

if (!dataString) {
return null;
return getBaseConfig();
}

const config = JSON.parse(dataString) as EncryptedConfiguration;
Expand All @@ -63,26 +66,8 @@ export function readConfigurationFile(): null | Configuration {
),
};
}
export function addConnectionToConfig(
event: Electron.IpcMainInvokeEvent,
name: string,
connection: ConnectionObject
): void {
let config = readConfigurationFile();

if (!config) {
config = {
version: 1,
connections: {},
};
}

if (!config.connections) {
config.connections = {};
}

config.connections[name] = connection;

function writeConfiguration(config: Configuration): void {
const encryptedConfig = {
...config,
connections: Object.fromEntries(
Expand All @@ -105,3 +90,24 @@ export function addConnectionToConfig(
}
);
}

export function addConnectionToConfig(connection: ConnectionObject): void {
const config = getConfiguration() ?? getBaseConfig();

if (!config.connections) {
config.connections = {};
}

config.connections[connection.name] = connection;

writeConfiguration(config);
}

export function changeTheme(theme: string): void {
const config = getConfiguration() ?? getBaseConfig();
config.theme = theme;

writeConfiguration(config);
}

export const testables = { getBaseConfig };
Loading

0 comments on commit 2889c9f

Please sign in to comment.