Skip to content

Commit

Permalink
Merge pull request #107 from jdeniau/save-sql-query-into-file
Browse files Browse the repository at this point in the history
  • Loading branch information
jdeniau authored Sep 17, 2024
2 parents 89040f0 + e732937 commit d952096
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 13 deletions.
9 changes: 9 additions & 0 deletions src/configuration/filePaths.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { app } from 'electron';
import { existsSync, mkdirSync } from 'node:fs';
import path, { dirname, resolve } from 'node:path';
import { isDevApp } from '../main-process/helpers';

Expand All @@ -13,6 +14,14 @@ export function getConfigurationFolder() {
return dirname(getConfigurationPath());
}

export function createConfigurationFolderIfNotExists() {
const configurationPah = getConfigurationPath();

if (!existsSync(dirname(configurationPah))) {
mkdirSync(dirname(configurationPah), { recursive: true });
}
}

export function getLogPath() {
return path.join(
app.getPath('userData'),
Expand Down
14 changes: 6 additions & 8 deletions src/configuration/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { dialog, safeStorage } from 'electron';
import { existsSync, mkdirSync, readFileSync, writeFile } from 'node:fs';
import { dirname } from 'node:path';
import { existsSync, readFileSync, writeFile } from 'node:fs';
import log from 'electron-log';
import { WindowState } from '../main-process/windowState';
import { CONFIGURATION_CHANNEL } from '../preload/configurationChannel';
import { ConnectionObject, ConnectionObjectWithoutSlug } from '../sql/types';
import { getConfigurationPath } from './filePaths';
import {
createConfigurationFolderIfNotExists,
getConfigurationPath,
} from './filePaths';
import { DEFAULT_LOCALE } from './locale';
import { DEFAULT_THEME } from './themes';
import {
Expand Down Expand Up @@ -93,12 +95,8 @@ function writeConfiguration(config: Configuration): void {
),
};

// mkdirSync(configPath, { recursive: true });

// create the folder of the `dataFilePath` if it does not exist
if (!existsSync(dirname(configurationPath))) {
mkdirSync(dirname(configurationPath), { recursive: true });
}
createConfigurationFolderIfNotExists();

writeFile(
configurationPath,
Expand Down
48 changes: 48 additions & 0 deletions src/main-process/sqlFileStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'path';
import {
createConfigurationFolderIfNotExists,
getConfigurationFolder,
} from '../configuration/filePaths';
import { SQL_FILE_STORAGE_CHANNEL } from '../preload/sqlFileStorageChannel';

const LATEST_SQL_FILENAME = 'latest.sql';

function getSqlFilePath(): string {
const configurationFolder = getConfigurationFolder();

return join(configurationFolder, LATEST_SQL_FILENAME);
}

function loadLatest(): string | null {
const latestSqlFilePath = getSqlFilePath();

if (!existsSync(latestSqlFilePath)) {
return null;
}

return readFileSync(latestSqlFilePath, 'utf-8');
}

function saveLatest(sql: string): void {
const latestSqlFilePath = getSqlFilePath();

createConfigurationFolderIfNotExists();

return writeFileSync(latestSqlFilePath, sql);
}

const IPC_EVENT_BINDING = {
[SQL_FILE_STORAGE_CHANNEL.LOAD_LATEST]: loadLatest,
[SQL_FILE_STORAGE_CHANNEL.SAVE_LATEST]: saveLatest,
} as const;

export function bindIpcMainSqlFileStorage(ipcMain: Electron.IpcMain): void {
for (const [channel, handler] of Object.entries(IPC_EVENT_BINDING)) {
ipcMain.handle(channel, (event, ...args: unknown[]) =>
// convert the first argument to senderId and bind the rest
// @ts-expect-error issue with strict type in tsconfig, but seems to work at runtime
handler(...args)
);
}
}
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { getLogPath } from './configuration/filePaths';
import { isDevApp, isMacPlatform } from './main-process/helpers';
import { installReactDevToolsExtension } from './main-process/installReactDevToolsExtension';
import { createMenu } from './main-process/menu';
import { bindIpcMainSqlFileStorage } from './main-process/sqlFileStorage';
import WindowStateKeeper from './main-process/windowState';
import connectionStackInstance from './sql';

Expand Down Expand Up @@ -84,6 +85,7 @@ app.whenReady().then(() => {
installReactDevToolsExtension();

bindIpcMainConfiguration(ipcMain);
bindIpcMainSqlFileStorage(ipcMain);
connectionStackInstance.bindIpcMain(ipcMain);

ipcMain.handle('get-is-dev', () => {
Expand Down
3 changes: 3 additions & 0 deletions src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { contextBridge, ipcRenderer } from 'electron';
import { config } from './preload/config';
import { navigationListener } from './preload/navigationListener';
import { sql } from './preload/sql';
import { sqlFileStorage } from './preload/sqlFileStorage';

contextBridge.exposeInMainWorld('config', config);
contextBridge.exposeInMainWorld('sql', sql);
contextBridge.exposeInMainWorld('sqlFileStorage', sqlFileStorage);
contextBridge.exposeInMainWorld('navigationListener', navigationListener);

ipcRenderer.invoke('get-is-dev').then((isDev) => {
Expand All @@ -25,6 +27,7 @@ declare global {
isMac: boolean;
config: typeof config;
sql: typeof sql;
sqlFileStorage: typeof sqlFileStorage;
navigationListener: typeof navigationListener;
}
}
12 changes: 12 additions & 0 deletions src/preload/sqlFileStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { bindChannel } from './bindChannel';
import { SQL_FILE_STORAGE_CHANNEL } from './sqlFileStorageChannel';

interface SqlFileStorage {
loadLatest(): Promise<string | null>;
saveLatest(sql: string): Promise<void>;
}

export const sqlFileStorage: SqlFileStorage = {
loadLatest: bindChannel(SQL_FILE_STORAGE_CHANNEL.LOAD_LATEST),
saveLatest: bindChannel(SQL_FILE_STORAGE_CHANNEL.SAVE_LATEST),
};
4 changes: 4 additions & 0 deletions src/preload/sqlFileStorageChannel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum SQL_FILE_STORAGE_CHANNEL {
LOAD_LATEST = 'sql-file-storage:load-latest',
SAVE_LATEST = 'sql-file-storage:save-latest',
}
26 changes: 21 additions & 5 deletions src/renderer/routes/sql.$connectionSlug.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { Button, Flex, Form } from 'antd';
import { ActionFunctionArgs, useFetcher } from 'react-router-dom';
import invariant from 'tiny-invariant';
Expand All @@ -9,10 +10,19 @@ import { RawSqlEditor } from '../component/MonacoEditor/RawSqlEditor';
import RawSqlResult from '../component/Query/RawSqlResult/RowDataPacketResult';

// const DEFAULT_VALUE = `SELECT * FROM employees e WHERE e.gender = 'F' LIMIT 10;`;
const DEFAULT_VALUE = `SELECT t.
FROM ticketing t
JOIN
LIMIT 10;`;
function useSqlFileStorage(): [string | null, (value: string) => void] {
const [sqlQuery, setSqlQuery] = useState<string | null>(null);

useEffect(() => {
window.sqlFileStorage.loadLatest().then((v) => setSqlQuery(v ?? ''));
}, []);

const saveValue = (value: string) => {
window.sqlFileStorage.saveLatest(value);
};

return [sqlQuery, saveValue];
}

type SqlActionReturnTypes =
| {
Expand Down Expand Up @@ -54,15 +64,21 @@ export default function SqlPage() {
const { t } = useTranslation();
const [form] = Form.useForm();
const fetcher = useFetcher<SqlActionReturnTypes>();
const [sqlQuery, saveSqlQuery] = useSqlFileStorage();

const { state } = fetcher;

if (sqlQuery === null) {
return null;
}

return (
<Flex vertical gap="small" style={{ height: '100%' }}>
<Form
form={form}
initialValues={{ raw: DEFAULT_VALUE }}
initialValues={{ raw: sqlQuery }}
onFinish={(values) => {
saveSqlQuery(values.raw);
fetcher.submit(values, {
method: 'post',
});
Expand Down

0 comments on commit d952096

Please sign in to comment.