Skip to content

Commit 2cf6764

Browse files
authored
feat: add reconnect and connection status (#71)
1 parent 670349e commit 2cf6764

File tree

12 files changed

+157
-7
lines changed

12 files changed

+157
-7
lines changed

src/drivers/MySQLConnection.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,15 @@ function mapHeaderType(column: ColumnDefinition): QueryResultHeader {
6868
export default class MySQLConnection extends SQLLikeConnection {
6969
protected connectionConfig: DatabaseConnectionConfig;
7070
protected connection: Connection | undefined;
71+
protected onStateChangedCallback: (state: string) => void;
7172

72-
constructor(connectionConfig: DatabaseConnectionConfig) {
73+
constructor(
74+
connectionConfig: DatabaseConnectionConfig,
75+
statusChanged: (state: string) => void
76+
) {
7377
super();
7478
this.connectionConfig = connectionConfig;
79+
this.onStateChangedCallback = statusChanged;
7580
}
7681

7782
protected async getConnection() {
@@ -81,7 +86,20 @@ export default class MySQLConnection extends SQLLikeConnection {
8186
dateStrings: true,
8287
namedPlaceholders: true,
8388
});
89+
90+
console.log('connected');
91+
this.onStateChangedCallback('Connected');
92+
93+
this.connection.on('error', () => {
94+
if (this.connection) {
95+
this.connection.removeAllListeners();
96+
this.connection.destroy();
97+
this.connection = undefined;
98+
this.onStateChangedCallback('Disconnected');
99+
}
100+
});
84101
}
102+
85103
return this.connection;
86104
}
87105

@@ -131,7 +149,9 @@ export default class MySQLConnection extends SQLLikeConnection {
131149
}
132150

133151
async close() {
134-
const conn = await this.getConnection();
135-
conn.destroy();
152+
if (this.connection) {
153+
const conn = await this.getConnection();
154+
conn.destroy();
155+
}
136156
}
137157
}

src/drivers/common/MySQLCommonInterface.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,13 @@ export default class MySQLCommonInterface extends SQLCommonInterface {
200200
return true;
201201
}
202202

203+
async getVersion(): Promise<string> {
204+
const response = await this.singleExecute<{ 'VERSION()': string }>(
205+
'SELECT VERSION();'
206+
);
207+
return response.rows[0]['VERSION()'];
208+
}
209+
203210
async getSchema(): Promise<DatabaseSchemas> {
204211
const databaseListResponse = await this.singleExecute<MySqlDatabase>(
205212
qb().table('information_schema.SCHEMATA').select('SCHEMA_NAME').toRawSQL()

src/drivers/common/NotImplementCommonInterface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@ export default class NotImplementCommonInterface extends SQLCommonInterface {
1313
async switchDatabase(): Promise<boolean> {
1414
throw 'Not implemented';
1515
}
16+
17+
async getVersion(): Promise<string> {
18+
throw 'Not implemented';
19+
}
1620
}

src/drivers/common/SQLCommonInterface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { DatabaseSchemas, TableDefinitionSchema } from 'types/SqlSchema';
22

33
export default abstract class SQLCommonInterface {
4+
abstract getVersion(): Promise<string>;
45
abstract getSchema(): Promise<DatabaseSchemas>;
56
abstract getTableSchema(
67
database: string,

src/main/ipc/handleConnection.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,26 @@ import SQLLikeConnection, {
33
ConnectionStoreItem,
44
DatabaseConnectionConfig,
55
} from './../../drivers/SQLLikeConnection';
6-
import { ipcMain } from 'electron';
6+
import { BrowserWindow, ipcMain } from 'electron';
77

88
export default class ConnectionIpcHandler {
99
protected connection: SQLLikeConnection | undefined;
10+
protected window?: BrowserWindow;
11+
12+
attachWindow(window: BrowserWindow) {
13+
this.window = window;
14+
}
1015

1116
register() {
1217
ipcMain.handle('connect', async (_, [store]: [ConnectionStoreItem]) => {
1318
if (store.type === 'mysql') {
14-
console.log('create mysql connection');
1519
this.connection = new MySQLConnection(
16-
store.config as unknown as DatabaseConnectionConfig
20+
store.config as unknown as DatabaseConnectionConfig,
21+
(status) => {
22+
if (this.window) {
23+
this.window.webContents.send('connection-status-change', status);
24+
}
25+
}
1726
);
1827
}
1928
return true;

src/main/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const createWindow = async () => {
6868
mainWindow.setMenu(null);
6969

7070
otherIpcHandler.attachWindow(mainWindow);
71+
connectionIpcHandler.attachWindow(mainWindow);
7172

7273
mainWindow.loadURL(resolveHtmlPath('index.html'));
7374

src/main/preload.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ const electronHandler = {
5050
ipcRenderer.invoke('close');
5151
},
5252

53+
listenConnectionStatusChanged: (
54+
callback: (event: IpcRendererEvent, status: string) => void
55+
) => {
56+
ipcRenderer.removeAllListeners('connection-status-change');
57+
return ipcRenderer.on('connection-status-change', callback);
58+
},
59+
5360
showMessageBox: (options: MessageBoxSyncOptions): Promise<number> =>
5461
ipcRenderer.invoke('show-message-box', [options]),
5562

@@ -121,7 +128,6 @@ const electronHandler = {
121128
},
122129

123130
openExternal: (url: string) => ipcRenderer.invoke('open-external', [url]),
124-
125131
};
126132

127133
contextBridge.exposeInMainWorld('electron', electronHandler);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { useCallback, useState, useEffect } from 'react';
2+
3+
interface StatusBarConnection {
4+
version?: string;
5+
status?: string;
6+
}
7+
8+
const MESSAGE_CHANNEL = 'status-bar-connection';
9+
10+
export function useStatusBar() {
11+
const setStatusBarConnectionStatus = useCallback(
12+
(data?: StatusBarConnection) => {
13+
window.postMessage({
14+
type: MESSAGE_CHANNEL,
15+
data,
16+
});
17+
},
18+
[]
19+
);
20+
21+
return { setStatusBarConnectionStatus };
22+
}
23+
24+
export function useStatusBarData() {
25+
const [connectionStatus, setConnectionStatus] = useState<
26+
StatusBarConnection | undefined
27+
>();
28+
29+
useEffect(() => {
30+
const receiveMessage = (
31+
e: MessageEvent<{ type: string; data?: StatusBarConnection }>
32+
) => {
33+
if (e.data?.type === MESSAGE_CHANNEL) {
34+
setConnectionStatus((prev) => {
35+
if (e.data?.data) return { ...prev, ...e.data?.data };
36+
return undefined;
37+
});
38+
}
39+
};
40+
41+
window.addEventListener('message', receiveMessage);
42+
return () => window.removeEventListener('message', receiveMessage);
43+
}, [setConnectionStatus]);
44+
45+
return { connectionStatus };
46+
}

src/renderer/components/StatusBar/index.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import pkg from './../../../../package.json';
44
import Button from '../Button';
55
import Stack from '../Stack';
66
import ButtonGroup from '../ButtonGroup';
7+
import { useStatusBarData } from './hook';
78

89
function StatusBarAutoUpdate() {
910
const [autoUpdateMessage, setAutoUpdateMessage] = useState('');
@@ -93,10 +94,30 @@ function StatusBarAutoUpdate() {
9394
}
9495

9596
export default function StatusBar() {
97+
const { connectionStatus } = useStatusBarData();
98+
9699
return (
97100
<div className={styles.statusBarContainer}>
98101
<ul>
99102
<li>QueryMaster v{pkg.version}</li>
103+
{!!connectionStatus?.version && (
104+
<li>MySQL {connectionStatus?.version}</li>
105+
)}
106+
{!!connectionStatus?.status && (
107+
<li>
108+
<span
109+
style={{
110+
color:
111+
connectionStatus.status === 'Connected'
112+
? '#27ae60'
113+
: '#e74c3c',
114+
}}
115+
>
116+
117+
</span>
118+
&nbsp;&nbsp;{connectionStatus.status}
119+
</li>
120+
)}
100121
<li style={{ flexGrow: 1 }}></li>
101122
<StatusBarAutoUpdate />
102123
</ul>

src/renderer/components/StatusBar/styles.module.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
display: flex;
1111
}
1212

13+
.statusBarContainer ul li {
14+
margin-right: 20px;
15+
}
16+
17+
.statusBarContainer ul li:last-child {
18+
margin-right: 0px;
19+
}
20+
1321
.popup {
1422
font-size: 1rem;
1523
width: 300px;

0 commit comments

Comments
 (0)