Merge pull request #3076 from oxen-io/unstable

Session 1.12.2
pull/3077/head
Audric Ackermann 3 weeks ago committed by GitHub
commit ab971ab702
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -578,5 +578,6 @@
"resolution": "Resolution",
"duration": "Duration",
"notApplicable": "N/A",
"unknownError": "Unknown Error"
"unknownError": "Unknown Error",
"displayNameErrorNew": "We were unable to load your display name. Please enter a new display name to continue."
}

@ -2,7 +2,7 @@
"name": "session-desktop",
"productName": "Session",
"description": "Private messaging from your desktop",
"version": "1.12.1",
"version": "1.12.2",
"license": "GPL-3.0",
"author": {
"name": "Oxen Labs",

@ -29,7 +29,7 @@ window.getNodeVersion = () => configAny.node_version;
window.sessionFeatureFlags = {
useOnionRequests: true,
useTestNet: isTestNet(),
useTestNet: isTestNet() || isTestIntegration(),
integrationTestEnv: isTestIntegration(),
useClosedGroupV3: false,
debug: {

@ -153,7 +153,6 @@ $session-margin-md: 15px;
$session-margin-lg: 20px;
// Animations
$session-transition-duration: 0.25s;
@keyframes fadein {
from {

@ -172,13 +172,13 @@
}
.rc-slider-tooltip-zoom-down-enter,
.rc-slider-tooltip-zoom-down-appear {
animation-duration: 0.3s;
animation-duration: var(--default-duration);
animation-fill-mode: both;
display: block !important;
animation-play-state: paused;
}
.rc-slider-tooltip-zoom-down-leave {
animation-duration: 0.3s;
animation-duration: var(--default-duration);
animation-fill-mode: both;
display: block !important;
animation-play-state: paused;

@ -0,0 +1,21 @@
import FocusTrap from 'focus-trap-react';
import React, { useState } from 'react';
import { useMount } from 'react-use';
/**
* Focus trap which activates on mount.
*/
export function SessionFocusTrap(props: { children: React.ReactNode }) {
const [active, setActive] = useState(false);
// Activate the trap on mount so we **should** have a button to tab through. focus-trap-react will throw if we don't have a button when the trap becomes active.
// We might still have an issue for dialogs which have buttons added with a useEffect, or asynchronously but have no buttons on mount.
// If that happens we will need to force those dialogs to have at least one button so focus-trap-react does not throw.
useMount(() => setActive(true));
return (
<FocusTrap active={active} focusTrapOptions={{ initialFocus: false, allowOutsideClick: true }}>
{props.children}
</FocusTrap>
);
}

@ -1,11 +1,11 @@
import classNames from 'classnames';
import FocusTrap from 'focus-trap-react';
import React, { useRef } from 'react';
import useKey from 'react-use/lib/useKey';
import { SessionIconButton } from './icon';
import { SessionButton, SessionButtonColor, SessionButtonType } from './basic/SessionButton';
import { SessionFocusTrap } from './SessionFocusTrap';
export type SessionWrapperModalType = {
title?: string;
@ -17,7 +17,7 @@ export type SessionWrapperModalType = {
cancelText?: string;
showExitIcon?: boolean;
headerIconButtons?: Array<any>;
children: any;
children: React.ReactNode;
headerReverse?: boolean;
additionalClassName?: string;
};
@ -64,7 +64,7 @@ export const SessionWrapperModal = (props: SessionWrapperModalType) => {
};
return (
<FocusTrap focusTrapOptions={{ initialFocus: false, allowOutsideClick: true }}>
<SessionFocusTrap>
<div
className={classNames('loki-dialog modal', additionalClassName || null)}
onClick={handleClick}
@ -128,6 +128,6 @@ export const SessionWrapperModal = (props: SessionWrapperModalType) => {
</div>
</div>
</div>
</FocusTrap>
</SessionFocusTrap>
);
};

@ -355,7 +355,7 @@ const StyledCallWindowControls = styled.div<{ isFullScreen: boolean; makeVisible
margin-right: auto;
left: 0;
right: 0;
transition: all 0.25s ease-in-out;
transition: all var(--default-duration) ease-in-out;
display: flex;
justify-content: center;

@ -1,7 +1,7 @@
import React from 'react';
import { SuggestionDataItem } from 'react-mentions';
import { MemberListItem } from '../../MemberListItem';
import { HTMLDirection } from '../../../util/i18n';
import { MemberListItem } from '../../MemberListItem';
const listRTLStyle = { position: 'absolute', bottom: '0px', right: '100%' };
@ -21,7 +21,7 @@ export const styleForCompositionBoxSuggestions = (dir: HTMLDirection = 'ltr') =>
paddingBottom: '5px',
backgroundColor: 'var(--suggestions-background-color)',
color: 'var(--suggestions-text-color)',
transition: '0.25s',
transition: 'var(--default-duration)',
'&focused': {
backgroundColor: 'var(--suggestions-background-hover-color)',

@ -1,5 +1,5 @@
import FocusTrap from 'focus-trap-react';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import useKey from 'react-use/lib/useKey';
@ -17,6 +17,7 @@ import {
SessionButtonType,
} from '../../basic/SessionButton';
import { SessionIconButton } from '../../icon';
import { SessionFocusTrap } from '../../SessionFocusTrap';
export const SelectionOverlay = () => {
const selectedMessageIds = useSelector(getSelectedMessageIds);
@ -69,7 +70,7 @@ export const SelectionOverlay = () => {
const classNameAndId = 'message-selection-overlay';
return (
<FocusTrap focusTrapOptions={{ initialFocus: false, allowOutsideClick: true }}>
<SessionFocusTrap>
<div className={classNameAndId} id={classNameAndId}>
<div className="close-button">
<SessionIconButton iconType="exit" iconSize="medium" onClick={onCloseOverlay} />
@ -93,6 +94,6 @@ export const SelectionOverlay = () => {
/>
</div>
</div>
</FocusTrap>
</SessionFocusTrap>
);
};

@ -13,7 +13,7 @@ export const StyledRightPanelContainer = styled.div`
height: var(--right-panel-height);
right: 0vw;
transition: transform 0.3s ease-in-out;
transition: transform var(--default-duration) ease-in-out;
transform: translateX(100%);
will-change: transform;
width: var(--right-panel-width);
@ -25,7 +25,7 @@ export const StyledRightPanelContainer = styled.div`
&.show {
transform: none;
transition: transform 0.3s ease-in-out;
transition: transform var(--default-duration) ease-in-out;
z-index: 3;
visibility: visible;
}

@ -62,6 +62,7 @@ const Password = (props: PasswordProps) => {
<input
type="password"
id="seed-input-password"
data-testid="password-input"
placeholder={i18n('enterPassword')}
onKeyUp={onEnter}
/>
@ -77,12 +78,14 @@ const Password = (props: PasswordProps) => {
text={i18n('done')}
buttonType={SessionButtonType.Simple}
onClick={confirmPassword}
dataTestId="session-confirm-ok-button"
/>
<SessionButton
text={i18n('cancel')}
buttonType={SessionButtonType.Simple}
buttonColor={SessionButtonColor.Danger}
onClick={onClose}
dataTestId="session-confirm-cancel-button"
/>
</div>
</>

@ -28,7 +28,7 @@ const StyledProgressBarContainer = styled.div`
const StyledProgressBarInner = styled.div`
background: var(--primary-color);
width: 90%;
transition: width 0.5s ease-in;
transition: width var(--default-duration) ease-in;
height: 100%;
`;

@ -4,10 +4,10 @@ import { createPortal } from 'react-dom';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { getUnreadConversationRequests } from '../../state/selectors/conversations';
import { isSearching } from '../../state/selectors/search';
import { getHideMessageRequestBanner } from '../../state/selectors/userConfig';
import { SessionIcon, SessionIconSize, SessionIconType } from '../icon';
import { MessageRequestBannerContextMenu } from '../menu/MessageRequestBannerContextMenu';
import { isSearching } from '../../state/selectors/search';
const StyledMessageRequestBanner = styled.div`
height: 64px;
@ -137,6 +137,6 @@ export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => {
);
};
const Portal = ({ children }: { children: any }) => {
const Portal = ({ children }: { children: React.ReactNode }) => {
return createPortal(children, document.querySelector('.inbox.index') as Element);
};

@ -34,7 +34,7 @@ type PropsHousekeeping = {
type Props = { conversationId: string } & PropsHousekeeping;
const Portal = ({ children }: { children: any }) => {
const Portal = ({ children }: { children: React.ReactNode }) => {
return createPortal(children, document.querySelector('.inbox.index') as Element);
};

@ -14,7 +14,7 @@ const StyledActionRow = styled.button`
display: flex;
align-items: center;
border-bottom: 1px var(--border-color) solid;
transition-duration: 0.25s;
transition-duration: var(--default-duration);
width: 100%;
&:first-child {

@ -1,12 +1,11 @@
import React, { createContext, useEffect, useState } from 'react';
import { SignUpMode, SignUpTab } from './SignUpTab';
import { SignInMode, SignInTab } from './SignInTab';
import { Data } from '../../data/data';
import { SettingsKey } from '../../data/settings-key';
import { getSwarmPollingInstance } from '../../session/apis/snode_api';
import { getConversationController } from '../../session/conversations';
import { mnDecode } from '../../session/crypto/mnemonic';
import { PromiseUtils, StringUtils, ToastUtils } from '../../session/utils';
import { TaskTimedOutError } from '../../session/utils/Promise';
import { fromHex } from '../../session/utils/String';
import { trigger } from '../../shims/events';
import {
generateMnemonic,
@ -14,9 +13,9 @@ import {
sessionGenerateKeyPair,
signInByLinkingDevice,
} from '../../util/accountManager';
import { fromHex } from '../../session/utils/String';
import { setSignInByLinking, setSignWithRecoveryPhrase, Storage } from '../../util/storage';
import { SettingsKey } from '../../data/settings-key';
import { Storage, setSignInByLinking, setSignWithRecoveryPhrase } from '../../util/storage';
import { SignInMode, SignInTab } from './SignInTab';
import { SignUpMode, SignUpTab } from './SignUpTab';
export async function resetRegistration() {
await Data.removeAll();
@ -104,7 +103,10 @@ export async function signInWithRecovery(signInDetails: {
* This is will try to sign in with the user recovery phrase.
* If no ConfigurationMessage is received in 60seconds, the loading will be canceled.
*/
export async function signInWithLinking(signInDetails: { userRecoveryPhrase: string }) {
export async function signInWithLinking(
signInDetails: { userRecoveryPhrase: string },
setSignInMode: (phase: SignInMode) => void
) {
const { userRecoveryPhrase } = signInDetails;
window?.log?.info('LINKING DEVICE');
@ -136,18 +138,14 @@ export async function signInWithLinking(signInDetails: { userRecoveryPhrase: str
trigger('openInbox');
} catch (e) {
await resetRegistration();
if (e instanceof TaskTimedOutError) {
ToastUtils.pushToastError(
'registrationError',
'Could not find your display name. Please Sign In by Restoring Your Account instead.'
);
} else {
ToastUtils.pushToastError(
'registrationError',
`Error: ${e.message || 'Something went wrong'}`
);
}
window?.log?.warn('exception during registration:', e);
ToastUtils.pushToastError('registrationError', window.i18n('displayNameErrorNew'));
window?.log?.error(
'[signInWithLinking] Error during sign in by linking lets try and sign in by recovery phrase',
e.message || e
);
getSwarmPollingInstance().stop(e);
await setSignWithRecoveryPhrase(false);
setSignInMode(SignInMode.UsingRecoveryPhrase);
}
}

@ -123,7 +123,7 @@ export const SignInTab = () => {
// from the configuration message will be used.
const showDisplayNameField = isRecovery;
// Display name is required only on isRecoveryMode
// Display name is required only on isRecoveryMode or if linking a device fails
const displayNameOK = (isRecovery && !displayNameError && !!displayName) || isLinking;
// Seed is mandatory no matter which mode
@ -139,9 +139,12 @@ export const SignInTab = () => {
});
} else if (isLinking) {
setIsLoading(true);
await signInWithLinking({
userRecoveryPhrase: recoveryPhrase,
});
await signInWithLinking(
{
userRecoveryPhrase: recoveryPhrase,
},
setSignInMode
);
setIsLoading(false);
}
};

@ -428,7 +428,7 @@ async function createWindow() {
}, 5000);
}
if (isDevProd()) {
if (isDevProd() && !isTestIntegration()) {
// Open the DevTools.
mainWindow.webContents.openDevTools({
mode: 'bottom',

@ -57,6 +57,8 @@ export function extractWebSocketContent(
}
let instance: SwarmPolling | undefined;
const timeouts: Array<NodeJS.Timeout> = [];
export const getSwarmPollingInstance = () => {
if (!instance) {
instance = new SwarmPolling();
@ -84,9 +86,11 @@ export class SwarmPolling {
if (waitForFirstPoll) {
await this.pollForAllKeys();
} else {
setTimeout(() => {
void this.pollForAllKeys();
}, 4000);
timeouts.push(
setTimeout(() => {
void this.pollForAllKeys();
}, 4000)
);
}
}
@ -98,6 +102,17 @@ export class SwarmPolling {
this.hasStarted = false;
}
// TODO[epic=ses-50] this is a temporary solution until onboarding is merged
public stop(e: Error) {
window.log.error(`[swarmPolling] stopped polling due to error: ${e.message || e}`);
for (let i = 0; i < timeouts.length; i++) {
clearTimeout(timeouts[i]);
window.log.debug(`[swarmPolling] cleared timeout ${timeouts[i]} `);
}
this.resetSwarmPolling();
}
public forcePolledTimestamp(pubkey: PubKey, lastPoll: number) {
this.groupPolling = this.groupPolling.map(group => {
if (PubKey.isEqual(pubkey, group.pubkey)) {
@ -163,7 +178,7 @@ export class SwarmPolling {
if (!window.getGlobalOnlineStatus()) {
window?.log?.error('pollForAllKeys: offline');
// Very important to set up a new polling call so we do retry at some point
setTimeout(this.pollForAllKeys.bind(this), SWARM_POLLING_TIMEOUT.ACTIVE);
timeouts.push(setTimeout(this.pollForAllKeys.bind(this), SWARM_POLLING_TIMEOUT.ACTIVE));
return;
}
// we always poll as often as possible for our pubkey
@ -200,7 +215,7 @@ export class SwarmPolling {
window?.log?.warn('pollForAllKeys exception: ', e);
throw e;
} finally {
setTimeout(this.pollForAllKeys.bind(this), SWARM_POLLING_TIMEOUT.ACTIVE);
timeouts.push(setTimeout(this.pollForAllKeys.bind(this), SWARM_POLLING_TIMEOUT.ACTIVE));
}
}

@ -1,10 +1,10 @@
import { ipcRenderer } from 'electron';
import React from 'react';
import { createGlobalStyle } from 'styled-components';
import { switchThemeTo } from './switchTheme';
import { classicDark } from './classicDark';
import { getOppositeTheme, isThemeMismatched } from '../util/theme';
import { declareCSSVariables, THEME_GLOBALS } from './globals';
import { classicDark } from './classicDark';
import { THEME_GLOBALS, declareCSSVariables } from './globals';
import { switchThemeTo } from './switchTheme';
// Defaults to Classic Dark theme
const SessionGlobalStyles = createGlobalStyle`
@ -14,7 +14,7 @@ const SessionGlobalStyles = createGlobalStyle`
};
`;
export const SessionTheme = ({ children }: { children: any }) => (
export const SessionTheme = ({ children }: { children: React.ReactNode }) => (
<>
<SessionGlobalStyles />
{children}

@ -1,3 +1,4 @@
import { isTestIntegration } from '../shared/env_vars';
import { hexColorToRGB } from '../util/hexColorToRGB';
import { COLORS } from './constants/colors';
@ -126,7 +127,7 @@ export const THEME_GLOBALS: ThemeGlobals = {
'--composition-container-height': '60px',
'--search-input-height': '34px',
'--default-duration': '0.25s',
'--default-duration': isTestIntegration() ? '0s' : '0.25s',
'--green-color': COLORS.PRIMARY.GREEN,
'--blue-color': COLORS.PRIMARY.BLUE,

@ -162,6 +162,7 @@ export type LocalizerKeys =
| 'disappears'
| 'displayName'
| 'displayNameEmpty'
| 'displayNameErrorNew'
| 'displayNameTooLong'
| 'document'
| 'documents'

Loading…
Cancel
Save