Skip to content

Commit b748b10

Browse files
authored
fix: Set default server servers.json and Open server on click notification (#2144)
1 parent 1a48b69 commit b748b10

7 files changed

Lines changed: 103 additions & 25 deletions

File tree

src/notifications/main.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Notification, nativeImage, NativeImage } from 'electron';
22

33
import { invoke } from '../ipc/main';
4-
import { dispatch, listen } from '../store';
4+
import { dispatch, dispatchSingle, listen } from '../store';
5+
import { ActionIPCMeta } from '../store/actions';
56
import { hasMeta } from '../store/fsa';
67
import { getRootWindow } from '../ui/main/rootWindow';
78
import {
@@ -45,7 +46,8 @@ const notifications = new Map();
4546

4647
const createNotification = async (
4748
id: string,
48-
{ title, body, icon, silent, canReply, actions }: ExtendedNotificationOptions
49+
{ title, body, icon, silent, canReply, actions }: ExtendedNotificationOptions,
50+
ipcMeta?: ActionIPCMeta
4951
): Promise<string> => {
5052
const notification = new Notification({
5153
title,
@@ -60,29 +62,43 @@ const createNotification = async (
6062
});
6163

6264
notification.addListener('show', () => {
63-
dispatch({ type: NOTIFICATIONS_NOTIFICATION_SHOWN, payload: { id } });
65+
dispatchSingle({
66+
type: NOTIFICATIONS_NOTIFICATION_SHOWN,
67+
payload: { id },
68+
ipcMeta,
69+
});
6470
});
6571

6672
notification.addListener('close', () => {
67-
dispatch({ type: NOTIFICATIONS_NOTIFICATION_CLOSED, payload: { id } });
73+
dispatchSingle({
74+
type: NOTIFICATIONS_NOTIFICATION_CLOSED,
75+
payload: { id },
76+
ipcMeta,
77+
});
6878
notifications.delete(id);
6979
});
7080

7181
notification.addListener('click', () => {
72-
dispatch({ type: NOTIFICATIONS_NOTIFICATION_CLICKED, payload: { id } });
82+
dispatchSingle({
83+
type: NOTIFICATIONS_NOTIFICATION_CLICKED,
84+
payload: { id },
85+
ipcMeta,
86+
});
7387
});
7488

7589
notification.addListener('reply', (_event, reply) => {
76-
dispatch({
90+
dispatchSingle({
7791
type: NOTIFICATIONS_NOTIFICATION_REPLIED,
7892
payload: { id, reply },
93+
ipcMeta,
7994
});
8095
});
8196

8297
notification.addListener('action', (_event, index) => {
83-
dispatch({
98+
dispatchSingle({
8499
type: NOTIFICATIONS_NOTIFICATION_ACTIONED,
85100
payload: { id, index },
101+
ipcMeta,
86102
});
87103
});
88104

@@ -118,27 +134,26 @@ const updateNotification = async (
118134
return id;
119135
};
120136

121-
const handleCreateEvent = async ({
122-
tag,
123-
...options
124-
}: ExtendedNotificationOptions): Promise<string> => {
137+
const handleCreateEvent = async (
138+
{ tag, ...options }: ExtendedNotificationOptions,
139+
ipcMeta?: ActionIPCMeta
140+
): Promise<string> => {
125141
if (tag && notifications.has(tag)) {
126142
return updateNotification(tag, options);
127143
}
128144

129145
const id = tag || Math.random().toString(36).slice(2);
130-
return createNotification(id, options);
146+
return createNotification(id, options, ipcMeta);
131147
};
132148

133149
export const setupNotifications = (): void => {
134150
listen(NOTIFICATIONS_CREATE_REQUESTED, async (action) => {
135151
if (!hasMeta(action)) {
136152
return;
137153
}
138-
139154
dispatch({
140155
type: NOTIFICATIONS_CREATE_RESPONDED,
141-
payload: await handleCreateEvent(action.payload),
156+
payload: await handleCreateEvent(action.payload, action.ipcMeta),
142157
meta: {
143158
id: action.meta.id,
144159
response: true,

src/store/actions.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,11 @@ type RootActions = {
3737

3838
export type ActionOf<Type extends keyof RootActions> = RootActions[Type];
3939

40-
export type RootAction = RootActions[keyof RootActions];
40+
export type RootAction = RootActions[keyof RootActions] & {
41+
ipcMeta?: ActionIPCMeta;
42+
};
43+
44+
export type ActionIPCMeta = {
45+
type: 'single' | 'local';
46+
webContentsId?: number;
47+
};

src/store/fsa.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ export const isLocallyScoped = <
4141
hasMeta(action) &&
4242
(action as Action & { meta: { scope: unknown } }).meta.scope === 'local';
4343

44+
export const isSingleScoped = <
45+
Action extends FluxStandardAction<string, unknown>
46+
>(
47+
action: Action
48+
): action is Action & { ipcMeta: { scope: 'single'; webContentsId: number } } =>
49+
(action as any & { ipcMeta: { webContentsId: unknown } }).ipcMeta
50+
?.webContentsId &&
51+
(action as any & { ipcMeta: { scope: unknown } }).ipcMeta?.scope === 'single';
52+
4453
export const isErrored = <Action extends FluxStandardAction<string, unknown>>(
4554
action: Action
4655
): action is Action & { error: true; payload: Error } =>

src/store/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ export const dispatch = <Action extends RootAction>(action: Action): void => {
4545
reduxStore.dispatch(action);
4646
};
4747

48+
export const dispatchSingle = <Action extends RootAction>(
49+
action: Action
50+
): void => {
51+
reduxStore.dispatch({
52+
...action,
53+
ipcMeta: { ...action.ipcMeta, scope: 'single' },
54+
});
55+
};
56+
57+
export const dispatchLocal = <Action extends RootAction>(
58+
action: Action
59+
): void => {
60+
reduxStore.dispatch({
61+
...action,
62+
ipcMeta: { ...action.ipcMeta, scope: 'local' },
63+
meta: { scope: 'local' },
64+
});
65+
};
66+
4867
type Selector<T> = (state: RootState) => T;
4968

5069
export const select = <T>(selector: Selector<T>): T =>

src/store/ipc.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,58 @@ import {
66
handle as handleFromRenderer,
77
invoke as invokeFromRenderer,
88
} from '../ipc/renderer';
9-
import { isFSA, FluxStandardAction, isLocallyScoped, hasMeta } from './fsa';
9+
import {
10+
isFSA,
11+
FluxStandardAction,
12+
isLocallyScoped,
13+
hasMeta,
14+
isSingleScoped,
15+
} from './fsa';
1016

1117
const enum ActionScope {
1218
LOCAL = 'local',
19+
SINGLE = 'single',
1320
}
1421

1522
export const forwardToRenderers: Middleware = (api: MiddlewareAPI) => {
1623
const renderers = new Set<WebContents>();
1724

1825
handleOnMain('redux/get-initial-state', async (webContents) => {
1926
renderers.add(webContents);
20-
2127
webContents.addListener('destroyed', () => {
2228
renderers.delete(webContents);
2329
});
2430

2531
return api.getState();
2632
});
2733

28-
handleOnMain('redux/action-dispatched', async (_, action) => {
29-
api.dispatch(action);
34+
handleOnMain('redux/action-dispatched', async (webContents, action) => {
35+
api.dispatch({
36+
...action,
37+
ipcMeta: { webContentsId: webContents.id, ...action.ipcMeta },
38+
});
3039
});
3140

3241
return (next: Dispatch) => (action: FluxStandardAction<string, unknown>) => {
3342
if (!isFSA(action) || isLocallyScoped(action)) {
3443
return next(action);
3544
}
36-
3745
const rendererAction = {
3846
...action,
3947
meta: {
4048
...(hasMeta(action) && action.meta),
4149
scope: ActionScope.LOCAL,
4250
},
4351
};
44-
52+
if (isSingleScoped(action)) {
53+
const { webContentsId } = action.ipcMeta;
54+
[...renderers]
55+
.filter((w) => w.id === webContentsId)
56+
.forEach((w) => {
57+
invokeFromMain(w, 'redux/action-dispatched', rendererAction);
58+
});
59+
return next(action);
60+
}
4561
renderers.forEach((webContents) => {
4662
invokeFromMain(webContents, 'redux/action-dispatched', rendererAction);
4763
});

src/ui/main/debounce.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export function debounce<T extends (...params: any[]) => unknown>(
2+
cb: T,
3+
wait = 20
4+
): T {
5+
let h: ReturnType<typeof setTimeout> | undefined;
6+
const callable = (...args: any) => {
7+
h && clearTimeout(h);
8+
h = setTimeout(() => cb(...args), wait);
9+
};
10+
return <T>(<any>callable);
11+
}

src/ui/main/rootWindow.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ import { createStructuredSelector } from 'reselect';
1414

1515
import { setupRootWindowReload } from '../../app/main/dev';
1616
import { Server } from '../../servers/common';
17-
import { dispatch, select, watch, listen } from '../../store';
17+
import { select, watch, listen, dispatchLocal } from '../../store';
1818
import { RootState } from '../../store/rootReducer';
1919
import { ROOT_WINDOW_STATE_CHANGED, WEBVIEW_FOCUS_REQUESTED } from '../actions';
2020
import { RootWindowIcon, WindowState } from '../common';
2121
import { selectGlobalBadge, selectGlobalBadgeCount } from '../selectors';
22+
import { debounce } from './debounce';
2223
import { getTrayIconPath } from './icons';
2324

2425
const webPreferences: WebPreferences = {
@@ -207,12 +208,12 @@ export const setupRootWindow = (): void => {
207208
}),
208209
];
209210

210-
const fetchAndDispatchWindowState = async (): Promise<void> => {
211-
dispatch({
211+
const fetchAndDispatchWindowState = debounce(async (): Promise<void> => {
212+
dispatchLocal({
212213
type: ROOT_WINDOW_STATE_CHANGED,
213214
payload: await fetchRootWindowState(),
214215
});
215-
};
216+
}, 1000);
216217

217218
getRootWindow().then((rootWindow) => {
218219
rootWindow.addListener('show', fetchAndDispatchWindowState);

0 commit comments

Comments
 (0)