Skip to content

Commit 1cb02ef

Browse files
authored
Fixed an issue where notification goes missing in GUI (#2623)
* Fixed an issue where notification goes missing in GUI * Fixed lint errors * Fixed notification count mismatch * Fixed an issue where electron\'s push notification shows invalid offer
1 parent 81f4e3b commit 1cb02ef

File tree

7 files changed

+70
-58
lines changed

7 files changed

+70
-58
lines changed

packages/gui/src/components/notification/NotificationsDropdown.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Trans } from '@lingui/macro';
44
import { Badge, Box, Button } from '@mui/material';
55
import React from 'react';
66

7-
import useNotifications from '../../hooks/useNotifications';
7+
import useValidNotifications from '../../hooks/useValidNotifications';
88

99
import NotificationsMenu from './NotificationsMenu';
1010

@@ -21,7 +21,7 @@ const buttonStyle = (theme) => ({
2121
});
2222

2323
export default function NotificationsDropdown() {
24-
const { unseenCount, setAsSeen } = useNotifications();
24+
const { unseenCount, setAsSeen } = useValidNotifications();
2525

2626
return (
2727
<DropdownBase>

packages/gui/src/components/notification/NotificationsProvider.tsx

+39-41
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
useLocalStorage,
55
} from '@chia-network/api-react';
66
import { orderBy } from 'lodash';
7-
import React, { useMemo, useEffect, useCallback, createContext, type ReactNode } from 'react';
7+
import React, { useMemo, useCallback, createContext, type ReactNode } from 'react';
88

99
import type Notification from '../../@types/Notification';
1010
import useBlockchainNotifications from '../../hooks/useBlockchainNotifications';
@@ -21,7 +21,7 @@ export const NotificationsContext = createContext<
2121
isLoading: boolean;
2222
error?: Error;
2323

24-
unseenCount: number;
24+
seenAt: number | undefined;
2525
setAsSeen: () => void;
2626

2727
areNotificationsEnabled: boolean;
@@ -31,6 +31,8 @@ export const NotificationsContext = createContext<
3131

3232
showNotification: (notification: Notification) => void;
3333
deleteNotification: (notificationId: string) => void;
34+
35+
showPushNotifications: (targetNotifications: Notification[]) => void;
3436
}
3537
| undefined
3638
>(undefined);
@@ -114,44 +116,40 @@ export default function NotificationsProvider(props: NotificationsProviderProps)
114116
return orderBy(list, ['timestamp'], ['desc']);
115117
}, [triggeredNotificationsByCurrentFingerprint, blockchainNotifications]);
116118

