Merge pull request #1744 from Bilb/use-retrieve-status-for-isOnline

use our retrieve status as isOnline status
pull/1747/head
Audric Ackermann 4 years ago committed by GitHub
commit a0811b699c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -450,7 +450,6 @@
}, window.CONSTANTS.NOTIFICATION_ENABLE_TIMEOUT_SECONDS * 1000); }, window.CONSTANTS.NOTIFICATION_ENABLE_TIMEOUT_SECONDS * 1000);
window.NewReceiver.queueAllCached(); window.NewReceiver.queueAllCached();
window.libsession.Utils.AttachmentDownloads.start({ window.libsession.Utils.AttachmentDownloads.start({
logger: window.log, logger: window.log,
}); });

@ -7,7 +7,6 @@ import { openConversationExternal } from '../state/ducks/conversations';
import { LeftPaneContactSection } from './session/LeftPaneContactSection'; import { LeftPaneContactSection } from './session/LeftPaneContactSection';
import { LeftPaneSettingSection } from './session/LeftPaneSettingSection'; import { LeftPaneSettingSection } from './session/LeftPaneSettingSection';
import { SessionTheme } from '../state/ducks/SessionTheme'; import { SessionTheme } from '../state/ducks/SessionTheme';
import { SessionOffline } from './session/network/SessionOffline';
import { SessionExpiredWarning } from './session/network/SessionExpiredWarning'; import { SessionExpiredWarning } from './session/network/SessionExpiredWarning';
import { getFocusedSection } from '../state/selectors/section'; import { getFocusedSection } from '../state/selectors/section';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
@ -44,7 +43,6 @@ const InnerLeftPaneMessageSection = (props: { isExpired: boolean }) => {
return ( return (
<> <>
<SessionOffline />
{props.isExpired && <SessionExpiredWarning />} {props.isExpired && <SessionExpiredWarning />}
<LeftPaneMessageSection <LeftPaneMessageSection
theme={theme} theme={theme}
@ -74,7 +72,6 @@ const InnerLeftPaneContactSection = () => {
return ( return (
<> <>
<SessionOffline />
<LeftPaneContactSection <LeftPaneContactSection
openConversationExternal={(id, messageId) => openConversationExternal={(id, messageId) =>
dispatch(openConversationExternal(id, messageId)) dispatch(openConversationExternal(id, messageId))

@ -20,11 +20,10 @@ import { onionPathModal } from '../state/ducks/modalDialog';
import { import {
getFirstOnionPath, getFirstOnionPath,
getFirstOnionPathLength, getFirstOnionPathLength,
getIsOnline,
getOnionPathsCount, getOnionPathsCount,
} from '../state/selectors/onions'; } from '../state/selectors/onions';
// tslint:disable-next-line: no-submodule-imports
import useNetworkState from 'react-use/lib/useNetworkState';
import { SessionSpinner } from './session/SessionSpinner'; import { SessionSpinner } from './session/SessionSpinner';
import { Flex } from './basic/Flex'; import { Flex } from './basic/Flex';
@ -36,9 +35,10 @@ export type StatusLightType = {
const OnionPathModalInner = () => { const OnionPathModalInner = () => {
const onionPath = useSelector(getFirstOnionPath); const onionPath = useSelector(getFirstOnionPath);
const isOnline = useSelector(getIsOnline);
// including the device and destination in calculation // including the device and destination in calculation
const glowDuration = onionPath.length + 2; const glowDuration = onionPath.length + 2;
if (!onionPath || onionPath.length === 0) { if (!isOnline || !onionPath || onionPath.length === 0) {
return <SessionSpinner loading={true} />; return <SessionSpinner loading={true} />;
} }
@ -144,7 +144,7 @@ export const ActionPanelOnionStatusLight = (props: {
const theme = useTheme(); const theme = useTheme();
const onionPathsCount = useSelector(getOnionPathsCount); const onionPathsCount = useSelector(getOnionPathsCount);
const firstPathLength = useSelector(getFirstOnionPathLength); const firstPathLength = useSelector(getFirstOnionPathLength);
const isOnline = useNetworkState().online; const isOnline = useSelector(getIsOnline);
// Set icon color based on result // Set icon color based on result
const red = theme.colors.destructive; const red = theme.colors.destructive;
@ -164,6 +164,9 @@ export const ActionPanelOnionStatusLight = (props: {
iconType={SessionIconType.Circle} iconType={SessionIconType.Circle}
iconColor={iconColor} iconColor={iconColor}
onClick={handleClick} onClick={handleClick}
glowDuration={10}
glowStartDelay={0}
noScale={true}
isSelected={isSelected} isSelected={isSelected}
theme={theme} theme={theme}
/> />

@ -12,6 +12,7 @@ export type SessionIconProps = {
glowDuration?: number; glowDuration?: number;
borderRadius?: number; borderRadius?: number;
glowStartDelay?: number; glowStartDelay?: number;
noScale?: boolean;
theme?: DefaultTheme; theme?: DefaultTheme;
}; };
@ -46,6 +47,7 @@ type StyledSvgProps = {
borderRadius?: number; borderRadius?: number;
glowDuration?: number; glowDuration?: number;
glowStartDelay?: number; glowStartDelay?: number;
noScale?: boolean;
iconColor?: string; iconColor?: string;
}; };
@ -91,16 +93,22 @@ const animation = (props: {
glowDuration?: number; glowDuration?: number;
glowStartDelay?: number; glowStartDelay?: number;
iconColor?: string; iconColor?: string;
noScale?: boolean;
}) => { }) => {
if (props.rotateDuration) { if (props.rotateDuration) {
return css` return css`
${rotate} ${props.rotateDuration}s infinite linear; ${rotate} ${props.rotateDuration}s infinite linear;
`; `;
} else if ( }
props.glowDuration !== undefined && if (props.noScale) {
props.glowStartDelay !== undefined && return css``;
props.iconColor }
) {
if (props.glowDuration === 10) {
console.warn('scake', props);
}
if (props.glowDuration !== undefined && props.glowStartDelay !== undefined && props.iconColor) {
return css` return css`
${glow( ${glow(
props.iconColor, props.iconColor,
@ -108,9 +116,9 @@ const animation = (props: {
props.glowStartDelay props.glowStartDelay
)} ${props.glowDuration}s ease infinite; )} ${props.glowDuration}s ease infinite;
`; `;
} else {
return;
} }
return;
}; };
//tslint:disable no-unnecessary-callback-wrapper //tslint:disable no-unnecessary-callback-wrapper
@ -119,6 +127,7 @@ const Svg = styled.svg<StyledSvgProps>`
transform: ${props => `rotate(${props.iconRotation}deg)`}; transform: ${props => `rotate(${props.iconRotation}deg)`};
animation: ${props => animation(props)}; animation: ${props => animation(props)};
border-radius: ${props => props.borderRadius}; border-radius: ${props => props.borderRadius};
filter: ${props => (props.noScale ? `drop-shadow(0px 0px 4px ${props.iconColor})` : '')};
`; `;
//tslint:enable no-unnecessary-callback-wrapper //tslint:enable no-unnecessary-callback-wrapper
@ -132,6 +141,7 @@ const SessionSvg = (props: {
rotateDuration?: number; rotateDuration?: number;
glowDuration?: number; glowDuration?: number;
glowStartDelay?: number; glowStartDelay?: number;
noScale?: boolean;
borderRadius?: number; borderRadius?: number;
theme: DefaultTheme; theme: DefaultTheme;
}) => { }) => {
@ -146,6 +156,7 @@ const SessionSvg = (props: {
glowDuration: props.glowDuration, glowDuration: props.glowDuration,
glowStartDelay: props.glowStartDelay, glowStartDelay: props.glowStartDelay,
iconColor: props.iconColor, iconColor: props.iconColor,
noScale: props.noScale,
}; };
return ( return (
@ -166,6 +177,7 @@ export const SessionIcon = (props: SessionIconProps) => {
glowDuration, glowDuration,
borderRadius, borderRadius,
glowStartDelay, glowStartDelay,
noScale,
} = props; } = props;
let { iconSize, iconRotation } = props; let { iconSize, iconRotation } = props;
iconSize = iconSize || SessionIconSize.Medium; iconSize = iconSize || SessionIconSize.Medium;
@ -189,6 +201,7 @@ export const SessionIcon = (props: SessionIconProps) => {
rotateDuration={rotateDuration} rotateDuration={rotateDuration}
glowDuration={glowDuration} glowDuration={glowDuration}
glowStartDelay={glowStartDelay} glowStartDelay={glowStartDelay}
noScale={noScale}
borderRadius={borderRadius} borderRadius={borderRadius}
iconRotation={iconRotation} iconRotation={iconRotation}
iconColor={iconColor} iconColor={iconColor}

@ -20,6 +20,9 @@ export const SessionIconButton = (props: SProps) => {
isSelected, isSelected,
notificationCount, notificationCount,
theme, theme,
glowDuration,
glowStartDelay,
noScale,
} = props; } = props;
const clickHandler = (e: any) => { const clickHandler = (e: any) => {
if (props.onClick) { if (props.onClick) {
@ -42,6 +45,9 @@ export const SessionIconButton = (props: SProps) => {
iconColor={iconColor} iconColor={iconColor}
iconRotation={iconRotation} iconRotation={iconRotation}
theme={themeToUSe} theme={themeToUSe}
glowDuration={glowDuration}
glowStartDelay={glowStartDelay}
noScale={noScale}
/> />
{Boolean(notificationCount) && <SessionNotificationCount count={notificationCount} />} {Boolean(notificationCount) && <SessionNotificationCount count={notificationCount} />}
</div> </div>

@ -1,36 +0,0 @@
import React from 'react';
// tslint:disable-next-line: no-submodule-imports
import useNetworkState from 'react-use/lib/useNetworkState';
import styled from 'styled-components';
type ContainerProps = {
show: boolean;
};
const OfflineContainer = styled.div<ContainerProps>`
background: ${props => props.theme.colors.accent};
color: ${props => props.theme.colors.textColor};
padding: ${props => (props.show ? props.theme.common.margins.sm : '0px')};
margin: ${props => (props.show ? props.theme.common.margins.xs : '0px')};
height: ${props => (props.show ? 'auto' : '0px')};
overflow: hidden;
transition: ${props => props.theme.common.animations.defaultDuration};
`;
const OfflineTitle = styled.h3`
padding-top: 0px;
margin-top: 0px;
`;
const OfflineMessage = styled.div``;
export const SessionOffline = () => {
const isOnline = useNetworkState().online;
return (
<OfflineContainer show={!isOnline}>
<OfflineTitle>{window.i18n('offline')}</OfflineTitle>
<OfflineMessage>{window.i18n('checkNetworkConnection')}</OfflineMessage>
</OfflineContainer>
);
};

@ -846,7 +846,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
const chatParams = { const chatParams = {
identifier: this.id, identifier: this.id,
body, body,
timestamp: this.get('sent_at') || Date.now(), timestamp: Date.now(), // force a new timestamp to handle user fixed his clock
expireTimer: this.get('expireTimer'), expireTimer: this.get('expireTimer'),
attachments, attachments,
preview, preview,

@ -179,7 +179,7 @@ export const sendViaOnion = async (
{ {
retries: 9, // each path can fail 3 times before being dropped, we have 3 paths at most retries: 9, // each path can fail 3 times before being dropped, we have 3 paths at most
factor: 2, factor: 2,
minTimeout: 1000, minTimeout: 100,
maxTimeout: 4000, maxTimeout: 4000,
onFailedAttempt: e => { onFailedAttempt: e => {
window?.log?.warn( window?.log?.warn(

@ -70,11 +70,11 @@ export async function sendMessage(
); );
throw e; throw e;
} }
if (!snode) { if (!usedNodes || usedNodes.length === 0) {
throw new window.textsecure.EmptySwarmError(pubKey, 'Ran out of swarm nodes to query'); throw new window.textsecure.EmptySwarmError(pubKey, 'Ran out of swarm nodes to query');
} else {
window?.log?.info(
`loki_message:::sendMessage - Successfully stored message to ${pubKey} via ${snode.ip}:${snode.port}`
);
} }
window?.log?.info(
`loki_message:::sendMessage - Successfully stored message to ${pubKey} via ${snode.ip}:${snode.port}`
);
} }

@ -24,11 +24,14 @@ import {
toHex, toHex,
} from '../utils/String'; } from '../utils/String';
import { Snode } from '../../data/data'; import { Snode } from '../../data/data';
import { updateIsOnline } from '../../state/ducks/onion';
// ONS name can have [a-zA-Z0-9_-] except that - is not allowed as start or end // ONS name can have [a-zA-Z0-9_-] except that - is not allowed as start or end
// do not define a regex but rather create it on the fly to avoid https://stackoverflow.com/questions/3891641/regex-test-only-works-every-other-time // do not define a regex but rather create it on the fly to avoid https://stackoverflow.com/questions/3891641/regex-test-only-works-every-other-time
export const onsNameRegex = '^\\w([\\w-]*[\\w])?$'; export const onsNameRegex = '^\\w([\\w-]*[\\w])?$';
export const ERROR_CODE_NO_CONNECT = 'ENETUNREACH: No network connection.';
const getSslAgentForSeedNode = (seedNodeHost: string, isSsl = false) => { const getSslAgentForSeedNode = (seedNodeHost: string, isSsl = false) => {
let filePrefix = ''; let filePrefix = '';
let pubkey256 = ''; let pubkey256 = '';
@ -493,8 +496,8 @@ export async function storeOnNode(targetNode: Snode, params: SendParams): Promis
e, e,
`destination ${targetNode.ip}:${targetNode.port}` `destination ${targetNode.ip}:${targetNode.port}`
); );
throw e;
} }
return false;
} }
/** */ /** */
@ -527,9 +530,12 @@ export async function retrieveNextMessages(
try { try {
const json = JSON.parse(result.body); const json = JSON.parse(result.body);
window.inboxStore?.dispatch(updateIsOnline(true));
return json.messages || []; return json.messages || [];
} catch (e) { } catch (e) {
window?.log?.warn('exception while parsing json of nextMessage:', e); window?.log?.warn('exception while parsing json of nextMessage:', e);
window.inboxStore?.dispatch(updateIsOnline(true));
return []; return [];
} }
@ -538,6 +544,11 @@ export async function retrieveNextMessages(
'Got an error while retrieving next messages. Not retrying as we trigger fetch often:', 'Got an error while retrieving next messages. Not retrying as we trigger fetch often:',
e e
); );
if (e.message === ERROR_CODE_NO_CONNECT) {
window.inboxStore?.dispatch(updateIsOnline(false));
} else {
window.inboxStore?.dispatch(updateIsOnline(true));
}
return []; return [];
} }
} }

@ -13,6 +13,7 @@ import { hrefPnServerDev, hrefPnServerProd } from '../../pushnotification/PnServ
let snodeFailureCount: Record<string, number> = {}; let snodeFailureCount: Record<string, number> = {};
import { Snode } from '../../data/data'; import { Snode } from '../../data/data';
import { ERROR_CODE_NO_CONNECT } from './SNodeAPI';
// tslint:disable-next-line: variable-name // tslint:disable-next-line: variable-name
export const TEST_resetSnodeFailureCount = () => { export const TEST_resetSnodeFailureCount = () => {
@ -37,6 +38,9 @@ export interface SnodeResponse {
export const NEXT_NODE_NOT_FOUND_PREFIX = 'Next node not found: '; export const NEXT_NODE_NOT_FOUND_PREFIX = 'Next node not found: ';
export const CLOCK_OUT_OF_SYNC_MESSAGE_ERROR =
'Your clock is out of sync with the network. Check your clock.';
// Returns the actual ciphertext, symmetric key that will be used // Returns the actual ciphertext, symmetric key that will be used
// for decryption, and an ephemeral_key to send to the next hop // for decryption, and an ephemeral_key to send to the next hop
async function encryptForPubKey(pubKeyX25519hex: string, reqObj: any): Promise<DestinationContext> { async function encryptForPubKey(pubKeyX25519hex: string, reqObj: any): Promise<DestinationContext> {
@ -195,9 +199,8 @@ async function buildOnionGuardNodePayload(
function process406Error(statusCode: number) { function process406Error(statusCode: number) {
if (statusCode === 406) { if (statusCode === 406) {
// clock out of sync // clock out of sync
console.warn('clock out of sync todo');
// this will make the pRetry stop // this will make the pRetry stop
throw new pRetry.AbortError('You clock is out of sync with the network. Check your clock.'); throw new pRetry.AbortError(CLOCK_OUT_OF_SYNC_MESSAGE_ERROR);
} }
} }
@ -783,6 +786,7 @@ const sendOnionRequest = async ({
// we are talking to a snode... // we are talking to a snode...
agent: snodeHttpsAgent, agent: snodeHttpsAgent,
abortSignal, abortSignal,
timeout: 5000,
}; };
const guardUrl = `https://${guardNode.ip}:${guardNode.port}/onion_req/v2`; const guardUrl = `https://${guardNode.ip}:${guardNode.port}/onion_req/v2`;
@ -859,7 +863,7 @@ export async function lokiOnionFetch(
return onionFetchRetryable(targetNode, body, associatedWith); return onionFetchRetryable(targetNode, body, associatedWith);
}, },
{ {
retries: 9, retries: 4,
factor: 1, factor: 1,
minTimeout: 1000, minTimeout: 1000,
maxTimeout: 2000, maxTimeout: 2000,
@ -875,6 +879,14 @@ export async function lokiOnionFetch(
} catch (e) { } catch (e) {
window?.log?.warn('onionFetchRetryable failed ', e); window?.log?.warn('onionFetchRetryable failed ', e);
// console.warn('error to show to user'); // console.warn('error to show to user');
if (e?.errno === 'ENETUNREACH') {
// better handle the no connection state
throw new Error(ERROR_CODE_NO_CONNECT);
}
if (e?.message === CLOCK_OUT_OF_SYNC_MESSAGE_ERROR) {
window?.log?.warn('Its an clock out of sync error ');
throw new pRetry.AbortError(CLOCK_OUT_OF_SYNC_MESSAGE_ERROR);
}
throw e; throw e;
} }
} }

@ -51,7 +51,6 @@ export const getSwarmPollingInstance = () => {
}; };
export class SwarmPolling { export class SwarmPolling {
private ourPubkey: PubKey | undefined;
private groupPolling: Array<{ pubkey: PubKey; lastPolledTimestamp: number }>; private groupPolling: Array<{ pubkey: PubKey; lastPolledTimestamp: number }>;
private readonly lastHashes: { [key: string]: PubkeyToHash }; private readonly lastHashes: { [key: string]: PubkeyToHash };
@ -61,7 +60,6 @@ export class SwarmPolling {
} }
public async start(waitForFirstPoll = false): Promise<void> { public async start(waitForFirstPoll = false): Promise<void> {
this.ourPubkey = UserUtils.getOurPubKeyFromCache();
this.loadGroupIds(); this.loadGroupIds();
if (waitForFirstPoll) { if (waitForFirstPoll) {
await this.TEST_pollForAllKeys(); await this.TEST_pollForAllKeys();
@ -74,7 +72,6 @@ export class SwarmPolling {
* Used fo testing only * Used fo testing only
*/ */
public TEST_reset() { public TEST_reset() {
this.ourPubkey = undefined;
this.groupPolling = []; this.groupPolling = [];
} }
@ -88,10 +85,6 @@ export class SwarmPolling {
public removePubkey(pk: PubKey | string) { public removePubkey(pk: PubKey | string) {
const pubkey = PubKey.cast(pk); const pubkey = PubKey.cast(pk);
window?.log?.info('Swarm removePubkey: removing pubkey from polling', pubkey.key); window?.log?.info('Swarm removePubkey: removing pubkey from polling', pubkey.key);
if (this.ourPubkey && PubKey.cast(pk).isEqual(this.ourPubkey)) {
this.ourPubkey = undefined;
}
this.groupPolling = this.groupPolling.filter(group => !pubkey.isEqual(group.pubkey)); this.groupPolling = this.groupPolling.filter(group => !pubkey.isEqual(group.pubkey));
} }
@ -132,9 +125,8 @@ export class SwarmPolling {
*/ */
public async TEST_pollForAllKeys() { public async TEST_pollForAllKeys() {
// we always poll as often as possible for our pubkey // we always poll as often as possible for our pubkey
const directPromise = this.ourPubkey const ourPubkey = UserUtils.getOurPubKeyFromCache();
? this.TEST_pollOnceForKey(this.ourPubkey, false) const directPromise = this.TEST_pollOnceForKey(ourPubkey, false);
: Promise.resolve();
const now = Date.now(); const now = Date.now();
const groupPromises = this.groupPolling.map(async group => { const groupPromises = this.groupPolling.map(async group => {

@ -3,10 +3,12 @@ import { Snode } from '../../data/data';
export type OnionState = { export type OnionState = {
snodePaths: Array<Array<Snode>>; snodePaths: Array<Array<Snode>>;
isOnline: boolean;
}; };
export const initialOnionPathState = { export const initialOnionPathState = {
snodePaths: new Array<Array<Snode>>(), snodePaths: new Array<Array<Snode>>(),
isOnline: false,
}; };
/** /**
@ -17,12 +19,15 @@ const onionSlice = createSlice({
initialState: initialOnionPathState, initialState: initialOnionPathState,
reducers: { reducers: {
updateOnionPaths(state: OnionState, action: PayloadAction<Array<Array<Snode>>>) { updateOnionPaths(state: OnionState, action: PayloadAction<Array<Array<Snode>>>) {
return { snodePaths: action.payload }; return { ...state, snodePaths: action.payload };
},
updateIsOnline(state: OnionState, action: PayloadAction<boolean>) {
return { ...state, isOnline: action.payload };
}, },
}, },
}); });
// destructures // destructures
const { actions, reducer } = onionSlice; const { actions, reducer } = onionSlice;
export const { updateOnionPaths } = actions; export const { updateOnionPaths, updateIsOnline } = actions;
export const defaultOnionReducer = reducer; export const defaultOnionReducer = reducer;

@ -21,3 +21,8 @@ export const getFirstOnionPathLength = createSelector(
getFirstOnionPath, getFirstOnionPath,
(state: Array<Snode>): number => state.length || 0 (state: Array<Snode>): number => state.length || 0
); );
export const getIsOnline = createSelector(
getOnionPaths,
(state: OnionState): boolean => state.isOnline
);

@ -216,7 +216,7 @@ describe('OnionPathsErrors', () => {
throw new Error('Error expected'); throw new Error('Error expected');
} catch (e) { } catch (e) {
expect(e.message).to.equal( expect(e.message).to.equal(
'You clock is out of sync with the network. Check your clock.' 'Your clock is out of sync with the network. Check your clock.'
); );
// this makes sure that this call would not be retried // this makes sure that this call would not be retried
expect(e.name).to.equal('AbortError'); expect(e.name).to.equal('AbortError');
@ -237,7 +237,7 @@ describe('OnionPathsErrors', () => {
throw new Error('Error expected'); throw new Error('Error expected');
} catch (e) { } catch (e) {
expect(e.message).to.equal( expect(e.message).to.equal(
'You clock is out of sync with the network. Check your clock.' 'Your clock is out of sync with the network. Check your clock.'
); );
// this makes sure that this call would not be retried // this makes sure that this call would not be retried
expect(e.name).to.equal('AbortError'); expect(e.name).to.equal('AbortError');

Loading…
Cancel
Save