diff --git a/package.json b/package.json index ee43118b5..4fe8e6f41 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,6 @@ "emoji-mart": "^5.5.2", "filesize": "3.6.1", "firstline": "1.2.1", - "fs": "^0.0.1-security", "fs-extra": "9.0.0", "glob": "7.1.2", "image-type": "^4.1.0", @@ -100,7 +99,7 @@ "linkify-it": "^4.0.1", "lodash": "^4.17.21", "long": "^4.0.0", - "maxmind": "^4.3.6", + "maxmind": "^4.3.18", "mic-recorder-to-mp3": "^2.2.2", "moment": "^2.29.4", "node-fetch": "^2.6.7", diff --git a/ts/components/dialog/OnionStatusPathDialog.tsx b/ts/components/dialog/OnionStatusPathDialog.tsx index 73a207d9f..4fcf42f78 100644 --- a/ts/components/dialog/OnionStatusPathDialog.tsx +++ b/ts/components/dialog/OnionStatusPathDialog.tsx @@ -1,9 +1,7 @@ -import { shell } from 'electron'; -import { readFileSync } from 'fs'; -import path from 'path'; -import React from 'react'; +import { ipcRenderer, shell } from 'electron'; +import React, { useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import useHover from 'react-use/lib/useHover'; import styled from 'styled-components'; @@ -11,16 +9,18 @@ import { CityResponse, Reader } from 'maxmind'; import { Snode } from '../../data/data'; import { onionPathModal } from '../../state/ducks/modalDialog'; import { - getFirstOnionPath, - getFirstOnionPathLength, - getIsOnline, - getOnionPathsCount, + useFirstOnionPath, + useFirstOnionPathLength, + useIsOnline, + useOnionPathsCount, } from '../../state/selectors/onions'; import { Flex } from '../basic/Flex'; +import { isEmpty, isTypedArray } from 'lodash'; +import { useMount } from 'react-use'; +import { SessionWrapperModal } from '../SessionWrapperModal'; import { SessionSpinner } from '../basic/SessionSpinner'; import { SessionIcon, SessionIconButton } from '../icon'; -import { SessionWrapperModal } from '../SessionWrapperModal'; export type StatusLightType = { glowStartDelay: number; @@ -77,11 +77,28 @@ const OnionCountryDisplay = ({ labelText, snodeIp }: { snodeIp?: string; labelTe return hoverable; }; +let reader: Reader | null; +const lang = 'en'; + const OnionPathModalInner = () => { - const onionPath = useSelector(getFirstOnionPath); - const isOnline = useSelector(getIsOnline); + const onionPath = useFirstOnionPath(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_dataLoaded, setDataLoaded] = useState(false); + const isOnline = useIsOnline(); + const glowDuration = onionPath.length + 2; + useMount(() => { + ipcRenderer.once('load-maxmind-data-reply', (_event, content) => { + const asArrayBuffer = content as Uint8Array; + if (asArrayBuffer && isTypedArray(asArrayBuffer) && !isEmpty(asArrayBuffer)) { + reader = new Reader(Buffer.from(asArrayBuffer.buffer)); + setDataLoaded(true); // retrigger a rerender + } + }); + ipcRenderer.send('load-maxmind-data'); + }); + if (!isOnline || !onionPath || onionPath.length === 0) { return ; } @@ -96,13 +113,6 @@ const OnionPathModalInner = () => { }, ]; - const binPath = (process.env.NODE_APP_INSTANCE || '').startsWith('devprod') - ? path.resolve(`${__dirname}/../../..`) - : path.resolve(`${process.resourcesPath}/..`); - const buffer = readFileSync(`${binPath}/mmdb/GeoLite2-Country.mmdb`); - const reader = new Reader(buffer); - const lang = 'en'; - return ( <> @@ -126,13 +136,13 @@ const OnionPathModalInner = () => { {nodes.map((snode: Snode | any) => { - const geoLookup = reader.get(snode.ip || '0.0.0.0'); - const countryName = geoLookup?.country?.names[lang] || window.i18n('unknownCountry'); - const labelText = snode.label || countryName; + const countryName = + reader?.get(snode.ip || '0.0.0.0')?.country?.names[lang] || + window.i18n('unknownCountry'); return ( @@ -197,9 +207,9 @@ export const ActionPanelOnionStatusLight = (props: { }) => { const { isSelected, handleClick, id } = props; - const onionPathsCount = useSelector(getOnionPathsCount); - const firstPathLength = useSelector(getFirstOnionPathLength); - const isOnline = useSelector(getIsOnline); + const onionPathsCount = useOnionPathsCount(); + const firstPathLength = useFirstOnionPathLength(); + const isOnline = useIsOnline(); // Set icon color based on result const errorColor = 'var(--button-path-error-color)'; diff --git a/ts/mains/main_node.ts b/ts/mains/main_node.ts index 8d37b28e5..fecd495bb 100644 --- a/ts/mains/main_node.ts +++ b/ts/mains/main_node.ts @@ -10,6 +10,7 @@ import { dialog, protocol as electronProtocol, ipcMain as ipc, + IpcMainEvent, Menu, nativeTheme, screen, @@ -154,7 +155,7 @@ if (windowFromUserConfig) { ephemeralConfig.set('window', windowConfig); } -// import {load as loadLocale} from '../..' +import { readFile } from 'fs-extra'; import { getAppRootPath } from '../node/getRootPath'; import { setLastestRelease } from '../node/latest_desktop_release'; import { load as loadLocale, LocaleMessagesWithNameType } from '../node/locale'; @@ -1074,6 +1075,16 @@ ipc.on('close-debug-log', () => { } }); ipc.on('save-debug-log', saveDebugLog); +ipc.on('load-maxmind-data', async (event: IpcMainEvent) => { + try { + const appRoot = app.isPackaged ? process.resourcesPath : app.getAppPath(); + const fileToRead = path.join(appRoot, 'mmdb', 'GeoLite2-Country.mmdb'); + const buffer = await readFile(fileToRead); + event.reply('load-maxmind-data-reply', new Uint8Array(buffer.buffer)); + } catch (e) { + event.reply('load-maxmind-data-reply', null); + } +}); // This should be called with an ipc sendSync ipc.on('get-media-permissions', event => { diff --git a/ts/state/selectors/onions.ts b/ts/state/selectors/onions.ts index 84764663e..4156f6a66 100644 --- a/ts/state/selectors/onions.ts +++ b/ts/state/selectors/onions.ts @@ -1,27 +1,41 @@ import { createSelector } from '@reduxjs/toolkit'; -import { StateType } from '../reducer'; +import { useSelector } from 'react-redux'; import { OnionState } from '../ducks/onion'; import { SectionType } from '../ducks/section'; +import { StateType } from '../reducer'; -export const getOnionPaths = (state: StateType): OnionState => state.onionPaths; +const getOnionPaths = (state: StateType): OnionState => state.onionPaths; -export const getOnionPathsCount = createSelector( +const getOnionPathsCount = createSelector( getOnionPaths, (state: OnionState): SectionType => state.snodePaths.length ); -export const getFirstOnionPath = createSelector( +const getFirstOnionPath = createSelector( getOnionPaths, (state: OnionState): Array<{ ip: string }> => state.snodePaths?.[0] || [] ); -export const getFirstOnionPathLength = createSelector( +const getFirstOnionPathLength = createSelector( getFirstOnionPath, (state: Array<{ ip: string }>): number => state.length || 0 ); -export const getIsOnline = createSelector( - getOnionPaths, - (state: OnionState): boolean => state.isOnline -); +const getIsOnline = createSelector(getOnionPaths, (state: OnionState): boolean => state.isOnline); + +export const useOnionPathsCount = () => { + return useSelector(getOnionPathsCount); +}; + +export const useIsOnline = () => { + return useSelector(getIsOnline); +}; + +export const useFirstOnionPathLength = () => { + return useSelector(getFirstOnionPathLength); +}; + +export const useFirstOnionPath = () => { + return useSelector(getFirstOnionPath); +}; diff --git a/yarn.lock b/yarn.lock index d3527d212..3d97f8ba5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3867,11 +3867,6 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fs@^0.0.1-security: - version "0.0.1-security" - resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" - integrity sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w== - fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -6882,7 +6877,7 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==