Skip to content

Commit 770573b

Browse files
Merge pull request #33 from dblens/upgrade/pg
allow users to disconnect form a database
2 parents 267e156 + c49064b commit 770573b

File tree

5 files changed

+102
-21
lines changed

5 files changed

+102
-21
lines changed

src/components/MainScreen.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import React, { useState } from 'react';
1+
/* eslint-disable no-alert */
2+
/* eslint-disable no-restricted-globals */
3+
import React, { useEffect, useState } from 'react';
4+
import electron from 'electron';
25
import DbSession from '../sessions/DbSession';
36
import OverviewScreen from './Overview/OverviewScreen';
47
import SqlScreen from './SqlScreen';
@@ -7,6 +10,7 @@ import TableScreen from './TableScreen';
710
import Titlebar from './Titlebar';
811
import ErdContainer from './ERD/ErdContainer';
912
import SettingsContainer from './Settings/SettingsContainer';
13+
import { useAppState } from '../state/AppProvider';
1014

1115
interface MainScreenProps {
1216
session: DbSession;
@@ -18,6 +22,30 @@ const MainScreen: React.FC<MainScreenProps> = ({
1822
session,
1923
}: MainScreenProps) => {
2024
const [selectedTab, setSelectedTab] = useState('OVERVIEW');
25+
const [, dispatch] = useAppState();
26+
27+
const disconnect = async () => {
28+
if (confirm('Are you sure want to disconnect from the database?')) {
29+
const { status } = await electron.ipcRenderer.invoke('disconnect');
30+
if (status === 'DISCONNECTED') {
31+
dispatch({
32+
type: 'CLEAR_SESSION',
33+
});
34+
}
35+
}
36+
};
37+
// eslint-disable-next-line react-hooks/exhaustive-deps
38+
const escFunction = (e: KeyboardEvent) => {
39+
if (e.code === 'Escape' && session) {
40+
disconnect();
41+
}
42+
};
43+
useEffect(() => {
44+
document.addEventListener('keydown', escFunction, false);
45+
return () => {
46+
document.removeEventListener('keydown', escFunction, false);
47+
};
48+
}, [escFunction]);
2149

2250
return (
2351
<div className="w-screen h-screen max-h-screen text-gray-800 focus:font-bold focus:outline-none">

src/components/Sidebar.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
/* eslint-disable no-alert */
2+
/* eslint-disable no-restricted-globals */
13
import React from 'react';
4+
import electron from 'electron';
5+
import ReactTooltip from 'react-tooltip';
6+
import { useAppState } from '../state/AppProvider';
27

38
interface SidebarProps {
49
selectedTab: string;
@@ -49,9 +54,35 @@ const Sidebar: React.FC<SidebarProps> = ({
4954
selectedTab,
5055
setSelectedTab,
5156
}: SidebarProps) => {
57+
const [, dispatch] = useAppState();
58+
59+
const disconnect = async () => {
60+
if (confirm('Are you sure want to disconnect from the database?')) {
61+
const { status } = await electron.ipcRenderer.invoke('disconnect');
62+
if (status === 'DISCONNECTED') {
63+
dispatch({
64+
type: 'CLEAR_SESSION',
65+
});
66+
}
67+
}
68+
};
5269
return (
53-
<div className="bg-gray-900 text-gray-100 w-12 pt-8 flex flex-col border border-gray-700 text-xs">
54-
<button type="button" className="mb-4">
70+
<div className="bg-gray-900 text-gray-100 w-12 flex flex-col border border-gray-700 text-xs">
71+
<button
72+
type="button"
73+
className="m-2 text-sm text-green-600 font-bold animate-ping"
74+
onClick={disconnect}
75+
data-tip
76+
data-for="btn-disconnect"
77+
>
78+
<span role="img" aria-label="app-icon" className="p-2 font-mono">
79+
80+
</span>
81+
</button>
82+
<ReactTooltip id="btn-disconnect" type="dark" place="right">
83+
<span>Click to Disconnect from the database</span>
84+
</ReactTooltip>
85+
<button type="button" className="mb-4 pt-4">
5586
<span
5687
role="img"
5788
aria-label="app-icon"

src/electron/DBHandler.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ import { BrowserWindow } from 'electron';
33
import { IpcMainInvokeEvent } from 'electron/main';
44
import { Client, ClientBase } from 'pg';
55

6-
const connections: Record<
7-
string,
8-
{ client: ClientBase; window: BrowserWindow }
9-
> = {};
6+
let connections: Record<string, { client: ClientBase; window: BrowserWindow }> =
7+
{};
108

119
export const connectDB = async ({
1210
window,
@@ -46,6 +44,12 @@ export const connectDB = async ({
4644
return { status: 'FAILED', uuid };
4745
};
4846

47+
export const disconnectDB = async () => {
48+
connections = {};
49+
50+
return { status: 'DISCONNECTED' };
51+
};
52+
4953
export const sqlExecute = async (
5054
_: IpcMainInvokeEvent,
5155
{ uuid = '', sql = 'SELECT NOW();' } = {}

src/main.dev.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,21 @@
1111
import 'core-js/stable';
1212
import 'regenerator-runtime/runtime';
1313
import path from 'path';
14-
import { app, BrowserWindow, shell, ipcMain, screen } from 'electron';
14+
import {
15+
app,
16+
BrowserWindow,
17+
shell,
18+
ipcMain,
19+
screen,
20+
dialog,
21+
IpcMainEvent,
22+
} from 'electron';
1523
import { autoUpdater } from 'electron-updater';
1624
import log from 'electron-log';
17-
import { connectDB, sqlExecute } from './electron/DBHandler';
18-
19-
import MenuBuilder from './menu';
20-
import { IpcMainEvent } from 'electron/main';
21-
import { dialog } from 'electron';
2225
import fs from 'fs';
26+
import MenuBuilder from './menu';
27+
import { connectDB, disconnectDB, sqlExecute } from './electron/DBHandler';
28+
2329
const { format } = require('@fast-csv/format');
2430

2531
export default class AppUpdater {
@@ -160,7 +166,7 @@ app.on('activate', () => {
160166
if (mainWindow === null) createWindow();
161167
});
162168

163-
ipcMain.on('ExportCSV', (_: IpcMainEvent, params: any) => {
169+
ipcMain.on('ExportCSV', (_: IpcMainEvent, params) => {
164170
const { fileName, data } = params;
165171

166172
dialog
@@ -172,27 +178,29 @@ ipcMain.on('ExportCSV', (_: IpcMainEvent, params: any) => {
172178
filters: [{ name: 'CSV files', extensions: ['csv'] }],
173179
properties: [],
174180
})
175-
.then((file: any) => {
181+
.then((file: Electron.SaveDialogReturnValue) => {
176182
// Stating whether dialog operation was cancelled or not.
177183
console.log(file.canceled);
178184
if (!file.canceled) {
179-
let items: unknown[] = [];
185+
const items: unknown[] = [];
180186

181187
const stream = format({ headers: true });
182-
const fileName = file.filePath.toString();
183-
const csvFile = fs.createWriteStream(fileName);
188+
const filePath = file?.filePath?.toString() ?? '/Downloads';
189+
const csvFile = fs.createWriteStream(filePath);
184190
stream.pipe(csvFile);
185-
data.forEach((el: Record<string, any>, i: number) => {
186-
const parsed: Record<string, any> = {};
187-
Object.entries(el).forEach(([key, value]: [any, any]) => {
191+
data.forEach((el: Record<string, unknown>, i: number) => {
192+
const parsed: Record<string, unknown> = {};
193+
Object.entries(el).forEach(([key, value]: [string, unknown]) => {
188194
parsed[key] =
189195
typeof value === 'object' ? JSON.stringify(value) : value;
190196
});
191197
items.push(parsed);
192198
stream.write(items[i]);
193199
});
194200
stream.end();
201+
return 1;
195202
}
203+
return 0;
196204
})
197205
.catch((err) => {
198206
console.log(err);
@@ -209,6 +217,13 @@ ipcMain.handle('connect', async (_, params) => {
209217
return res;
210218
});
211219

220+
ipcMain.handle('disconnect', async () => {
221+
console.log('disconnect');
222+
// console.log(JSON.stringify({ params }, null, 2));
223+
const res = await disconnectDB();
224+
return res;
225+
});
226+
212227
ipcMain.handle('SQL_EXECUTE', sqlExecute);
213228

214229
// Define custom protocol handler.

src/state/reducer.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface AppState {
1515

1616
export type AppStateActions =
1717
| { type: 'SET_SESSION'; payload: DbSession }
18+
| { type: 'CLEAR_SESSION' }
1819
| { type: 'SET_HISTORY'; payload: HistoryType[] }
1920
| { type: 'ADD_HISTORY'; payload: HistoryType }
2021
| { type: 'REMOVE_HISTORY'; payload: number }
@@ -29,6 +30,8 @@ export function appReducer(
2930
switch (action.type) {
3031
case 'SET_SESSION':
3132
return { ...state, session: action.payload };
33+
case 'CLEAR_SESSION':
34+
return { ...state, session: undefined };
3235
case 'SET_HISTORY':
3336
return { ...state, history: action.payload };
3437
case 'ADD_HISTORY':

0 commit comments

Comments
 (0)