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

start registreting theme in config #5

Merged
merged 2 commits into from
Feb 20, 2024
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
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
Loading