diff --git a/.storybook/config.js b/.storybook/config.js index 1b697b307e6c..d42c60ffe5bf 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -72,6 +72,8 @@ addDecorator((storyFn /* , context */) => { document.body.classList.add('keyboard-mode'); } + document.body.classList.add('page-is-visible'); + return (
diff --git a/stylesheets/_global.scss b/stylesheets/_global.scss index 9fb0c183c89c..8f518908c85b 100644 --- a/stylesheets/_global.scss +++ b/stylesheets/_global.scss @@ -269,12 +269,6 @@ $loading-height: 16px; border-color: $color-ios-blue-tint $color-ios-blue-tint $color-gray-02 $color-gray-02 !important; } - - @keyframes rotate { - to { - transform: rotate(360deg); - } - } } .x { @@ -689,3 +683,9 @@ $loading-height: 16px; .overflow-hidden { overflow: hidden; } + +@keyframes rotate { + to { + transform: rotate(360deg); + } +} diff --git a/stylesheets/_mixins.scss b/stylesheets/_mixins.scss index 21375975e754..fbec957656c4 100644 --- a/stylesheets/_mixins.scss +++ b/stylesheets/_mixins.scss @@ -109,6 +109,14 @@ } } +// NOTE: As of this writing, this mixin only works in the main window, because this class +// is only applied there. +@mixin only-when-page-is-visible { + .page-is-visible & { + @content; + } +} + // Search results loading @mixin search-results-loading-pulsating-background { diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index c579a4a26dad..ea81ab11a737 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -1131,21 +1131,10 @@ .module-message__metadata__status-icon--paused, .module-message__metadata__status-icon--sending { - animation: module-message__metadata__status-icon--spinning 4s linear infinite; - - @include light-theme { - @include color-svg('../images/sending.svg', $color-white); - } - @include dark-theme { - @include color-svg('../images/sending.svg', $color-white); - } -} - -@keyframes module-message__metadata__status-icon--spinning { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); + @include only-when-page-is-visible { + animation: rotate 4s linear infinite; } + @include color-svg('../images/sending.svg', $color-white); } .module-message__metadata__status-icon--sent { @@ -4129,11 +4118,15 @@ button.module-image__border-overlay:focus { background-color: $color-gray-60; } - animation: typing-animation 1600ms ease infinite; + @include only-when-page-is-visible { + animation: typing-animation 1600ms ease infinite; + } } .module-left-pane .module-typing-animation__dot { - animation-name: typing-animation-bare; + @include only-when-page-is-visible { + animation-name: typing-animation-bare; + } } .module-typing-animation__dot--light { @@ -4649,7 +4642,7 @@ button.module-image__border-overlay:focus { height: 100%; width: 100%; - animation: spinner-arc-animation 1000ms linear infinite; + animation: rotate 1000ms linear infinite; @include light-theme { @include color-svg('../images/spinner-56.svg', $color-gray-60); @@ -4659,18 +4652,6 @@ button.module-image__border-overlay:focus { } } -@keyframes spinner-arc-animation { - 0% { - transform: rotate(0deg); - } - 50% { - transform: rotate(180deg); - } - 100% { - transform: rotate(360deg); - } -} - // In these --small and --mini sizes, we're exploding our @color-svg mixin so we don't // have to duplicate our background colors for the dark/ios/size matrix. @@ -6489,8 +6470,9 @@ button.module-image__border-overlay:focus { } &--sending { - animation: module-conversation-list__item--contact-or-conversation__contact__message__text__status-icon--spinning - 4s linear infinite; + @include only-when-page-is-visible { + animation: rotate 4s linear infinite; + } @include light-theme { @include color-svg('../images/sending.svg', $color-gray-60); } @@ -6664,13 +6646,6 @@ button.module-image__border-overlay:focus { } } -@keyframes module-conversation-list__item--contact-or-conversation__contact__message__text__status-icon--spinning { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - // Module: Left Pane .module-left-pane { diff --git a/stylesheets/components/MessageDetail.scss b/stylesheets/components/MessageDetail.scss index e9e63c5626c9..c33e5e4feb52 100644 --- a/stylesheets/components/MessageDetail.scss +++ b/stylesheets/components/MessageDetail.scss @@ -121,19 +121,11 @@ &--Pending:after { width: 12px; - animation: module-message-detail__contact-group__header--Pending 4s linear - infinite; + animation: rotate 4s linear infinite; @include normal-icon('../images/sending.svg'); } } -@keyframes module-message-detail__contact-group__header--Pending { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - .module-message-detail__contact { margin-bottom: 8px; padding: 8px 0; diff --git a/ts/components/App.tsx b/ts/components/App.tsx index 87ea4e23796b..e9f7e9c4a602 100644 --- a/ts/components/App.tsx +++ b/ts/components/App.tsx @@ -1,3 +1,6 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + import React, { useEffect } from 'react'; import classNames from 'classnames'; @@ -6,6 +9,7 @@ import { Inbox } from './Inbox'; import { Install } from './Install'; import { StandaloneRegistration } from './StandaloneRegistration'; import { ThemeType } from '../types/Util'; +import { usePageVisibility } from '../util/hooks'; export type PropsType = { appView: AppViewType; @@ -32,7 +36,7 @@ export const App = ({ contents = ; } - // This is here so that themes are properly applied to anything that is + // This are here so that themes are properly applied to anything that is // created in a portal and exists outside of the container. useEffect(() => { document.body.classList.remove('light-theme'); @@ -46,6 +50,11 @@ export const App = ({ } }, [theme]); + const isPageVisible = usePageVisibility(); + useEffect(() => { + document.body.classList.toggle('page-is-visible', isPageVisible); + }, [isPageVisible]); + return (