WIP Adding country and styling to modals.

This commit is contained in:
Warrick Corfe-Tan 2021-05-25 11:43:43 +10:00
parent d7b22e13e1
commit 22a32283b7
8 changed files with 250 additions and 270 deletions

View file

@ -61,6 +61,7 @@
"classnames": "2.2.5",
"color": "^3.1.2",
"config": "1.28.1",
"country-code-lookup": "^0.0.19",
"cross-env": "^6.0.3",
"dompurify": "^2.0.7",
"electron-is-dev": "^1.1.0",
@ -74,6 +75,7 @@
"fs-extra": "9.0.0",
"glob": "7.1.2",
"he": "1.2.0",
"ip2country": "^1.0.0",
"jquery": "3.3.1",
"jsbn": "1.1.0",
"libsodium-wrappers": "^0.7.8",

View file

@ -1200,19 +1200,11 @@ input {
// .session-onion-path-wrapper {
.onion-node-list {
.onion__node-list {
display: flex;
flex-direction: column;
margin: $session-margin-sm;
// .onion-node__vertical-line {
// border-left: 3px solid green;
// left: 50%;
// position: absolute;
// margin-left: -3px;
// top: 0;
// height: 100%
// }
align-items: flex-start;
.onion__node {
display: flex;
@ -1220,21 +1212,24 @@ input {
align-items: center;
margin: $session-margin-xs;
// unsure
position: relative;
.line {
width: 50%;
height: 100%;
position: absolute;
left: 25%;
top: 40%;
border-left: 3px solid green;
}
* {
margin: $session-margin-sm;
}
svg {
animation: glow 1s ease-in-out infinite alternate;
}
}
}
@keyframes glow {
from {
-webkit-filter: drop-shadow( 0px 0px 3px rgba(0,0,0,0));
filter: drop-shadow( 0px 0px 3px rgba(0,0,0,0));
}
to {
-webkit-filter: drop-shadow( 0px 0px 5px rgba(46, 247, 28, 0.7));
filter: drop-shadow( 0px 0px 5px rgba(68, 236, 2, 0.7));
}
}

View file

@ -13,206 +13,176 @@ import { getTheme } from '../state/selectors/theme';
import electron from 'electron';
import { useSelector } from 'react-redux';
import { StateType } from '../state/reducer';
import { OnionPathNodeType } from '../state/ducks/onion';
import { SessionIconButton, SessionIconSize, SessionIconType } from './session/icon';
import { Constants } from '../session';
const { shell } = electron;
import { SessionWrapperModal } from '../components/session/SessionWrapperModal';
import { Snode } from '../session/onions';
interface OnionStatusDialogProps {
theme: DefaultTheme;
nodes?: Array<string>;
onClose: any;
}
import ip2country from "ip2country";
import countryLookup from "country-code-lookup";
export interface IPathNode {
ip?: string;
label: string;
}
// import ipLocation = require('ip-location');
export const OnionPath = (props: { nodes: IPathNode[]; hasPath: boolean }) => {
const { nodes, hasPath } = props;
return (
<div className="onionPath">
{nodes.map(node => {
// return OnionPathNode(hasPath, node)
return OnionPathNode({ hasPath, node });
})}
</div>
);
};
// interface OnionStatusDialogProps {
// theme: DefaultTheme;
// nodes?: Array<string>;
// onClose: any;
// }
export const OnionStatusDialog = (props: OnionStatusDialogProps) => {
const { theme, onClose } = props;
// export interface IPathNode {
// ip?: string;
// label: string;
// }
const [onionPathAddresses, setOnionPathAddresses] = useState<string[]>([]);
const [pathNodes, setPathNodes] = useState<IPathNode[]>([]);
const [hasPath, setHasPath] = useState<boolean>(false);
// export const OnionPath = (props: { nodes: IPathNode[]; hasPath: boolean }) => {
// const { nodes, hasPath } = props;
const getOnionPathAddresses = () => {
const onionPathAddresses = getPathNodesIPAddresses();
console.log('Current Onion Path - ', onionPathAddresses);
setOnionPathAddresses(onionPathAddresses);
};
// return (
// <div className="onionPath">
// {nodes.map(node => {
// return OnionPathNode({ hasPath, node });
// })}
// </div>
// );
// };
const buildOnionPath = () => {
// TODO: Add i18n to onion path
// Default path values
let path = [
{
label: 'You',
},
{
ip: 'Connecting...',
label: 'Entry Node',
},
{
ip: 'Connecting...',
label: 'Service Node',
},
{
ip: 'Connecting...',
label: 'Service Node',
},
{
label: 'Destination',
},
];
// export const OnionStatusDialog = (props: OnionStatusDialogProps) => {
// const { theme, onClose } = props;
// FIXME call function to check if an onion path exists
setHasPath(onionPathAddresses.length !== 0);
// const [onionPathAddresses, setOnionPathAddresses] = useState<string[]>([]);
// const [pathNodes, setPathNodes] = useState<IPathNode[]>([]);
// const [hasPath, setHasPath] = useState<boolean>(false);
// if there is a onion path, update the addresses
if (onionPathAddresses.length) {
onionPathAddresses.forEach((ipAddress, index) => {
const pathIndex = index + 1;
path[pathIndex].ip = ipAddress;
});
}
setPathNodes(path);
};
// const getOnionPathAddresses = () => {
// const onionPathAddresses = getPathNodesIPAddresses();
// console.log('Current Onion Path - ', onionPathAddresses);
// setOnionPathAddresses(onionPathAddresses);
// };
useInterval(() => {
getOnionPathAddresses();
}, 1000);
// const buildOnionPath = () => {
// // TODO: Add i18n to onion path
// // Default path values
// let path = [
// {
// label: 'You',
// },
// {
// ip: 'Connecting...',
// label: 'Entry Node',
// },
// {
// ip: 'Connecting...',
// label: 'Service Node',
// },
// {
// ip: 'Connecting...',
// label: 'Service Node',
// },
// {
// label: 'Destination',
// },
// ];
useEffect(() => {
buildOnionPath();
}, [onionPathAddresses]);
// // FIXME call function to check if an onion path exists
// setHasPath(onionPathAddresses.length !== 0);
const openFAQPage = () => {
console.log('Opening FAQ Page');
shell.openExternal('https://getsession.org/faq/#onion-routing');
};
// // if there is a onion path, update the addresses
// if (onionPathAddresses.length) {
// onionPathAddresses.forEach((ipAddress, index) => {
// const pathIndex = index + 1;
// path[pathIndex].ip = ipAddress;
// });
// }
// setPathNodes(path);
// };
return (
<SessionModal title={window.i18n('onionPathIndicatorTitle')} theme={theme} onClose={onClose}>
<div className="spacer-sm" />
<div className="onionDescriptionContainer">
<p>{window.i18n('onionPathIndicatorDescription')}</p>
</div>
// useInterval(() => {
// getOnionPathAddresses();
// }, 1000);
<OnionPath nodes={pathNodes} hasPath={hasPath} />
// useEffect(() => {
// buildOnionPath();
// }, [onionPathAddresses]);
<SessionButton
text={window.i18n('learnMore')}
buttonType={SessionButtonType.BrandOutline}
buttonColor={SessionButtonColor.Green}
onClick={openFAQPage}
/>
</SessionModal>
);
};
// const openFAQPage = () => {
// console.log('Opening FAQ Page');
// shell.openExternal('https://getsession.org/faq/#onion-routing');
// };
// export const OnionPathNode = (hasPath: boolean, node: IPathNode): JSX.Element => {
// export const OnionPathNode = (hasPath: boolean, node: any): JSX.Element => {
// export const OnionPathNode = (hasPath: boolean, node: any) => {
// export const OnionPathNode = (hasPath: any, node: any) => {
export const OnionPathNode = (props: any) => {
const { hasPath, node } = props;
// return (
// <SessionModal title={window.i18n('onionPathIndicatorTitle')} theme={theme} onClose={onClose}>
// <div className="spacer-sm" />
// <div className="onionDescriptionContainer">
// <p>{window.i18n('onionPathIndicatorDescription')}</p>
// </div>
const theme = useSelector(getTheme);
console.log('@@@ onionpathnode theme', theme);
// <OnionPath nodes={pathNodes} hasPath={hasPath} />
const onionPaths = useSelector((state: StateType) => state.onionPaths);
console.log('@@@ state onion path node', onionPaths);
// if (!(node && node.label && node.ip)) {
// return <div>{'Onion' + JSON.stringify(onionPaths)}</div>;
// }
// let connectedNodesCounts = onionPaths.nodes.reduce()
let connectedNodesCount = _.sumBy(onionPaths.nodes, (node: OnionPathNodeType) => {
return node.isConnected ? 1 : 0;
});
if (true) {
console.log('@@@', connectedNodesCount);
return (
// <SessionIconButton
// />
<div className="idk"></div>
);
}
return (
<div className="dotContainer">
<div className={classNames('dot', hasPath ? 'green' : 'red')}></div>
<p>
{node && node.label ? <b>{node.label}</b> : null}
{node.ip && (
<>
<br />
{node.ip}
</>
)}
</p>
</div>
);
};
// <SessionButton
// text={window.i18n('learnMore')}
// buttonType={SessionButtonType.BrandOutline}
// buttonColor={SessionButtonColor.Green}
// onClick={openFAQPage}
// />
// </SessionModal>
// );
// };
const OnionPathModalInner = (props: any) => {
const onionPaths = useSelector((state: StateType) => state.onionPaths);
// let connectedNodesCount = _.sumBy(onionPaths.nodes, (node: OnionPathNodeType) => {
// return node.isConnected ? 1 : 0;
// })
const onionPath = useSelector((state: StateType) => state.onionPaths.snodePath);
return (
<div className="onion-node-list">
{/* <div className="onion-node__vertical-line"></div> */}
{onionPaths.nodes.map((node: OnionPathNodeType, index: number) => {
let nodeStatusColor = node.isConnected
? Constants.UI.COLORS.GREEN
: node.isAttemptingConnect
? Constants.UI.COLORS.WARNING
: Constants.UI.COLORS.DANGER;
<div className="onion__node-list">
{onionPath.path.map((snode: Snode, index: number) => {
return (
<>
<div className="onion__node">
{index <= onionPaths.nodes.map.length ?
<div className="line"></div>
:
null
}
<StatusLight color={nodeStatusColor}></StatusLight>
{node.ip ?
<div className="onion-node__country">country</div>
:
null
}
</div>
<LabelledStatusLight snode={snode} ></LabelledStatusLight>
</>
);
})}
{/* TODO: Destination node maybe pass in light colour maybe changes based on if 3 nodes are connected similar to the action panel light? */}
<LabelledStatusLight label={'Destination'}></LabelledStatusLight>
</div>
);
};
/**
* Component containing a coloured status light and an adjacent country label.
* @param props
* @returns
*/
export const LabelledStatusLight = (props: any): JSX.Element => {
let { snode, label } = props;
let labelText = label ? label : countryLookup.byIso(ip2country(snode.ip))?.country;
console.log('@@@@ country data:: ', labelText);
if (!labelText) {
labelText = `${snode.ip} - Destination unknown`;
console.log(`@@@@ country data failure on code:: ${ip2country(snode.ip)} and ip ${snode.ip}`);
}
return (
<div className="onion__node">
<StatusLight color={Constants.UI.COLORS.GREEN}></StatusLight>
{labelText ?
<>
<div className="onion-node__country">{labelText}</div>
</>
:
null
}
</div>
)
}
export const OnionNodeLight = (props: any) => {
}
export const StatusLight = (props: any) => {
const [showModal, toggleShowModal] = useState(false);
const { isSelected, color } = props;

View file

@ -13,7 +13,7 @@ import {
hasSyncedInitialConfigurationItem,
removeItemById,
} from '../../data/data';
import { OnionPaths } from '../../session/onions';
import { OnionPaths, Snode, SnodePath } from '../../session/onions';
import { getMessageQueue } from '../../session/sending';
import { clearSessionsAndPreKeys } from '../../util/accountManager';
import { useDispatch, useSelector } from 'react-redux';
@ -36,11 +36,9 @@ import { forceRefreshRandomSnodePool } from '../../session/snode_api/snodePool';
import { SwarmPolling } from '../../session/snode_api/swarmPolling';
import { getOnionPathStatus } from '../../session/onions/onionSend';
import { Constants } from '../../session';
import { IPathNode, OnionPathNode, StatusLight } from '../OnionStatusDialog';
import { OnionUpdate, updateOnionPaths, OnionPathNodeType } from '../../state/ducks/onion';
import { StatusLight } from '../OnionStatusDialog';
import { StateType } from '../../state/reducer';
import _ from 'lodash';
import { constants } from 'original-fs';
// tslint:disable-next-line: no-import-side-effect no-submodule-imports
@ -132,23 +130,28 @@ const Section = (props: { type: SectionType; avatarPath?: string; hasOnionPath?:
// calculate light status.
// TODO: Refactor this so this logic is reusable elsewhere.
// TEST:
if (type === SectionType.PathIndicator) {
const onionPaths = useSelector((state: StateType) => state.onionPaths);
console.log('@@@ state onion path node', onionPaths);
let connectedNodesCount = _.sumBy(onionPaths.nodes, (node: OnionPathNodeType) => {
return node.isConnected ? 1 : 0;
});
const onionState = useSelector((state: StateType) => state.onionPaths);
console.log('@@@@@ is connected count: ', connectedNodesCount);
let statusColor = Constants.UI.COLORS.DANGER;
if (!(onionState && onionState.snodePath)) {
return <StatusLight isSelected={isSelected} color={Constants.UI.COLORS.DANGER}></StatusLight>;
} else {
const statusColor =
connectedNodesCount > 2
? Constants.UI.COLORS.GREEN
: connectedNodesCount > 1
? Constants.UI.COLORS.WARNING
: Constants.UI.COLORS.DANGER;
console.log('@@@@@ is connected color: ', statusColor);
const onionSnodePath = onionState.snodePath;
if (onionState && onionSnodePath && onionSnodePath.path.length > 0) {
let onionNodeCount = onionSnodePath.path.length;
statusColor =
onionNodeCount > 2
? Constants.UI.COLORS.GREEN
: onionNodeCount > 1
? Constants.UI.COLORS.WARNING
: Constants.UI.COLORS.DANGER;
}
}
return <StatusLight isSelected={isSelected} color={statusColor}></StatusLight>;
}
@ -256,30 +259,39 @@ export const ActionsPanel = () => {
const getOnionPathIndicator = () => {
const hasOnionPath = getOnionPathStatus();
const update: OnionUpdate = {
nodes: [
{
ip: 'hi',
label: 'hi',
isConnected: Math.random() > 0.5,
isAttemptingConnect: Math.random() > 0.7,
},
{
ip: 'hi2',
label: 'hi2',
isConnected: Math.random() > 0.5,
isAttemptingConnect: Math.random() > 0.7,
},
{
ip: 'hi3',
label: 'hi3',
isConnected: Math.random() > 0.5,
isAttemptingConnect: Math.random() > 0.7,
},
],
};
// const update: OnionUpdate = {
// nodes: [
// {
// ip: 'hi',
// label: 'hi',
// isConnected: Math.random() > 0.5,
// isAttemptingConnect: Math.random() > 0.7,
// },
// {
// ip: 'hi2',
// label: 'hi2',
// isConnected: Math.random() > 0.5,
// isAttemptingConnect: Math.random() > 0.7,
// },
// {
// ip: 'hi3',
// label: 'hi3',
// isConnected: Math.random() > 0.5,
// isAttemptingConnect: Math.random() > 0.7,
// },
// ],
// };
dispatch(updateOnionPaths(update));
// dispatch(updateOnionPaths(update));
// TEST: Stuff
// let testNode: SnodePath = {
// bad: false,
// path: new Array<Snode>()
// }
// dispatch(updateOnionPaths(testNode));
console.log('Is Onion Path found -', hasOnionPath);
setHasOnionPath(hasOnionPath);

1
ts/ip2country.d.ts vendored Normal file
View file

@ -0,0 +1 @@
declare module 'ip2country';

View file

@ -6,11 +6,13 @@ import { UserUtils } from '../utils';
import { snodeHttpsAgent } from '../snode_api/onions';
import { allowOnlyOneAtATime } from '../utils/Promise';
import { updateOnionPaths } from '../../state/ducks/onion';
export type Snode = SnodePool.Snode;
const desiredGuardCount = 3;
const minimumGuardCount = 2;
interface SnodePath {
export interface SnodePath {
path: Array<Snode>;
bad: boolean;
}
@ -61,6 +63,8 @@ export class OnionPaths {
goodPaths = this.onionPaths.filter(x => !x.bad);
}
window.inboxStore?.dispatch(updateOnionPaths(goodPaths[0]));
const paths = _.shuffle(goodPaths);
if (!toExclude) {
@ -101,6 +105,8 @@ export class OnionPaths {
log.error('LokiSnodeAPI::getOnionPath - otherPaths no path in', otherPaths[0]);
}
// window.inboxStore?.dispatch(updateOnionPaths(otherPaths[0]));
return otherPaths[0].path;
}

View file

@ -1,51 +1,23 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
// import { OpenGroupV2InfoJoinable } from '../../opengroup/opengroupV2/ApiUtil';
// export type DefaultRoomsState = Array<OpenGroupV2InfoJoinable>;
import { SnodePath, Snode } from "../../session/onions/index";
export type OnionState = {
nodes: Array<OnionPathNodeType>;
// ip?: string;
// label?: string;
// isConnected?: boolean;
// isAttemptingConnect?: boolean;
};
const initialState: OnionState = {
nodes: new Array<OnionPathNodeType>(),
// nodes: Array<OnionPathNodeType>;
// path: SnodePath;
snodePath: SnodePath;
};
// const initialState: OnionState = {
// ip: '',
// label: '',
// isConnected: false,
// isAttemptingConnect: false
// // nodes: new Array<OnionPathNodeType>(),
// nodes: new Array<Snode>(),
// };
/**
* Payload to dispatch to update the base64 data of a default room
*/
export type Base64Update = {
base64Data: string;
roomId: string;
};
/**
* Type for a singular onion node to be used in the onion redux state.
*/
export type OnionPathNodeType = {
ip?: string;
label?: string;
isConnected?: boolean;
isAttemptingConnect?: boolean;
};
/**
* Payload to dispatch an update of the onion node paths
*/
export type OnionUpdate = {
nodes: Array<OnionPathNodeType>;
};
const initialState = {
snodePath: {
path: new Array<Snode>(),
bad: false
}
}
/**
* This slice is the one holding the default joinable rooms fetched once in a while from the default opengroup v2 server.
@ -54,9 +26,12 @@ const onionSlice = createSlice({
name: 'onionPaths',
initialState,
reducers: {
updateOnionPaths(state, action: PayloadAction<OnionUpdate>) {
window.log.warn('updating default rooms', action.payload);
return action.payload as OnionState;
// updateOnionPaths(state, action: PayloadAction<OnionUpdate>) {
updateOnionPaths(state, action: PayloadAction<SnodePath>) {
console.log('@@@@ dispatching:: ', action);
return {
snodePath: action.payload
}
},
},
});

View file

@ -1254,6 +1254,13 @@ asar@0.14.0:
mksnapshot "^0.3.0"
tmp "0.0.28"
asbycountry@^1.2.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/asbycountry/-/asbycountry-1.4.1.tgz#9fd71094edbd0d740d67b0cf75c9a6ecf47eea09"
integrity sha512-742TViobYfarc5bJd6l57/i2YURi7g87t6VgzShubyt2it1r0yWWRvezVPcGF4/fTuNnjizgX6dW4ReJhn8vMw==
dependencies:
chalk "^1.1.3"
asn1.js@^4.0.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
@ -2771,6 +2778,11 @@ cosmiconfig@^7.0.0:
path-type "^4.0.0"
yaml "^1.10.0"
country-code-lookup@^0.0.19:
version "0.0.19"
resolved "https://registry.yarnpkg.com/country-code-lookup/-/country-code-lookup-0.0.19.tgz#3fbf0192758ecf0d5eee0efbc220d62706c50fd6"
integrity sha512-lpvgdPyj8RuP0CSZhACNf5ueKlLbv/IQUAQfg7yr/qJbFrdcWV7Y+aDN9K/u/bx3MXRfcsjuW+TdIc0AEj7kDw==
crc32-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4"
@ -5707,6 +5719,13 @@ ip-regex@^1.0.1:
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd"
integrity sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0=
ip2country@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ip2country/-/ip2country-1.0.0.tgz#dc5b3ffb50862e7b452d43f6cce7e5ddc1b8951d"
integrity sha1-3Fs/+1CGLntFLUP2zOfl3cG4lR0=
dependencies:
asbycountry "^1.2.0"
ip@^1.1.0, ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"