diff --git a/package-lock.json b/package-lock.json
index d6618f1b0..be53e9ec7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,18 @@
{
"name": "stremio",
- "version": "5.0.0-beta.15",
+ "version": "5.0.0-beta.16",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "stremio",
- "version": "5.0.0-beta.15",
+ "version": "5.0.0-beta.16",
"license": "gpl-2.0",
"dependencies": {
"@babel/runtime": "7.26.0",
"@sentry/browser": "8.42.0",
"@stremio/stremio-colors": "5.2.0",
- "@stremio/stremio-core-web": "0.48.3",
+ "@stremio/stremio-core-web": "0.48.4",
"@stremio/stremio-icons": "5.4.1",
"@stremio/stremio-video": "0.0.48",
"a-color-picker": "1.2.1",
@@ -3371,9 +3371,10 @@
"integrity": "sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg=="
},
"node_modules/@stremio/stremio-core-web": {
- "version": "0.48.3",
- "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.3.tgz",
- "integrity": "sha512-JL8pOLOEVACYG+33Dtp/mrB2/vuc7RoYZdxX1BQa5MPR8EzsODjpvL5uETmdxo/swgtMZyx2A6/e1B53eKA4oQ==",
+ "version": "0.48.4",
+ "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.4.tgz",
+ "integrity": "sha512-848OLm0dtP75aAlYhUB0KoOqwosJIj+ubB8/abuaAzH/N3dtxs40vu2AezmMpGjwR4V60rlOUkUZeWFvrUOjrw==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "7.24.1"
}
diff --git a/package.json b/package.json
index f79244edd..79593f9d5 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "stremio",
"displayName": "Stremio",
- "version": "5.0.0-beta.15",
+ "version": "5.0.0-beta.16",
"author": "Smart Code OOD",
"private": true,
"license": "gpl-2.0",
@@ -16,7 +16,7 @@
"@babel/runtime": "7.26.0",
"@sentry/browser": "8.42.0",
"@stremio/stremio-colors": "5.2.0",
- "@stremio/stremio-core-web": "0.48.3",
+ "@stremio/stremio-core-web": "0.48.4",
"@stremio/stremio-icons": "5.4.1",
"@stremio/stremio-video": "0.0.48",
"a-color-picker": "1.2.1",
diff --git a/src/App/styles.less b/src/App/styles.less
index edff6e66d..1a1f50dcc 100644
--- a/src/App/styles.less
+++ b/src/App/styles.less
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2023 Smart code 203358507
+// Copyright (C) 2017-2024 Smart code 203358507
@import (reference) '~stremio/common/screen-sizes.less';
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
@@ -13,6 +13,20 @@
@import (once, less) '~stremio-router/styles.css';
}
+// iOS pads the bottom inset more than needed, so we deduce the actual inset size when using the webapp
+@calculated-bottom-safe-inset: ~"min(env(safe-area-inset-bottom, 0rem), max(1rem, calc(100lvh - 100svh - env(safe-area-inset-top, 0rem))))";
+@html-width: ~"calc(max(100svw, 100dvw))";
+@html-height: ~"calc(max(100svh, 100dvh))";
+@safe-area-inset-top: env(safe-area-inset-top, 0rem);
+@safe-area-inset-right: env(safe-area-inset-right, 0rem);
+@safe-area-inset-bottom: env(safe-area-inset-bottom, 0rem);
+@safe-area-inset-left: env(safe-area-inset-left, 0rem);
+
+@top-overlay-size: 5.25rem;
+@bottom-overlay-size: 0rem;
+@overlap-size: 3rem;
+@transparency-grandient-pad: 6rem;
+
:root {
--landscape-shape-ratio: 0.5625;
--poster-shape-ratio: 1.464;
@@ -40,6 +54,15 @@
--modal-background-color: rgba(15, 13, 32, 1);
--outer-glow: 0px 0px 15px rgba(123, 91, 245, 0.37);
--border-radius: 0.75rem;
+ --calculated-bottom-safe-inset: @calculated-bottom-safe-inset;
+ --top-overlay-size: @top-overlay-size;
+ --bottom-overlay-size: @bottom-overlay-size;
+ --overlap-size: @overlap-size;
+ --transparency-grandient-pad: @transparency-grandient-pad;
+ --safe-area-inset-top: @safe-area-inset-top;
+ --safe-area-inset-right: @safe-area-inset-right;
+ --safe-area-inset-bottom: @safe-area-inset-bottom;
+ --safe-area-inset-left: @safe-area-inset-left;
}
* {
@@ -85,13 +108,16 @@ svg {
}
html {
- width: 100%;
- height: 100%;
+ width: @html-width;
+ height: @html-height;
min-width: 640px;
min-height: 480px;
font-family: 'PlusJakartaSans', 'sans-serif';
overflow: auto;
overscroll-behavior: none;
+ user-select: none;
+ touch-action: manipulation;
+ -webkit-tap-highlight-color: transparent;
body {
width: 100%;
@@ -106,9 +132,9 @@ html {
.toasts-container {
position: absolute;
- top: calc(1.2 * var(--horizontal-nav-bar-size));
- right: 0;
- bottom: calc(1.2 * var(--horizontal-nav-bar-size));
+ top: calc(1.2 * var(--horizontal-nav-bar-size) + var(--safe-area-inset-top));
+ right: var(--safe-area-inset-right);
+ bottom: calc(1.2 * var(--horizontal-nav-bar-size) + var(--calculated-bottom-safe-inset, 0rem));
left: auto;
z-index: 1;
padding: 0 calc(0.5 * var(--horizontal-nav-bar-size));
@@ -193,4 +219,10 @@ html {
}
}
}
+}
+
+@media only screen and (max-width: @minimum) {
+ :root {
+ --bottom-overlay-size: 6rem;
+ }
}
\ No newline at end of file
diff --git a/src/components/BottomSheet/BottomSheet.less b/src/components/BottomSheet/BottomSheet.less
index 4ac68ead6..6428e5081 100644
--- a/src/components/BottomSheet/BottomSheet.less
+++ b/src/components/BottomSheet/BottomSheet.less
@@ -23,7 +23,6 @@
opacity: 0.8;
transition: opacity 0.1s ease-out;
cursor: pointer;
- -webkit-tap-highlight-color: transparent;
}
.container {
diff --git a/src/components/Button/Button.less b/src/components/Button/Button.less
index 6baf8b7aa..f6a7bcb4b 100644
--- a/src/components/Button/Button.less
+++ b/src/components/Button/Button.less
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2023 Smart code 203358507
+// Copyright (C) 2017-2024 Smart code 203358507
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
@@ -6,7 +6,6 @@
outline-width: var(--focus-outline-size);
outline-color: @color-surface-light5;
outline-offset: calc(-1 * var(--focus-outline-size));
- -webkit-tap-highlight-color: transparent;
cursor: pointer;
&:focus {
diff --git a/src/components/Chips/Chip/Chip.less b/src/components/Chips/Chip/Chip.less
index b71a7b6b6..4261f35e9 100644
--- a/src/components/Chips/Chip/Chip.less
+++ b/src/components/Chips/Chip/Chip.less
@@ -16,7 +16,6 @@
text-transform: capitalize;
padding: 0 1.75rem;
border-radius: @height;
- -webkit-tap-highlight-color: transparent;
background-color: transparent;
user-select: none;
overflow: hidden;
diff --git a/src/components/MainNavBars/MainNavBars.less b/src/components/MainNavBars/MainNavBars.less
index e76192fbb..2ee57b505 100644
--- a/src/components/MainNavBars/MainNavBars.less
+++ b/src/components/MainNavBars/MainNavBars.less
@@ -1,10 +1,15 @@
-// Copyright (C) 2017-2023 Smart code 203358507
+// Copyright (C) 2017-2024 Smart code 203358507
@import (reference) '~stremio/common/screen-sizes.less';
.main-nav-bars-container {
position: relative;
z-index: 0;
+ overflow: clip;
+ margin-left: env(safe-area-inset-left, 0px);
+ margin-right: env(safe-area-inset-right, 0px);
+ width: calc(100% - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px));
+ height: 100%;
.horizontal-nav-bar {
position: absolute;
@@ -17,18 +22,20 @@
.vertical-nav-bar {
position: absolute;
top: var(--horizontal-nav-bar-size);
- bottom: 0;
+ bottom: var(--calculated-bottom-safe-inset);
left: 0;
z-index: 1;
}
.nav-content-container {
position: absolute;
- top: var(--horizontal-nav-bar-size);
+ padding-top: calc(var(--horizontal-nav-bar-size) + env(safe-area-inset-top, 0px));
+ top: 0;
right: 0;
bottom: 0;
left: var(--vertical-nav-bar-size);
z-index: 0;
+ overflow: scroll;
}
}
@@ -36,7 +43,7 @@
.main-nav-bars-container {
.nav-content-container {
left: 0;
- bottom: var(--vertical-nav-bar-size);
+ padding-bottom: var(--vertical-nav-bar-size);
}
.vertical-nav-bar {
diff --git a/src/components/MetaItem/styles.less b/src/components/MetaItem/styles.less
index 089ef1c21..359525b28 100644
--- a/src/components/MetaItem/styles.less
+++ b/src/components/MetaItem/styles.less
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2023 Smart code 203358507
+// Copyright (C) 2017-2024 Smart code 203358507
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less';
@@ -18,7 +18,6 @@
.meta-item-container {
padding: 1rem;
overflow: visible;
- -webkit-tap-highlight-color: transparent;
&:hover, &:focus, &:global(.active), &:global(.selected) {
outline-style: none;
diff --git a/src/components/MultiselectMenu/Dropdown/Dropdown.tsx b/src/components/MultiselectMenu/Dropdown/Dropdown.tsx
index b58582271..3da75619b 100644
--- a/src/components/MultiselectMenu/Dropdown/Dropdown.tsx
+++ b/src/components/MultiselectMenu/Dropdown/Dropdown.tsx
@@ -1,6 +1,6 @@
// Copyright (C) 2017-2024 Smart code 203358507
-import React from 'react';
+import React, { useRef, useEffect, useCallback } from 'react';
import { Button } from 'stremio/components';
import { useTranslation } from 'react-i18next';
import classNames from 'classnames';
@@ -19,33 +19,57 @@ type Props = {
const Dropdown = ({ level, setLevel, options, onSelect, selectedOption, menuOpen }: Props) => {
const { t } = useTranslation();
+ const optionsRef = useRef(new Map());
+ const containerRef = useRef(null);
- const onBackButtonClick = () => {
+ const handleSetOptionRef = useCallback((value: number) => (node: HTMLButtonElement | null) => {
+ if (node) {
+ optionsRef.current.set(value, node);
+ } else {
+ optionsRef.current.delete(value);
+ }
+ }, []);
+
+ const handleBackClick = useCallback(() => {
setLevel(level - 1);
- };
+ }, [setLevel, level]);
+
+ useEffect(() => {
+ if (menuOpen && selectedOption && containerRef.current) {
+ const selectedNode = optionsRef.current.get(selectedOption.value);
+ if (selectedNode) {
+ selectedNode.scrollIntoView({
+ behavior: 'smooth',
+ block: 'nearest'
+ });
+ }
+ }
+ }, [menuOpen, selectedOption]);
return (
-
- {
- level > 0 ?
-
- : null
+
+ {level > 0 ?
+
+ : null
}
- {
- options
- .filter((option: MultiselectMenuOption) => !option.hidden)
- .map((option: MultiselectMenuOption, index) => (
-
- ))
-
+ {options
+ .filter((option: MultiselectMenuOption) => !option.hidden)
+ .map((option: MultiselectMenuOption) => (
+
+ ))
}
);
diff --git a/src/components/MultiselectMenu/Dropdown/Option/Option.tsx b/src/components/MultiselectMenu/Dropdown/Option/Option.tsx
index 877bebc72..91aa173f7 100644
--- a/src/components/MultiselectMenu/Dropdown/Option/Option.tsx
+++ b/src/components/MultiselectMenu/Dropdown/Option/Option.tsx
@@ -1,6 +1,6 @@
// Copyright (C) 2017-2024 Smart code 203358507
-import React, { useCallback, useMemo } from 'react';
+import React, { useCallback, useMemo, forwardRef } from 'react';
import classNames from 'classnames';
import { Button } from 'stremio/components';
import styles from './Option.less';
@@ -12,7 +12,7 @@ type Props = {
onSelect: (value: number) => void;
};
-const Option = ({ option, selectedOption, onSelect }: Props) => {
+const Option = forwardRef
(({ option, selectedOption, onSelect }, ref) => {
// consider using option.id === selectedOption?.id instead
const selected = useMemo(() => option?.value === selectedOption?.value, [option, selectedOption]);
@@ -22,6 +22,7 @@ const Option = ({ option, selectedOption, onSelect }: Props) => {
return (
);
-};
+});
+
+Option.displayName = 'Option';
export default Option;
diff --git a/src/components/NavBar/HorizontalNavBar/styles.less b/src/components/NavBar/HorizontalNavBar/styles.less
index 2be5ccf35..512788855 100644
--- a/src/components/NavBar/HorizontalNavBar/styles.less
+++ b/src/components/NavBar/HorizontalNavBar/styles.less
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2023 Smart code 203358507
+// Copyright (C) 2017-2024 Smart code 203358507
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less';
@@ -12,6 +12,8 @@
padding-right: 1rem;
background-color: transparent;
overflow: visible;
+ padding-top: var(--safe-area-inset-top);
+ box-sizing: content-box;
.logo-container {
flex: none;
@@ -32,7 +34,7 @@
}
.back-button-container {
- margin-left: 1rem;
+ margin-left: max(0rem, calc(1rem - var(--safe-area-inset-left)));
}
.title {
diff --git a/src/components/NavBar/VerticalNavBar/NavTabButton/styles.less b/src/components/NavBar/VerticalNavBar/NavTabButton/styles.less
index 01fd993e3..99592189d 100644
--- a/src/components/NavBar/VerticalNavBar/NavTabButton/styles.less
+++ b/src/components/NavBar/VerticalNavBar/NavTabButton/styles.less
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2023 Smart code 203358507
+// Copyright (C) 2017-2024 Smart code 203358507
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less';
@@ -11,11 +11,13 @@
background-color: transparent;
border-radius: 0.75rem;
- &:hover {
- background-color: var(--overlay-color);
+ @media (pointer: fine) {
+ &:hover {
+ background-color: var(--overlay-color);
- .label {
- opacity: 0.6;
+ .label {
+ opacity: 0.6;
+ }
}
}
diff --git a/src/components/RadioButton/RadioButton.less b/src/components/RadioButton/RadioButton.less
index 4f3380147..7e414a521 100644
--- a/src/components/RadioButton/RadioButton.less
+++ b/src/components/RadioButton/RadioButton.less
@@ -24,7 +24,6 @@
outline-width: var(--focus-outline-size);
outline-color: @color-surface-light5;
outline-offset: calc(-1 * var(--focus-outline-size));
- -webkit-tap-highlight-color: transparent;
input[type='radio'] {
opacity: 0;
diff --git a/src/index.html b/src/index.html
index 6b9e62fe4..ba8a9e795 100644
--- a/src/index.html
+++ b/src/index.html
@@ -3,7 +3,7 @@
-
+
diff --git a/src/routes/Addons/Addon/Addon.js b/src/routes/Addons/Addon/Addon.js
index 0b1687465..e2182243a 100644
--- a/src/routes/Addons/Addon/Addon.js
+++ b/src/routes/Addons/Addon/Addon.js
@@ -8,19 +8,43 @@ const { default: Icon } = require('@stremio/stremio-icons/react');
const { Button, Image } = require('stremio/components');
const styles = require('./styles');
-const Addon = ({ className, id, name, version, logo, description, types, behaviorHints, installed, onToggle, onConfigure, onShare, dataset }) => {
+const Addon = ({ className, id, name, version, logo, description, types, behaviorHints, installed, onInstall, onUninstall, onConfigure, onOpen, onShare, dataset }) => {
const { t } = useTranslation();
- const toggleButtonOnClick = React.useCallback((event) => {
- if (typeof onToggle === 'function') {
- onToggle({
- type: 'toggle',
+ const onInstallClick = React.useCallback((event) => {
+ event.stopPropagation();
+ if (typeof onInstall === 'function') {
+ onInstall({
+ type: 'install',
nativeEvent: event.nativeEvent,
reactEvent: event,
dataset: dataset
});
}
- }, [onToggle, dataset]);
+ }, [onInstall, dataset]);
+ const onUninstallClick = React.useCallback((event) => {
+ event.stopPropagation();
+ if (typeof onUninstall === 'function') {
+ onUninstall({
+ type: 'uninstall',
+ nativeEvent: event.nativeEvent,
+ reactEvent: event,
+ dataset: dataset
+ });
+ }
+ }, [onUninstall, dataset]);
+ const onOpenClick = React.useCallback((event) => {
+ event.stopPropagation();
+ if (typeof onOpen === 'function') {
+ onOpen({
+ type: 'open',
+ nativeEvent: event.nativeEvent,
+ reactEvent: event,
+ dataset: dataset
+ });
+ }
+ }, [onOpen, dataset]);
const configureButtonOnClick = React.useCallback((event) => {
+ event.stopPropagation();
if (typeof onConfigure === 'function') {
onConfigure({
type: 'configure',
@@ -31,6 +55,7 @@ const Addon = ({ className, id, name, version, logo, description, types, behavio
}
}, [onConfigure, dataset]);
const shareButtonOnClick = React.useCallback((event) => {
+ event.stopPropagation();
if (typeof onShare === 'function') {
onShare({
type: 'share',
@@ -41,20 +66,15 @@ const Addon = ({ className, id, name, version, logo, description, types, behavio
}
}, [onShare, dataset]);
const onKeyDown = React.useCallback((event) => {
- if (event.key === 'Enter' && typeof onToggle === 'function') {
- onToggle({
- type: 'toggle',
- nativeEvent: event.nativeEvent,
- reactEvent: event,
- dataset: dataset
- });
+ if (event.key === 'Enter') {
+ onOpenClick(event);
}
- }, [onToggle, dataset]);
+ }, [onOpenClick]);
const renderLogoFallback = React.useCallback(() => (
), []);
return (
-