diff --git a/package.json b/package.json index 64f1c6c8f..ee7f58991 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/preload.js b/preload.js index 226bfbe21..0ec1831b2 100644 --- a/preload.js +++ b/preload.js @@ -29,7 +29,7 @@ window.getNodeVersion = () => configAny.node_version; window.sessionFeatureFlags = { useOnionRequests: true, - useTestNet: isTestNet(), + useTestNet: isTestNet() || isTestIntegration(), integrationTestEnv: isTestIntegration(), useClosedGroupV3: false, debug: { diff --git a/stylesheets/_session_constants.scss b/stylesheets/_session_constants.scss index 87f9d4ca4..02b7323be 100644 --- a/stylesheets/_session_constants.scss +++ b/stylesheets/_session_constants.scss @@ -153,7 +153,6 @@ $session-margin-md: 15px; $session-margin-lg: 20px; // Animations -$session-transition-duration: 0.25s; @keyframes fadein { from { diff --git a/stylesheets/_session_slider.scss b/stylesheets/_session_slider.scss index 81feb400c..db3b56efa 100644 --- a/stylesheets/_session_slider.scss +++ b/stylesheets/_session_slider.scss @@ -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; diff --git a/ts/components/SessionFocusTrap.tsx b/ts/components/SessionFocusTrap.tsx new file mode 100644 index 000000000..bec1f9471 --- /dev/null +++ b/ts/components/SessionFocusTrap.tsx @@ -0,0 +1,21 @@ +import FocusTrap from 'focus-trap-react'; +import { ReactNode, useState } from 'react'; +import { useMount } from 'react-use'; + +/** + * Focus trap which activates on mount. + */ +export function SessionFocusTrap(props: { children: 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 ( + + {props.children} + + ); +} diff --git a/ts/components/SessionWrapperModal.tsx b/ts/components/SessionWrapperModal.tsx index 26c7b9069..775160a8f 100644 --- a/ts/components/SessionWrapperModal.tsx +++ b/ts/components/SessionWrapperModal.tsx @@ -1,11 +1,11 @@ import classNames from 'classnames'; -import FocusTrap from 'focus-trap-react'; -import { useRef } from 'react'; +import { ReactNode, 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; - children: any; + children: ReactNode; headerReverse?: boolean; additionalClassName?: string; }; @@ -64,7 +64,7 @@ export const SessionWrapperModal = (props: SessionWrapperModalType) => { }; return ( - +
{
-
+ ); }; diff --git a/ts/components/calling/CallButtons.tsx b/ts/components/calling/CallButtons.tsx index 93532bd6e..17de2e086 100644 --- a/ts/components/calling/CallButtons.tsx +++ b/ts/components/calling/CallButtons.tsx @@ -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; diff --git a/ts/components/conversation/composition/UserMentions.tsx b/ts/components/conversation/composition/UserMentions.tsx index 6db54809f..e0b2cebf2 100644 --- a/ts/components/conversation/composition/UserMentions.tsx +++ b/ts/components/conversation/composition/UserMentions.tsx @@ -20,7 +20,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)', diff --git a/ts/components/conversation/header/ConversationHeaderSelectionOverlay.tsx b/ts/components/conversation/header/ConversationHeaderSelectionOverlay.tsx index 991db8cdc..300a78d0e 100644 --- a/ts/components/conversation/header/ConversationHeaderSelectionOverlay.tsx +++ b/ts/components/conversation/header/ConversationHeaderSelectionOverlay.tsx @@ -1,4 +1,3 @@ -import FocusTrap from 'focus-trap-react'; import { useDispatch, useSelector } from 'react-redux'; import useKey from 'react-use/lib/useKey'; @@ -16,6 +15,7 @@ import { SessionButtonType, } from '../../basic/SessionButton'; import { SessionIconButton } from '../../icon'; +import { SessionFocusTrap } from '../../SessionFocusTrap'; export const SelectionOverlay = () => { const selectedMessageIds = useSelector(getSelectedMessageIds); @@ -68,7 +68,7 @@ export const SelectionOverlay = () => { const classNameAndId = 'message-selection-overlay'; return ( - +
@@ -92,6 +92,6 @@ export const SelectionOverlay = () => { />
-
+ ); }; diff --git a/ts/components/dialog/SessionSeedModal.tsx b/ts/components/dialog/SessionSeedModal.tsx index 91d981988..ec658973d 100644 --- a/ts/components/dialog/SessionSeedModal.tsx +++ b/ts/components/dialog/SessionSeedModal.tsx @@ -62,6 +62,7 @@ const Password = (props: PasswordProps) => { @@ -77,12 +78,14 @@ const Password = (props: PasswordProps) => { text={i18n('done')} buttonType={SessionButtonType.Simple} onClick={confirmPassword} + dataTestId="session-confirm-ok-button" /> diff --git a/ts/components/leftpane/LeftPaneSectionHeader.tsx b/ts/components/leftpane/LeftPaneSectionHeader.tsx index 33767b813..f5b3f008e 100644 --- a/ts/components/leftpane/LeftPaneSectionHeader.tsx +++ b/ts/components/leftpane/LeftPaneSectionHeader.tsx @@ -29,7 +29,7 @@ const StyledProgressBarContainer = styled.div` const StyledProgressBarInner = styled.div` background: var(--primary-color); width: 100%; - transition: width 0.5s ease-in; + transition: width var(--default-duration) ease-in; height: 100%; `; diff --git a/ts/components/leftpane/MessageRequestsBanner.tsx b/ts/components/leftpane/MessageRequestsBanner.tsx index efdca2646..5fde56784 100644 --- a/ts/components/leftpane/MessageRequestsBanner.tsx +++ b/ts/components/leftpane/MessageRequestsBanner.tsx @@ -1,4 +1,4 @@ -import { MouseEvent } from 'react'; +import { MouseEvent, ReactNode } from 'react'; import { contextMenu } from 'react-contexify'; import { createPortal } from 'react-dom'; import { useSelector } from 'react-redux'; @@ -137,6 +137,6 @@ export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => { ); }; -const Portal = ({ children }: { children: any }) => { +const Portal = ({ children }: { children: ReactNode }) => { return createPortal(children, document.querySelector('.inbox.index') as Element); }; diff --git a/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx b/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx index 23986ecad..a1d95ba47 100644 --- a/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx +++ b/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; import { isNil } from 'lodash'; -import { MouseEvent, useCallback } from 'react'; +import { MouseEvent, ReactNode, useCallback } from 'react'; import { contextMenu } from 'react-contexify'; import { createPortal } from 'react-dom'; import { useDispatch, useSelector } from 'react-redux'; @@ -34,7 +34,7 @@ type PropsHousekeeping = { type Props = { conversationId: string } & PropsHousekeeping; -const Portal = ({ children }: { children: any }) => { +const Portal = ({ children }: { children: ReactNode }) => { return createPortal(children, document.querySelector('.inbox.index') as Element); }; diff --git a/ts/components/leftpane/overlay/choose-action/OverlayChooseAction.tsx b/ts/components/leftpane/overlay/choose-action/OverlayChooseAction.tsx index 9d20c82cb..cd18c699f 100644 --- a/ts/components/leftpane/overlay/choose-action/OverlayChooseAction.tsx +++ b/ts/components/leftpane/overlay/choose-action/OverlayChooseAction.tsx @@ -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 { diff --git a/ts/components/registration/RegistrationStages.tsx b/ts/components/registration/RegistrationStages.tsx index 6adf31bc9..1d1f2d866 100644 --- a/ts/components/registration/RegistrationStages.tsx +++ b/ts/components/registration/RegistrationStages.tsx @@ -15,7 +15,7 @@ import { } from '../../state/onboarding/selectors/registration'; import { Storage } from '../../util/storage'; import { Flex } from '../basic/Flex'; -import { SpacerLG, SpacerSM, SpacerXS } from '../basic/Text'; +import { SpacerSM, SpacerXL, SpacerXS } from '../basic/Text'; import { SessionIcon, SessionIconButton } from '../icon'; import { OnboardContainer } from './components'; import { CreateAccount, RestoreAccount, Start } from './stages'; @@ -44,7 +44,7 @@ export const RegistrationStages = () => { return ( - +
@@ -82,7 +82,7 @@ export const RegistrationStages = () => { - + = []; + export const getSwarmPollingInstance = () => { if (!instance) { instance = new SwarmPolling(); @@ -96,9 +98,11 @@ export class SwarmPolling { if (waitForFirstPoll) { await this.pollForAllKeys(); } else { - setTimeout(() => { - void this.pollForAllKeys(); - }, 4000); + timeouts.push( + setTimeout(() => { + void this.pollForAllKeys(); + }, 4000) + ); } } @@ -110,6 +114,15 @@ export class SwarmPolling { this.hasStarted = false; } + public stop(e?: Error) { + window.log.info('[swarmPolling] stopped swarm polling', e?.message || e || ''); + + for (let i = 0; i < timeouts.length; i++) { + clearTimeout(timeouts[i]); + } + this.resetSwarmPolling(); + } + public forcePolledTimestamp(pubkey: PubKey, lastPoll: number) { this.groupPolling = this.groupPolling.map(group => { if (PubKey.isEqual(pubkey, group.pubkey)) { @@ -175,7 +188,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 @@ -212,7 +225,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)); } } diff --git a/ts/themes/SessionTheme.tsx b/ts/themes/SessionTheme.tsx index 0b07ee3a7..ef0694aff 100644 --- a/ts/themes/SessionTheme.tsx +++ b/ts/themes/SessionTheme.tsx @@ -1,5 +1,6 @@ import { ipcRenderer } from 'electron'; +import { ReactNode } from 'react'; import { createGlobalStyle } from 'styled-components'; import { getOppositeTheme, isThemeMismatched } from '../util/theme'; import { classicDark } from './classicDark'; @@ -14,7 +15,7 @@ const SessionGlobalStyles = createGlobalStyle` }; `; -export const SessionTheme = ({ children }: { children: any }) => ( +export const SessionTheme = ({ children }: { children: ReactNode }) => ( <> {children} diff --git a/ts/themes/globals.tsx b/ts/themes/globals.tsx index 7afad7b75..644350f27 100644 --- a/ts/themes/globals.tsx +++ b/ts/themes/globals.tsx @@ -1,3 +1,4 @@ +import { isTestIntegration } from '../shared/env_vars'; import { hexColorToRGB } from '../util/hexColorToRGB'; import { COLORS } from './constants/colors'; @@ -129,8 +130,8 @@ export const THEME_GLOBALS: ThemeGlobals = { '--composition-container-height': '60px', '--search-input-height': '34px', - '--default-duration': '0.25s', - '--default-duration-seconds': '0.25', // framer-motion requires a number + '--default-duration': isTestIntegration() ? '0s' : '0.25s', + '--default-duration-seconds': isTestIntegration() ? '0' : '0.25', // framer-motion requires a number '--green-color': COLORS.PRIMARY.GREEN, '--blue-color': COLORS.PRIMARY.BLUE,