diff --git a/src/App/App.js b/src/App/App.js index 21ba1132b..6acb6af28 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -6,7 +6,7 @@ const { useTranslation } = require('react-i18next'); const { Router } = require('stremio-router'); const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services'); const { NotFound } = require('stremio/routes'); -const { ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender } = require('stremio/common'); +const { ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender, platform } = require('stremio/common'); const ServicesToaster = require('./ServicesToaster'); const DeepLinkHandler = require('./DeepLinkHandler'); const SearchParamsHandler = require('./SearchParamsHandler'); @@ -52,6 +52,14 @@ const App = () => { window.removeEventListener('hashchange', onLocationHashChange); }; }, []); + React.useEffect(() => { + // on mobile and macOS the scrollbars aren't visible when they're not being used, + // so we imitate this behavior on those platforms, and fallback to a custom scrollbar on other platforms + const osOverlayScrollbar = platform.isMobile() || platform.isMac(); + + if (osOverlayScrollbar) + document.documentElement.classList.add('os-overlay-scrollbar'); + }, []); React.useEffect(() => { const onCoreStateChanged = () => { setInitialized( diff --git a/src/App/styles.less b/src/App/styles.less index f3dfb4b03..cdbf4d8bd 100644 --- a/src/App/styles.less +++ b/src/App/styles.less @@ -58,6 +58,16 @@ box-shadow: none; overflow: hidden; word-break: break-word; + + @supports not selector(::-webkit-scrollbar) { + scrollbar-width: thin; + scrollbar-color: var(--overlay-color) transparent; + } +} + +html:global(.os-overlay-scrollbar) { + // this makes the browser ignore the custom scrollbar styles when it bahves as an overlay on this platform + // and fallbacks to using the OS scrollbar with the given properties when it's not an overlay scrollbar scrollbar-width: thin; scrollbar-color: var(--overlay-color) transparent; } @@ -130,9 +140,12 @@ html { z-index: 1; padding: 0 calc(0.5 * var(--horizontal-nav-bar-size)); overflow-y: auto; - scrollbar-width: none; pointer-events: none; + @supports not selector(::-webkit-scrollbar) { + scrollbar-width: none; + } + &::-webkit-scrollbar { display: none; } diff --git a/src/common/MainNavBars/MainNavBars.js b/src/common/MainNavBars/MainNavBars.js index 542d68fe4..ed42840f7 100644 --- a/src/common/MainNavBars/MainNavBars.js +++ b/src/common/MainNavBars/MainNavBars.js @@ -14,7 +14,7 @@ const TABS = [ { id: 'settings', label: 'SETTINGS', icon: 'settings', href: '#/settings' }, ]; -const MainNavBars = React.memo(({ className, route, query, children }) => { +const MainNavBars = React.memo(({ className, route, query, noOverflow, children }) => { return (
{ selected={route} tabs={TABS} /> -
{children}
+
{children}
); }); @@ -43,7 +43,8 @@ MainNavBars.propTypes = { className: PropTypes.string, route: PropTypes.string, query: PropTypes.string, - children: PropTypes.node + noOverflow: PropTypes.bool, + children: PropTypes.node, }; module.exports = MainNavBars; diff --git a/src/common/MainNavBars/styles.less b/src/common/MainNavBars/styles.less index 8515294db..cfdb6970a 100644 --- a/src/common/MainNavBars/styles.less +++ b/src/common/MainNavBars/styles.less @@ -40,15 +40,27 @@ --top-overlay-size: calc(var(--horizontal-nav-bar-size) + var(--safe-area-inset-top, 0px)); --bottom-overlay-size: calc(var(--vertical-nav-bar-size) + var(--calculated-bottom-safe-inset, 0px)); - --overlap-size: 3rem; - --transparency-grandient-pad: 6rem; + --overlap-size: 6rem; + --transparency-grandient-pad: 2.5rem; mask: linear-gradient( to bottom, transparent calc(var(--top-overlay-size) - var(--overlap-size)), + rgba(0, 0, 0, 0.08) calc(var(--top-overlay-size) - (var(--overlap-size) / 2)), + rgba(0, 0, 0, 0.16) calc(var(--top-overlay-size) - (var(--overlap-size) / 3)), black calc(var(--top-overlay-size) + var(--transparency-grandient-pad)), black 100% ); + overflow-x: hidden; + + &::-webkit-scrollbar-track { + margin-top: var(--top-overlay-size); + } + + &.no-overflow { + overflow: clip; + overflow: hidden; + } } } @@ -61,10 +73,18 @@ mask: linear-gradient( to bottom, transparent calc(var(--top-overlay-size) - var(--overlap-size)), + rgba(0, 0, 0, 0.08) calc(var(--top-overlay-size) - (var(--overlap-size) / 2)), + rgba(0, 0, 0, 0.16) calc(var(--top-overlay-size) - (var(--overlap-size) / 3)), black calc(var(--top-overlay-size) + var(--transparency-grandient-pad)), black calc(100% - var(--bottom-overlay-size) - var(--transparency-grandient-pad)), + rgba(0, 0, 0, 0.16) calc(100% - var(--bottom-overlay-size) + (var(--overlap-size) / 3)), + rgba(0, 0, 0, 0.08) calc(100% - var(--bottom-overlay-size) + (var(--overlap-size) / 2)), transparent calc(100% - var(--bottom-overlay-size) + var(--overlap-size)) ); + + &::-webkit-scrollbar-track { + margin-bottom: var(--bottom-overlay-size); + } } .vertical-nav-bar { diff --git a/src/common/MetaPreview/styles.less b/src/common/MetaPreview/styles.less index 1c40127c1..9315bf8de 100644 --- a/src/common/MetaPreview/styles.less +++ b/src/common/MetaPreview/styles.less @@ -63,7 +63,13 @@ overflow-y: auto; &:not(:hover) { - scrollbar-color: transparent transparent; + @supports not selector(::-webkit-scrollbar-thumb) { + scrollbar-color: transparent transparent; + } + + html:global(.os-overlay-scrollbar) { + scrollbar-color: transparent transparent; + } &::-webkit-scrollbar-thumb, &::-webkit-scrollbar-track { background-color: transparent; @@ -245,7 +251,14 @@ flex-shrink: 0; margin-top: 3rem; overflow: visible; - scrollbar-width: none; + + @supports not selector(::-webkit-scrollbar) { + scrollbar-width: none; + } + + html:global(.os-overlay-scrollbar) { + scrollbar-width: none; + } &::-webkit-scrollbar { display: none; diff --git a/src/common/NavBar/VerticalNavBar/styles.less b/src/common/NavBar/VerticalNavBar/styles.less index 99e59be93..d91d15e70 100644 --- a/src/common/NavBar/VerticalNavBar/styles.less +++ b/src/common/NavBar/VerticalNavBar/styles.less @@ -11,7 +11,14 @@ width: var(--vertical-nav-bar-size); background-color: transparent; overflow-y: auto; - scrollbar-width: none; + + @supports not selector(::-webkit-scrollbar) { + scrollbar-width: none; + } + + html:global(.os-overlay-scrollbar) { + scrollbar-width: none; + } &::-webkit-scrollbar { display: none; diff --git a/src/common/platform.js b/src/common/platform.js index 1e112de6d..2cd13b93a 100644 --- a/src/common/platform.js +++ b/src/common/platform.js @@ -19,10 +19,14 @@ const Bowser = require('bowser'); const browser = Bowser.parse(window.navigator?.userAgent || ''); const name = iOS() ? 'ios' : (browser?.os?.name || 'unknown').toLowerCase(); +const osName = browser?.os?.name || 'unknown'; module.exports = { name, isMobile: () => { return name === 'ios' || name === 'android'; + }, + isMac: () => { + return osName === 'macOS'; } }; diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index c4c99173a..b724cbbe9 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -80,7 +80,7 @@ const Addons = ({ urlParams, queryParams }) => { clearSharedAddon(); }, [urlParams, queryParams]); return ( - +
{selectInputs.map((selectInput, index) => ( diff --git a/src/routes/Addons/styles.less b/src/routes/Addons/styles.less index af4db66ae..c8844177b 100644 --- a/src/routes/Addons/styles.less +++ b/src/routes/Addons/styles.less @@ -30,7 +30,7 @@ --top-overlay-size: var(--selectable-inputs-assumed-height); --bottom-vertical-nav-bar-size: 0px; --bottom-overlay-size: var(--bottom-vertical-nav-bar-size); - --overlap-size: 2.5rem; + --overlap-size: 6rem; --transparency-grandient-pad: 3rem; width: 100%; @@ -50,10 +50,17 @@ mask: linear-gradient( to bottom, transparent calc(var(--top-overlay-size) - var(--overlap-size)), + rgba(0, 0, 0, 0.08) calc(var(--top-overlay-size) - (var(--overlap-size) / 2)), + rgba(0, 0, 0, 0.16) calc(var(--top-overlay-size) - (var(--overlap-size) / 3)), black calc(var(--top-overlay-size) + var(--transparency-grandient-pad)), black 100% ); z-index: 1; + + &::-webkit-scrollbar-track { + margin-top: var(--selectable-inputs-assumed-height); + margin-bottom: var(--bottom-overlay-size); + } } .selectable-inputs-container { diff --git a/src/routes/Discover/Discover.js b/src/routes/Discover/Discover.js index 2e2e90beb..8dfca5671 100644 --- a/src/routes/Discover/Discover.js +++ b/src/routes/Discover/Discover.js @@ -82,7 +82,7 @@ const Discover = ({ urlParams, queryParams }) => { setSelectedMetaItemIndex(0); }, [discover.selected]); return ( - +
diff --git a/src/routes/Discover/styles.less b/src/routes/Discover/styles.less index ea92a4f38..64a1008be 100644 --- a/src/routes/Discover/styles.less +++ b/src/routes/Discover/styles.less @@ -32,7 +32,7 @@ --top-overlay-size: var(--selectable-inputs-assumed-height); --bottom-vertical-nav-bar-size: 0px; --bottom-overlay-size: var(--bottom-vertical-nav-bar-size); - --overlap-size: 3rem; + --overlap-size: 6rem; --transparency-grandient-pad: 6rem; width: 100%; @@ -58,10 +58,17 @@ mask: linear-gradient( to bottom, transparent calc(var(--top-overlay-size) - var(--overlap-size)), + rgba(0, 0, 0, 0.08) calc(var(--top-overlay-size) - (var(--overlap-size) / 2)), + rgba(0, 0, 0, 0.16) calc(var(--top-overlay-size) - (var(--overlap-size) / 3)), black calc(var(--top-overlay-size) + var(--transparency-grandient-pad)), black 100% ); z-index: 1; + + &::-webkit-scrollbar-track { + margin-top: var(--selectable-inputs-assumed-height); + margin-bottom: var(--bottom-overlay-size); + } } .selectable-inputs-container { diff --git a/src/routes/Library/Library.js b/src/routes/Library/Library.js index 8f22a0c00..2242dc5c0 100644 --- a/src/routes/Library/Library.js +++ b/src/routes/Library/Library.js @@ -62,7 +62,7 @@ const Library = ({ model, urlParams, queryParams }) => { } }, [profile.auth, library.selected]); return ( - +
{ model === 'continue_watching' || profile.auth !== null ? diff --git a/src/routes/Library/styles.less b/src/routes/Library/styles.less index a76eb421d..28b73c753 100644 --- a/src/routes/Library/styles.less +++ b/src/routes/Library/styles.less @@ -21,7 +21,7 @@ --top-overlay-size: var(--selectable-inputs-assumed-height); --bottom-vertical-nav-bar-size: 0px; --bottom-overlay-size: var(--bottom-vertical-nav-bar-size); - --overlap-size: 3rem; + --overlap-size: 6rem; --transparency-grandient-pad: 6rem; width: 100%; @@ -40,10 +40,17 @@ mask: linear-gradient( to bottom, transparent calc(var(--top-overlay-size) - var(--overlap-size)), + rgba(0, 0, 0, 0.08) calc(var(--top-overlay-size) - (var(--overlap-size) / 2)), + rgba(0, 0, 0, 0.16) calc(var(--top-overlay-size) - (var(--overlap-size) / 3)), black calc(var(--top-overlay-size) + var(--transparency-grandient-pad)), black 100% ); z-index: 1; + + &::-webkit-scrollbar-track { + margin-top: var(--selectable-inputs-assumed-height); + margin-bottom: var(--bottom-overlay-size); + } } .selectable-inputs-container { diff --git a/src/routes/MetaDetails/StreamsList/styles.less b/src/routes/MetaDetails/StreamsList/styles.less index 6393d5c98..50274d044 100644 --- a/src/routes/MetaDetails/StreamsList/styles.less +++ b/src/routes/MetaDetails/StreamsList/styles.less @@ -204,7 +204,14 @@ .streams-container { margin-top: 0; - scrollbar-color: @color-surface-light5-20 transparent; + + @supports not selector(::-webkit-scrollbar-thumb) { + scrollbar-color: @color-surface-light5-20 transparent; + } + + html:global(.os-overlay-scrollbar) { + scrollbar-color: @color-surface-light5-20 transparent; + } &::-webkit-scrollbar-thumb { background-color: @color-surface-light5-20; diff --git a/src/routes/MetaDetails/styles.less b/src/routes/MetaDetails/styles.less index 36fff8fd7..e0b8ef32e 100644 --- a/src/routes/MetaDetails/styles.less +++ b/src/routes/MetaDetails/styles.less @@ -23,7 +23,7 @@ --navbar-assumed-height: 84px; --top-overlay-size: var(--navbar-assumed-height); - --overlap-size: 3rem; + --overlap-size: 6rem; --transparency-grandient-pad: 6rem; .background-image-layer { @@ -68,10 +68,16 @@ mask: linear-gradient( to bottom, transparent calc(var(--top-overlay-size) - var(--overlap-size)), + rgba(0, 0, 0, 0.08) calc(var(--top-overlay-size) - (var(--overlap-size) / 2)), + rgba(0, 0, 0, 0.16) calc(var(--top-overlay-size) - (var(--overlap-size) / 3)), black calc(var(--top-overlay-size) + var(--transparency-grandient-pad)), black 100% ); + &::-webkit-scrollbar-track { + margin-top: var(--navbar-assumed-height); + } + .vertical-nav-bar { --vertical-nav-bar-size: 6rem; flex: none; diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index f502a2e31..b2066b144 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -193,7 +193,7 @@ const Settings = () => { closeConfigureServerUrlModal(); }, [routeFocused]); return ( - +