117-
const showPushNotifications = useCallback(() => {
118-
// if fingerprint is not set then we can't show push notifications (user is not logged in)
119-
if (!globalNotifications || !pushNotifications || isLoadingServices || !fingerprint) {
120-
return;
121-
}
119+
const showPushNotifications = useCallback(
120+
(targetNotifications: Notification[]) => {
121+
// if fingerprint is not set then we can't show push notifications (user is not logged in)
122+
if (!globalNotifications || !pushNotifications || isLoadingServices || !fingerprint) {
123+
return;
124+
}
122125

123-
setLastPushNotificationTimestamp((prevLastPushNotificationTimestamp = 0) => {
124-
const firstUnseenNotification = notifications.find(
125-
(notification) => notification.timestamp > prevLastPushNotificationTimestamp,
126-
);
126+
setLastPushNotificationTimestamp((prevLastPushNotificationTimestamp = 0) => {
127+
const firstUnseenNotification = targetNotifications.find(
128+
(notification) => notification.timestamp > prevLastPushNotificationTimestamp,
129+
);
127130

128-
if (!firstUnseenNotification) {
129-
return prevLastPushNotificationTimestamp;
130-
}
131+
if (!firstUnseenNotification) {
132+
return prevLastPushNotificationTimestamp;
133+
}
131134

132-
const { title, body } = pushNotificationStringsForNotificationType(firstUnseenNotification);
135+
const { title, body } = pushNotificationStringsForNotificationType(firstUnseenNotification);
133136

134-
showPushNotification({
135-
title,
136-
body,
137-
});
137+
showPushNotification({
138+
title,
139+
body,
140+
});
138141

139-
return firstUnseenNotification.timestamp;
140-
});
141-
}, [
142-
globalNotifications,
143-
pushNotifications,
144-
isLoadingServices,
145-
setLastPushNotificationTimestamp,
146-
notifications,
147-
showPushNotification,
148-
fingerprint,
149-
]);
150-
151-
const unseenCount = useMemo(
152-
() =>
153-
seenAt ? notifications.filter((notification) => notification.timestamp > seenAt).length : notifications.length,
154-
[seenAt, notifications],
142+
return firstUnseenNotification.timestamp;
143+
});
144+
},
145+
[
146+
globalNotifications,
147+
pushNotifications,
148+
isLoadingServices,
149+
setLastPushNotificationTimestamp,
150+
showPushNotification,
151+
fingerprint,
152+
],
155153
);
156154

157155
const setAsSeen = useCallback(() => {
@@ -183,19 +181,15 @@ export default function NotificationsProvider(props: NotificationsProviderProps)
183181
[setTriggeredNotifications, deleteNotification],
184182
);
185183

186-
useEffect(() => {
187-
showPushNotifications();
188-
}, [showPushNotifications]);
189-
190184
const contextValue = useMemo(
191185
() => ({
192186
// base state
193187
notifications,
194188
isLoading,
195189
error,
196190
// seen
197-
unseenCount,
198191
setAsSeen,
192+
seenAt,
199193
// settings
200194
areNotificationsEnabled: globalNotifications,
201195
setNotificationsEnabled: setGlobalNotifications,
@@ -204,14 +198,16 @@ export default function NotificationsProvider(props: NotificationsProviderProps)
204198

205199
showNotification,
206200
deleteNotification: handleDeleteNotification,
201+
202+
showPushNotifications,
207203
}),
208204
[
209205
notifications,
210206
isLoading,
211207
error,
212208

213-
unseenCount,
214209
setAsSeen,
210+
seenAt,
215211

216212
globalNotifications,
217213
setGlobalNotifications,
@@ -220,6 +216,8 @@ export default function NotificationsProvider(props: NotificationsProviderProps)
220216

221217
showNotification,
222218
handleDeleteNotification,
219+
220+
showPushNotifications,
223221
],
224222
);
225223

packages/gui/src/electron/main.tsx

+12-10
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import {
1414
import fs from 'fs';
1515
import path from 'path';
1616
import url from 'url';
17-
import sanitizeFilename from 'sanitize-filename';
1817

1918
import { initialize, enable } from '@electron/remote/main';
2019
import axios from 'axios';
2120
import windowStateKeeper from 'electron-window-state';
2221
import React from 'react';
2322
// import os from 'os';
2423
import ReactDOMServer from 'react-dom/server';
24+
import sanitizeFilename from 'sanitize-filename';
2525
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
2626
import isURL from 'validator/es/lib/isURL';
2727

@@ -32,7 +32,6 @@ import AppIcon from '../assets/img/chia64x64.png';
3232
import About from '../components/about/About';
3333
import { i18n } from '../config/locales';
3434
import chiaEnvironment, { chiaInit } from '../util/chiaEnvironment';
35-
import downloadFile from './utils/downloadFile';
3635
import loadConfig, { checkConfigFileExists } from '../util/loadConfig';
3736
import manageDaemonLifetime from '../util/manageDaemonLifetime';
3837
import { setUserDataDir } from '../util/userData';
@@ -41,6 +40,7 @@ import CacheManager from './CacheManager';
4140
import { readAddressBook, saveAddressBook } from './addressBook';
4241
import installDevTools from './installDevTools.dev';
4342
import { readPrefs, savePrefs, migratePrefs } from './prefs';
43+
import downloadFile from './utils/downloadFile';
4444

4545
/**
4646
* Open the given external protocol URL in the desktop's default manner.
@@ -330,6 +330,15 @@ if (ensureSingleInstance() && ensureCorrectEnvironment()) {
330330

331331
const { folder, tasks } = options;
332332

333+
const handleDownloadProgress = (progress: any, downloadUrl: string, index: number, total: number) => {
334+
mainWindow?.webContents.send('multipleDownloadProgress', {
335+
progress,
336+
url: downloadUrl,
337+
index,
338+
total,
339+
});
340+
};
341+
333342
for (let i = 0; i < tasks.length; i++) {
334343
const { url: downloadUrl, filename } = tasks[i];
335344

@@ -344,14 +353,7 @@ if (ensureSingleInstance() && ensureCorrectEnvironment()) {
344353
const filePath = path.join(folder, sanitizedFilename);
345354

346355
await downloadFile(downloadUrl, filePath, {
347-
onProgress: (progress) => {
348-
mainWindow?.webContents.send('multipleDownloadProgress', {
349-
progress,
350-
url: downloadUrl,
351-
index: i,
352-
total: tasks.length,
353-
});
354-
},
356+
onProgress: (progress) => handleDownloadProgress(progress, downloadUrl, i, tasks.length),
355357
});
356358

357359
const fileStats = await fs.promises.stat(filePath);

packages/gui/src/electron/utils/downloadFile.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { promises as fs, createWriteStream, type WriteStream } from 'fs';
44
import debug from 'debug';
55

66
import type Headers from '../../@types/Headers';
7+
78
import fileExists from './fileExists';
89

910
const log = debug('chia-gui:downloadFile');

packages/gui/src/hooks/useBlockchainNotifications.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export default function useBlockchainNotifications() {
7676
blockchainNotificationsList.map(async (notification): Promise<Notification | null> => {
7777
try {
7878
const { id, message: hexMessage, height } = notification;
79-
const message = hexMessage ? Buffer.from(hexMessage, 'hex').toString() : '';
79+
const message = hexMessage ? Buffer.from(hexMessage.replace(/^0x/, ''), 'hex').toString() : '';
8080
if (!message) {
8181
throw new Error('Notification has not message');
8282
}

packages/gui/src/hooks/useValidNotifications.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from 'react';
1+
import { useEffect, useState, useMemo } from 'react';
22

33
import type Notification from '../@types/Notification';
44
import type OfferState from '../@types/OfferState';
@@ -32,11 +32,16 @@ function filterNotifications(notifications: Notification[], getOffer: (offerId:
3232
}
3333

3434
export default function useValidNotifications() {
35-
const { notifications, ...rest } = useNotifications();
35+
const { notifications, seenAt, showPushNotifications, ...rest } = useNotifications();
3636
const { getOffer, subscribeToChanges } = useOffers();
3737

3838
const [validNotifications, setValidNotifications] = useState(() => filterNotifications(notifications, getOffer));
3939

40+
const unseenCount = useMemo(
41+
() => (seenAt ? validNotifications.filter((n) => n.timestamp > seenAt).length : validNotifications.length),
42+
[seenAt, validNotifications],
43+
);
44+
4045
useEffect(() => {
4146
setValidNotifications(filterNotifications(notifications, getOffer));
4247
}, [notifications, getOffer]);
@@ -58,8 +63,13 @@ export default function useValidNotifications() {
5863
};
5964
}, [getOffer, notifications, subscribeToChanges]);
6065

66+
useEffect(() => {
67+
showPushNotifications(validNotifications);
68+
}, [showPushNotifications, validNotifications]);
69+
6170
return {
6271
notifications: validNotifications,
72+
unseenCount,
6373
...rest,
6474
};
6575
}

packages/wallets/src/components/WalletHistory.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,9 @@ export default function WalletHistory(props: Props) {
385385

386386
const memoValues = memos ? Object.values(memos) : [];
387387

388-
const memoValuesDecoded = memoValues.map((memoHex) => {
388+
const memoValuesDecoded = memoValues.map((mv) => {
389389
try {
390+
const memoHex = (mv as string).replace(/^0x/, '');
390391
const buf = Buffer.from(memoHex, 'hex');
391392
const decodedValue = buf.toString('utf8');
392393
const bufCheck = Buffer.from(decodedValue, 'utf8');
@@ -396,7 +397,7 @@ export default function WalletHistory(props: Props) {
396397

397398
return decodedValue;
398399
} catch (error: any) {
399-
return memoHex;
400+
return mv;
400401
}
401402
});
402403

0 commit comments

Comments
 (0)