diff --git a/stylesheets/_variables.scss b/stylesheets/_variables.scss index ed4fe971bb22..df7efbf5343c 100644 --- a/stylesheets/_variables.scss +++ b/stylesheets/_variables.scss @@ -40,6 +40,7 @@ $color-black-alpha-05: rgba($color-black, 0.05); $color-black-alpha-06: rgba($color-black, 0.06); $color-black-alpha-08: rgba($color-black, 0.08); $color-black-alpha-12: rgba($color-black, 0.12); +$color-black-alpha-16: rgba($color-black, 0.16); $color-black-alpha-20: rgba($color-black, 0.2); $color-black-alpha-30: rgba($color-black, 0.3); $color-black-alpha-40: rgba($color-black, 0.4); @@ -49,6 +50,8 @@ $color-black-alpha-70: rgba($color-black, 0.7); $color-black-alpha-80: rgba($color-black, 0.8); $color-black-alpha-90: rgba($color-black, 0.9); +$color-transparent: rgba(0, 0, 0, 0); + $color-ultramarine-dark: #1851b4; $color-ultramarine-icon: #3a76f0; $color-ultramarine-light: #6191f3; diff --git a/stylesheets/components/StoryViewer.scss b/stylesheets/components/StoryViewer.scss index ae419de44c28..7f0197c13f34 100644 --- a/stylesheets/components/StoryViewer.scss +++ b/stylesheets/components/StoryViewer.scss @@ -72,7 +72,8 @@ padding: 0 16px; position: absolute; transform: translateX(-50%); - width: 284px; + min-width: 284px; + width: 56.25vh; z-index: $z-index-above-base; &--group-avatar { @@ -93,7 +94,7 @@ } &__caption { - @include font-body-1-bold; + @include font-body-1; color: $color-gray-05; padding: 4px 0; margin-bottom: 24px; @@ -146,4 +147,69 @@ height: 100%; } } + + &__arrow { + align-items: center; + display: flex; + height: 100vh; + position: absolute; + width: 25%; + + button { + @include button-reset; + height: 24px; + opacity: 0; + width: 24px; + transition: opacity 200ms ease-in-out; + } + + &--left { + justify-content: flex-start; + left: 0; + + button { + margin-left: 24px; + @include color-svg( + '../images/icons/v2/chevron-left-24.svg', + $color-white + ); + } + } + + &--right { + justify-content: flex-end; + right: 0; + + button { + margin-right: 24px; + @include color-svg( + '../images/icons/v2/chevron-right-24.svg', + $color-white + ); + } + } + + &--visible button { + opacity: 1; + visibility: visible; + } + } + + &__protection { + position: absolute; + width: 100%; + z-index: $z-index-above-base; + + &--top { + background: linear-gradient($color-black-alpha-16, $color-transparent); + top: 0; + height: 80px; + } + + &--bottom { + background: linear-gradient($color-transparent, $color-black-alpha-40); + bottom: 0; + height: 180px; + } + } } diff --git a/ts/components/StoryViewer.stories.tsx b/ts/components/StoryViewer.stories.tsx index 4811fd02a3a7..f3b9ec51584a 100644 --- a/ts/components/StoryViewer.stories.tsx +++ b/ts/components/StoryViewer.stories.tsx @@ -42,6 +42,7 @@ function getDefaultProps(): PropsType { stories: [ { attachment: fakeAttachment({ + path: 'snow.jpg', url: '/fixtures/snow.jpg', }), messageId: '123', @@ -60,6 +61,7 @@ story.add('Wide story', () => ( stories={[ { attachment: fakeAttachment({ + path: 'file.jpg', url: '/fixtures/nathan-anderson-316188-unsplash.jpg', }), messageId: '123', @@ -89,6 +91,7 @@ story.add('Multi story', () => { stories={[ { attachment: fakeAttachment({ + path: 'snow.jpg', url: '/fixtures/snow.jpg', }), messageId: '123', @@ -97,6 +100,7 @@ story.add('Multi story', () => { }, { attachment: fakeAttachment({ + path: 'file.jpg', url: '/fixtures/nathan-anderson-316188-unsplash.jpg', }), messageId: '456', @@ -115,6 +119,7 @@ story.add('Caption', () => ( { attachment: fakeAttachment({ caption: 'This place looks lovely', + path: 'file.jpg', url: '/fixtures/nathan-anderson-316188-unsplash.jpg', }), messageId: '123', @@ -133,6 +138,7 @@ story.add('Long Caption', () => ( attachment: fakeAttachment({ caption: 'Snowycle, snowycle, snowycle\nI want to ride my snowycle, snowycle, snowycle\nI want to ride my snowycle\nI want to ride my snow\nI want to ride my snowycle\nI want to ride it where I like\nSnowycle, snowycle, snowycle\nI want to ride my snowycle, snowycle, snowycle\nI want to ride my snowycle\nI want to ride my snow\nI want to ride my snowycle\nI want to ride it where I like\nSnowycle, snowycle, snowycle\nI want to ride my snowycle, snowycle, snowycle\nI want to ride my snowycle\nI want to ride my snow\nI want to ride my snowycle\nI want to ride it where I like', + path: 'file.jpg', url: '/fixtures/snow.jpg', }), messageId: '123', diff --git a/ts/components/StoryViewer.tsx b/ts/components/StoryViewer.tsx index a1ed70fea6b5..3f3f42c98ded 100644 --- a/ts/components/StoryViewer.tsx +++ b/ts/components/StoryViewer.tsx @@ -9,6 +9,7 @@ import React, { useRef, useState, } from 'react'; +import classNames from 'classnames'; import { useSpring, animated, to } from '@react-spring/web'; import type { BodyRangeType, LocalizerType } from '../types/Util'; import type { ConversationType } from '../state/ducks/conversations'; @@ -76,6 +77,13 @@ export type PropsType = { const CAPTION_BUFFER = 20; const CAPTION_INITIAL_LENGTH = 200; const CAPTION_MAX_LENGTH = 700; +const MOUSE_IDLE_TIME = 3000; + +enum Arrow { + None, + Left, + Right, +} export const StoryViewer = ({ conversationId, @@ -313,6 +321,38 @@ export const StoryViewer = ({ loadStoryReplies(conversationId, messageId); }, [conversationId, isGroupStory, loadStoryReplies, messageId]); + const [arrowToShow, setArrowToShow] = useState(Arrow.None); + + useEffect(() => { + if (arrowToShow === Arrow.None) { + return; + } + + let lastMouseMove: number | undefined; + + function updateLastMouseMove() { + lastMouseMove = Date.now(); + } + + function checkMouseIdle() { + requestAnimationFrame(() => { + if (lastMouseMove && Date.now() - lastMouseMove > MOUSE_IDLE_TIME) { + setArrowToShow(Arrow.None); + } else { + checkMouseIdle(); + } + }); + } + checkMouseIdle(); + + document.addEventListener('mousemove', updateLastMouseMove); + + return () => { + lastMouseMove = undefined; + document.removeEventListener('mousemove', updateLastMouseMove); + }; + }, [arrowToShow]); + const replies = replyState && replyState.messageId === messageId ? replyState.replies : []; @@ -327,6 +367,22 @@ export const StoryViewer = ({ style={{ background: getStoryBackground(attachment) }} />
+
setArrowToShow(Arrow.Left)} + > +
+
+
setArrowToShow(Arrow.Right)} + > +
+