You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
5.2 KiB
TypeScript
189 lines
5.2 KiB
TypeScript
import React from 'react';
|
|
|
|
import _ from 'lodash';
|
|
|
|
import { getTheme } from '../state/selectors/theme';
|
|
|
|
import electron from 'electron';
|
|
import { useSelector } from 'react-redux';
|
|
import { StateType } from '../state/reducer';
|
|
import { SessionIcon, SessionIconButton, SessionIconSize, SessionIconType } from './session/icon';
|
|
const { shell } = electron;
|
|
|
|
import { SessionWrapperModal } from '../components/session/SessionWrapperModal';
|
|
import { Snode } from '../session/onions';
|
|
|
|
import ip2country from 'ip2country';
|
|
import countryLookup from 'country-code-lookup';
|
|
import { useTheme } from 'styled-components';
|
|
import { useNetwork } from '../hooks/useNetwork';
|
|
|
|
export type OnionPathModalType = {
|
|
onConfirm?: () => void;
|
|
onClose?: () => void;
|
|
confirmText?: string;
|
|
cancelText?: string;
|
|
title?: string;
|
|
};
|
|
|
|
export type StatusLightType = {
|
|
glowStartDelay: number;
|
|
glowDuration: number;
|
|
color?: string;
|
|
};
|
|
|
|
const OnionPathModalInner = (props: any) => {
|
|
const onionNodes = useSelector((state: StateType) => state.onionPaths.snodePath);
|
|
const confirmModalState = useSelector((state: StateType) => state);
|
|
console.log('onion path: ', confirmModalState);
|
|
const onionPath = onionNodes.path;
|
|
// including the device and destination in calculation
|
|
const glowDuration = onionPath.length + 2;
|
|
|
|
const nodes = [
|
|
{
|
|
label: window.i18n('device'),
|
|
},
|
|
...onionNodes.path,
|
|
,
|
|
{
|
|
label: window.i18n('destination'),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<>
|
|
<p className="onion__description">{window.i18n('onionPathIndicatorDescription')}</p>
|
|
<div className="onion__node-list">
|
|
{nodes.map((snode: Snode | any, index: number) => {
|
|
return (
|
|
<OnionNodeStatusLight
|
|
glowDuration={glowDuration}
|
|
glowStartDelay={index}
|
|
label={snode.label}
|
|
snode={snode}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export type OnionNodeStatusLightType = {
|
|
snode: Snode;
|
|
label?: string;
|
|
glowStartDelay: number;
|
|
glowDuration: number;
|
|
};
|
|
|
|
/**
|
|
* Component containing a coloured status light and an adjacent country label.
|
|
* @param props
|
|
* @returns
|
|
*/
|
|
export const OnionNodeStatusLight = (props: OnionNodeStatusLightType): JSX.Element => {
|
|
const { snode, label, glowStartDelay, glowDuration } = props;
|
|
const theme = useTheme();
|
|
|
|
let labelText = label ? label : countryLookup.byIso(ip2country(snode.ip))?.country;
|
|
if (!labelText) {
|
|
labelText = window.i18n('unknownCountry');
|
|
}
|
|
return (
|
|
<div className="onion__node">
|
|
<ModalStatusLight
|
|
glowDuration={glowDuration}
|
|
glowStartDelay={glowStartDelay}
|
|
color={theme.colors.accent}
|
|
></ModalStatusLight>
|
|
{labelText ? (
|
|
<>
|
|
<div className="onion-node__country">{labelText}</div>
|
|
</>
|
|
) : null}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* An icon with a pulsating glow emission.
|
|
*/
|
|
export const ModalStatusLight = (props: StatusLightType) => {
|
|
const { glowStartDelay, glowDuration, color } = props;
|
|
const theme = useSelector(getTheme);
|
|
|
|
return (
|
|
<SessionIcon
|
|
borderRadius={50}
|
|
iconColor={color}
|
|
glowDuration={glowDuration}
|
|
glowStartDelay={glowStartDelay}
|
|
iconType={SessionIconType.Circle}
|
|
iconSize={SessionIconSize.Medium}
|
|
theme={theme}
|
|
/>
|
|
);
|
|
};
|
|
|
|
|
|
/**
|
|
* A status light specifically for the action panel. Color is based on aggregate node states instead of individual onion node state
|
|
*/
|
|
export const ActionPanelOnionStatusLight = (props: { isSelected: boolean, handleClick: () => void }) => {
|
|
const { isSelected, handleClick } = props;
|
|
|
|
let iconColor;
|
|
const theme = useTheme();
|
|
const firstOnionPath = useSelector((state: StateType) => state.onionPaths.snodePath.path);
|
|
const hasOnionPath = firstOnionPath.length > 2;
|
|
|
|
// Set icon color based on result
|
|
const red = theme.colors.destructive;
|
|
const green = theme.colors.accent;
|
|
const orange = theme.colors.warning;
|
|
|
|
|
|
iconColor = hasOnionPath ? theme.colors.accent : theme.colors.destructive;
|
|
const onionState = useSelector((state: StateType) => state.onionPaths);
|
|
|
|
iconColor = red;
|
|
const isOnline = useNetwork();
|
|
if (!(onionState && onionState.snodePath) || !isOnline) {
|
|
iconColor = red;
|
|
} else {
|
|
const onionSnodePath = onionState.snodePath;
|
|
if (onionState && onionSnodePath && onionSnodePath.path.length > 0) {
|
|
let onionNodeCount = onionSnodePath.path.length;
|
|
iconColor = onionNodeCount > 2 ? green : onionNodeCount > 1 ? orange : red;
|
|
}
|
|
}
|
|
|
|
return <SessionIconButton
|
|
iconSize={SessionIconSize.Medium}
|
|
iconType={SessionIconType.Circle}
|
|
iconColor={iconColor}
|
|
onClick={handleClick}
|
|
isSelected={isSelected}
|
|
theme={theme}
|
|
/>
|
|
}
|
|
|
|
export const OnionPathModal = (props: OnionPathModalType) => {
|
|
const onConfirm = () => {
|
|
shell.openExternal('https://getsession.org/faq/#onion-routing');
|
|
};
|
|
return (
|
|
<SessionWrapperModal
|
|
title={props.title || window.i18n('onionPathIndicatorTitle')}
|
|
confirmText={props.confirmText || window.i18n('learnMore')}
|
|
cancelText={props.cancelText || window.i18n('cancel')}
|
|
onConfirm={onConfirm}
|
|
onClose={props.onClose}
|
|
showExitIcon={true}
|
|
>
|
|
<OnionPathModalInner {...props}></OnionPathModalInner>
|
|
</SessionWrapperModal>
|
|
);
|
|
};
|