From 0dbde3ff19d576c8cd5eff7dc5cfd15743268e23 Mon Sep 17 00:00:00 2001 From: Christiaan Scheermeijer Date: Tue, 22 Jun 2021 13:24:39 +0200 Subject: [PATCH 01/20] fix(project): fix header content shift due to image load --- src/components/Header/Header.tsx | 5 +++-- .../Header/__snapshots__/Header.test.tsx.snap | 12 +---------- .../Layout/__snapshots__/Layout.test.tsx.snap | 12 +---------- src/components/Logo/Logo.test.tsx | 21 ++++++++++++++++++- src/components/Logo/Logo.tsx | 5 +++-- .../Root/__snapshots__/Root.test.tsx.snap | 11 +--------- 6 files changed, 29 insertions(+), 37 deletions(-) diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index ef8ec58ef..1930de5af 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -35,6 +35,7 @@ const Header: React.FC = ({ }) => { const { t } = useTranslation('menu'); const [mobileSearchActive, setMobileSearchActive] = useState(false); + const [logoLoaded, setLogoLoaded] = useState(false); const breakpoint = useBreakpoint(); const headerClassName = classNames(styles.header, styles[headerType], { [styles.mobileSearchActive]: mobileSearchActive && breakpoint <= Breakpoint.sm, @@ -78,11 +79,11 @@ const Header: React.FC = ({ {logoSrc && (
- + setLogoLoaded(true)} />
)}
{searchEnabled ? search : null}
diff --git a/src/components/Header/__snapshots__/Header.test.tsx.snap b/src/components/Header/__snapshots__/Header.test.tsx.snap index 65c232b1a..56da8be4e 100644 --- a/src/components/Header/__snapshots__/Header.test.tsx.snap +++ b/src/components/Header/__snapshots__/Header.test.tsx.snap @@ -39,17 +39,7 @@ exports[`
renders header 1`] = ` + />
- +
); diff --git a/src/screens/Search/Search.tsx b/src/screens/Search/Search.tsx index 1edf034fd..3a243bcd6 100644 --- a/src/screens/Search/Search.tsx +++ b/src/screens/Search/Search.tsx @@ -27,7 +27,7 @@ const Search: React.FC> = ({ }, }) => { const { t } = useTranslation('search'); - const { siteName, searchPlaylist } = useContext(ConfigContext); + const { siteName, searchPlaylist, options } = useContext(ConfigContext); const firstRender = useFirstRender(); const searchQuery = UIStore.useState((s) => s.searchQuery); const { updateSearchQuery } = useSearchQueryUpdater(); @@ -87,7 +87,13 @@ const Search: React.FC> = ({

{t('heading')}

- +
); diff --git a/src/screens/Series/Series.tsx b/src/screens/Series/Series.tsx index e04cbead6..e4bd13e44 100644 --- a/src/screens/Series/Series.tsx +++ b/src/screens/Series/Series.tsx @@ -175,6 +175,7 @@ const Series = ({ isLoading={isLoading} currentCardItem={item} currentCardLabel={t('current_episode')} + enableCardTitles={config.options.shelveTitles} /> diff --git a/src/services/config.service.ts b/src/services/config.service.ts index d69e117b2..cefa93789 100644 --- a/src/services/config.service.ts +++ b/src/services/config.service.ts @@ -10,6 +10,7 @@ import { PersonalShelf } from '../enum/PersonalShelf'; const contentSchema: SchemaOf = object({ playlistId: string().defined(), featured: boolean().notRequired(), + enableText: boolean().notRequired(), }).defined(); const menuSchema: SchemaOf = object().shape({ @@ -69,6 +70,7 @@ const loadConfig = async (configLocation: string) => { const data = await response.json(); addPersonalShelves(data); + addContentDefaultOptions(data); if (data.version) { return parseDeprecatedConfig(data); @@ -85,6 +87,7 @@ const addPersonalShelves = (data: Config) => { if (!data.content.some(({ playlistId }) => playlistId === PersonalShelf.Favorites)) { data.content.push({ playlistId: PersonalShelf.Favorites }); } + if (data.options.enableContinueWatching) { if (!data.content.some(({ playlistId }) => playlistId === PersonalShelf.ContinueWatching)) { data.content.splice(1, 0, { playlistId: PersonalShelf.ContinueWatching }); @@ -92,6 +95,14 @@ const addPersonalShelves = (data: Config) => { } }; +/** + * Add content default options + * @param {Config} data + */ +const addContentDefaultOptions = (data: Config) => { + data.content = data.content.map((content) => Object.assign({ enableText: true, featured: false }, content)); +}; + /** * Serialize deprecated config to v3 config * @param {Config} config diff --git a/types/Config.d.ts b/types/Config.d.ts index a86cb5252..db1b9c4dc 100644 --- a/types/Config.d.ts +++ b/types/Config.d.ts @@ -27,6 +27,7 @@ export type Simple = { export type Content = { playlistId: string; featured?: boolean; + enableText?: boolean; }; export type Menu = { From f0503d891677666a6996da7f9fadb637dffb69c1 Mon Sep 17 00:00:00 2001 From: Christiaan Scheermeijer Date: Thu, 24 Jun 2021 16:20:25 +0200 Subject: [PATCH 18/20] fix(project): fix backgroundColor option not working --- src/providers/ConfigProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/ConfigProvider.tsx b/src/providers/ConfigProvider.tsx index 256952ec0..8da6b241b 100644 --- a/src/providers/ConfigProvider.tsx +++ b/src/providers/ConfigProvider.tsx @@ -72,7 +72,7 @@ const ConfigProvider: FunctionComponent = ({ const root = document.querySelector(':root') as HTMLElement; if (root && backgroundColor) { - root.style.setProperty('--background-color', backgroundColor); + root.style.setProperty('--body-background-color', backgroundColor); root.style.setProperty('--background-contrast-color', calculateContrastColor(backgroundColor)); } From a3d561ae8668bc28159cefb15e2d1c98b79f0701 Mon Sep 17 00:00:00 2001 From: Christiaan Scheermeijer Date: Thu, 24 Jun 2021 21:52:33 +0200 Subject: [PATCH 19/20] chore(home): use preventDefault to prevent vertical scrolling --- src/components/TileDock/TileDock.tsx | 138 ++++++++++++++------------- 1 file changed, 73 insertions(+), 65 deletions(-) diff --git a/src/components/TileDock/TileDock.tsx b/src/components/TileDock/TileDock.tsx index b696aa99f..54472b928 100644 --- a/src/components/TileDock/TileDock.tsx +++ b/src/components/TileDock/TileDock.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames'; -import React, { useLayoutEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; import styles from './TileDock.module.scss'; @@ -73,10 +73,6 @@ const TileDock = ({ const [slideToIndex, setSlideToIndex] = useState(0); const [transform, setTransform] = useState(-100); const [doAnimationReset, setDoAnimationReset] = useState(false); - const [touchPosition, setTouchPosition] = useState({ - x: 0, - y: 0, - } as Position); const frameRef = useRef() as React.MutableRefObject; const tileWidth: number = 100 / tilesToShow; const isMultiPage: boolean = items?.length > tilesToShow; @@ -92,65 +88,84 @@ const TileDock = ({ const showLeftControl: boolean = needControls && !(cycleMode === 'stop' && index === 0); const showRightControl: boolean = needControls && !(cycleMode === 'stop' && index === items.length - tilesToShow); - const slide = (direction: Direction): void => { - const directionFactor = direction === 'right' ? 1 : -1; - let nextIndex: number = index + tilesToShow * directionFactor; + const slide = useCallback( + (direction: Direction): void => { + const directionFactor = direction === 'right' ? 1 : -1; + let nextIndex: number = index + tilesToShow * directionFactor; - if (nextIndex < 0) { - if (cycleMode === 'stop') nextIndex = 0; - if (cycleMode === 'restart') nextIndex = index === 0 ? 0 - tilesToShow : 0; - } - if (nextIndex > items.length - tilesToShow) { - if (cycleMode === 'stop') nextIndex = items.length - tilesToShow; - if (cycleMode === 'restart') nextIndex = index >= items.length - tilesToShow ? items.length : items.length - tilesToShow; - } + if (nextIndex < 0) { + if (cycleMode === 'stop') nextIndex = 0; + if (cycleMode === 'restart') nextIndex = index === 0 ? 0 - tilesToShow : 0; + } + if (nextIndex > items.length - tilesToShow) { + if (cycleMode === 'stop') nextIndex = items.length - tilesToShow; + if (cycleMode === 'restart') nextIndex = index >= items.length - tilesToShow ? items.length : items.length - tilesToShow; + } - const steps: number = Math.abs(index - nextIndex); - const movement: number = steps * tileWidth * (0 - directionFactor); + const steps: number = Math.abs(index - nextIndex); + const movement: number = steps * tileWidth * (0 - directionFactor); - setSlideToIndex(nextIndex); - setTransform(-100 + movement); - if (!animated) setDoAnimationReset(true); - }; + setSlideToIndex(nextIndex); + setTransform(-100 + movement); + if (!animated) setDoAnimationReset(true); + }, + [animated, cycleMode, index, items.length, tileWidth, tilesToShow], + ); - const handleTouchStart = (event: React.TouchEvent): void => { - setTouchPosition({ - x: event.touches[0].clientX, - y: event.touches[0].clientY, - }); - }; + const handleTouchStart = useCallback( + (event: React.TouchEvent): void => { + const touchPosition: Position = { + x: event.touches[0].clientX, + y: event.touches[0].clientY, + }; + + function handleTouchMove(this: HTMLDocument, event: TouchEvent): void { + const newPosition: Position = { + x: event.changedTouches[0].clientX, + y: event.changedTouches[0].clientY, + }; + const movementX: number = Math.abs(newPosition.x - touchPosition.x); + const movementY: number = Math.abs(newPosition.y - touchPosition.y); + + if (movementX > movementY && movementX > 10) { + event.preventDefault(); + event.stopPropagation(); + } + } - const [verticalScrollStopped, setVerticalScrollStopped] = useState(false); + function handleTouchEnd(this: HTMLDocument, event: TouchEvent): void { + const newPosition = { + x: event.changedTouches[0].clientX, + y: event.changedTouches[0].clientY, + }; - const handleTouchMove = (event: React.TouchEvent): void => { - if (verticalScrollStopped) return; + const movementX: number = Math.abs(newPosition.x - touchPosition.x); + const movementY: number = Math.abs(newPosition.y - touchPosition.y); + const direction: Direction = newPosition.x < touchPosition.x ? 'right' : 'left'; - const newPosition = { - x: event.changedTouches[0].clientX, - y: event.changedTouches[0].clientY, - }; - const movementX: number = Math.abs(newPosition.x - touchPosition.x); - const movementY: number = Math.abs(newPosition.y - touchPosition.y); - if (movementX > movementY && movementX > 10) { - setVerticalScrollStopped(true); - document.body.style.overflowY = 'hidden'; - } - }; - const handleTouchEnd = (event: React.TouchEvent): void => { - const newPosition = { - x: event.changedTouches[0].clientX, - y: event.changedTouches[0].clientY, - }; - const movementX: number = Math.abs(newPosition.x - touchPosition.x); - const movementY: number = Math.abs(newPosition.y - touchPosition.y); - const direction: Direction = newPosition.x < touchPosition.x ? 'right' : 'left'; - - setVerticalScrollStopped(false); - document.body.style.overflowY = ''; - if (movementX > minimalTouchMovement && movementX > movementY) { - slide(direction); - } - }; + if (movementX > minimalTouchMovement && movementX > movementY) { + slide(direction); + } + + cleanup(); + } + + function handleTouchCancel() { + cleanup(); + } + + function cleanup() { + document.removeEventListener('touchmove', handleTouchMove); + document.removeEventListener('touchend', handleTouchEnd); + document.removeEventListener('touchcancel', handleTouchCancel); + } + + document.addEventListener('touchmove', handleTouchMove, { passive: false }); + document.addEventListener('touchend', handleTouchEnd); + document.addEventListener('touchcancel', handleTouchCancel); + }, + [minimalTouchMovement, slide], + ); useLayoutEffect(() => { const resetAnimation = (): void => { @@ -195,14 +210,7 @@ const TileDock = ({ return (
{showLeftControl && !!renderLeftControl &&
{renderLeftControl(() => slide('left'))}
} -
    +
      {wrapWithEmptyTiles ? (
    • Date: Thu, 24 Jun 2021 22:02:57 +0200 Subject: [PATCH 20/20] chore(project): fix React unmounted state update warning --- src/components/Animation/Animation.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/Animation/Animation.tsx b/src/components/Animation/Animation.tsx index 7d8ad3ac2..a8966feca 100644 --- a/src/components/Animation/Animation.tsx +++ b/src/components/Animation/Animation.tsx @@ -26,26 +26,31 @@ const Animation = ({ const seconds = duration / 1000; const transition = `transform ${seconds}s ease-out`; // todo: -webkit-transform; - const timeout = useRef(); - const timeout2 = useRef(); + const timeout = useRef(); + const timeout2 = useRef(); useEffect(() => { if (timeout.current) clearTimeout(timeout.current); if (timeout2.current) clearTimeout(timeout2.current); if (open) { setHasOpenedBefore(true); - timeout.current = setTimeout(() => setStatus('opening'), delay); - timeout2.current = setTimeout(() => { + timeout.current = window.setTimeout(() => setStatus('opening'), delay); + timeout2.current = window.setTimeout(() => { setStatus('open'); onOpenAnimationEnd && onOpenAnimationEnd(); }, duration + delay); } else if (hasOpenedBefore) { - timeout.current = setTimeout(() => setStatus('closing'), delay); - timeout2.current = setTimeout(() => { + timeout.current = window.setTimeout(() => setStatus('closing'), delay); + timeout2.current = window.setTimeout(() => { setStatus('closed'); onCloseAnimationEnd && onCloseAnimationEnd(); }, duration + delay); } + + return () => { + clearTimeout(timeout.current); + clearTimeout(timeout2.current); + }; }, [duration, delay, transition, open, onOpenAnimationEnd, onCloseAnimationEnd, hasOpenedBefore, setHasOpenedBefore]); if (!open && status === 'closed') {