diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index cf4a9db50..dee0401ca 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,13 +1,20 @@
name: Build
on:
-
push:
branches:
+ - development
+ tags-ignore:
- '**'
+ pull_request:
+ branches:
+ - development
# Allow manual dispatch in GH
workflow_dispatch:
+permissions:
+ contents: write
+
jobs:
build:
runs-on: ubuntu-latest
@@ -24,7 +31,7 @@ jobs:
run: npm run lint
# Create recursivelly the destiantion dir with
# "--parrents where no error if existing, make parent directories as needed."
- - run: mkdir -p ./build/${{ github.ref_name }}
+ - run: mkdir -p ./build/${{ github.head_ref || github.ref_name }}
- name: Deploy to GitHub Pages
if: ${{ github.actor != 'dependabot[bot]' }}
uses: peaceiris/actions-gh-pages@v4
@@ -33,5 +40,5 @@ jobs:
publish_dir: ./build
# in stremio, we use `feat/features-name` or `fix/this-bug`
# so we need a recursive creation of the destination dir
- destination_dir: ${{ github.ref_name }}
+ destination_dir: ${{ github.head_ref || github.ref_name }}
allow_empty_commit: true
diff --git a/package-lock.json b/package-lock.json
index 76db0e04a..5775eb1a9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,18 @@
{
"name": "stremio",
- "version": "5.0.0-beta.8",
+ "version": "5.0.0-beta.9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "stremio",
- "version": "5.0.0-beta.8",
+ "version": "5.0.0-beta.9",
"license": "gpl-2.0",
"dependencies": {
"@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "5.0.1",
- "@stremio/stremio-core-web": "0.47.2",
+ "@stremio/stremio-core-web": "0.47.8",
"@stremio/stremio-icons": "5.2.0",
"@stremio/stremio-video": "0.0.38",
"a-color-picker": "1.2.1",
@@ -2971,9 +2971,9 @@
"license": "MIT"
},
"node_modules/@stremio/stremio-core-web": {
- "version": "0.47.2",
- "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.47.2.tgz",
- "integrity": "sha512-kJXkshXT5f5go137id9MHrVA7PfHao2pGSxfEBbMDGFCqAVfF4jRFTXmfLC0cS1R+EjYhajUrSsXnEddtb2c7g==",
+ "version": "0.47.8",
+ "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.47.8.tgz",
+ "integrity": "sha512-X5yKSCm5DXR7U6oIO+2kaI1q3TnaWP6df/HFa1RBi/uw+8IYk+FB8GWpryxXyisJTFiUfQgcJDIlHROauaBQkg==",
"dependencies": {
"@babel/runtime": "7.24.1"
}
diff --git a/package.json b/package.json
index bf4b1bd10..6610395a6 100755
--- a/package.json
+++ b/package.json
@@ -1,12 +1,13 @@
{
"name": "stremio",
"displayName": "Stremio",
- "version": "5.0.0-beta.8",
+ "version": "5.0.0-beta.9",
"author": "Smart Code OOD",
"private": true,
"license": "gpl-2.0",
"scripts": {
"start": "webpack serve --mode development",
+ "start-prod": "webpack serve --mode production",
"build": "webpack --mode production",
"test": "jest",
"lint": "eslint src"
@@ -15,7 +16,7 @@
"@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "5.0.1",
- "@stremio/stremio-core-web": "0.47.2",
+ "@stremio/stremio-core-web": "0.47.8",
"@stremio/stremio-icons": "5.2.0",
"@stremio/stremio-video": "0.0.38",
"a-color-picker": "1.2.1",
diff --git a/src/common/AddonDetailsModal/AddonDetailsModal.js b/src/common/AddonDetailsModal/AddonDetailsModal.js
index 085925aac..ffe2671d0 100644
--- a/src/common/AddonDetailsModal/AddonDetailsModal.js
+++ b/src/common/AddonDetailsModal/AddonDetailsModal.js
@@ -105,7 +105,9 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
}
}
:
- addonDetails.remoteAddon !== null && addonDetails.remoteAddon.content.type === 'Ready' ?
+ addonDetails.remoteAddon !== null &&
+ addonDetails.remoteAddon.content.type === 'Ready' &&
+ !addonDetails.remoteAddon.content.content.manifest.behaviorHints.configurationRequired ?
{
className: styles['install-button'],
@@ -131,7 +133,7 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
}
:
null;
- return toggleButton !== null ? configureButton ? [cancelButton, configureButton, toggleButton] : [cancelButton, toggleButton] : [cancelButton];
+ return configureButton && toggleButton ? [cancelButton, configureButton, toggleButton] : configureButton ? [cancelButton, configureButton] : toggleButton ? [cancelButton, toggleButton] : [cancelButton];
}, [addonDetails, onCloseRequest]);
const modalBackground = React.useMemo(() => {
return addonDetails.remoteAddon?.content.type === 'Ready' ? addonDetails.remoteAddon.content.content.manifest.background : null;
diff --git a/src/common/Button/Button.js b/src/common/Button/Button.js
index 9d5ef7c1e..b9afe3217 100644
--- a/src/common/Button/Button.js
+++ b/src/common/Button/Button.js
@@ -6,7 +6,7 @@ const classnames = require('classnames');
const styles = require('./styles');
const { useLongPress } = require('use-long-press');
-const Button = React.forwardRef(({ className, href, disabled, children, onLongPress, ...props }, ref) => {
+const Button = React.forwardRef(({ className, href, disabled, children, onLongPress, onDoubleClick, ...props }, ref) => {
const longPress = useLongPress(onLongPress, { detect: 'pointer' });
const onKeyDown = React.useCallback((event) => {
if (typeof props.onKeyDown === 'function') {
@@ -42,6 +42,7 @@ const Button = React.forwardRef(({ className, href, disabled, children, onLongPr
href,
onKeyDown,
onMouseDown,
+ onDoubleClick,
...longPress()
},
children
@@ -58,6 +59,7 @@ Button.propTypes = {
onKeyDown: PropTypes.func,
onMouseDown: PropTypes.func,
onLongPress: PropTypes.func,
+ onDoubleClick: PropTypes.func
};
module.exports = Button;
diff --git a/src/common/CONSTANTS.js b/src/common/CONSTANTS.js
index aeaa51462..742c138d6 100644
--- a/src/common/CONSTANTS.js
+++ b/src/common/CONSTANTS.js
@@ -44,7 +44,7 @@ const EXTERNAL_PLAYERS = [
{
label: 'EXTERNAL_PLAYER_DISABLED',
value: null,
- platforms: ['ios', 'android', 'windows', 'linux', 'macos'],
+ platforms: ['ios', 'visionos', 'android', 'windows', 'linux', 'macos'],
},
{
label: 'EXTERNAL_PLAYER_ALLOW_CHOOSING',
@@ -54,7 +54,7 @@ const EXTERNAL_PLAYERS = [
{
label: 'VLC',
value: 'vlc',
- platforms: ['ios', 'android'],
+ platforms: ['ios', 'visionos', 'android'],
},
{
label: 'MPV',
@@ -79,12 +79,17 @@ const EXTERNAL_PLAYERS = [
{
label: 'Outplayer',
value: 'outplayer',
- platforms: ['ios'],
+ platforms: ['ios', 'visionos'],
+ },
+ {
+ label: 'Moonplayer (VisionOS)',
+ value: 'moonplayer',
+ platforms: ['visionos'],
},
{
label: 'M3U Playlist',
value: 'm3u',
- platforms: ['ios', 'android', 'windows', 'linux', 'macos'],
+ platforms: ['ios', 'visionos', 'android', 'windows', 'linux', 'macos'],
},
];
diff --git a/src/common/MultiselectMenu/Dropdown/Dropdown.less b/src/common/MultiselectMenu/Dropdown/Dropdown.less
new file mode 100644
index 000000000..1e15419ef
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Dropdown.less
@@ -0,0 +1,30 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+.dropdown {
+ background: var(--modal-background-color);
+ display: none;
+ position: absolute;
+ width: 100%;
+ top: 100%;
+ left: 0;
+ z-index: 10;
+ box-shadow: var(--outer-glow);
+ border-radius: var(--border-radius);
+ overflow: hidden;
+
+ &.open {
+ display: block;
+ }
+
+ .back-button {
+ display: flex;
+ align-items: center;
+ gap: 0 0.5rem;
+ padding: 0.75rem;
+ color: var(--primary-foreground-color);
+
+ .back-button-icon {
+ width: 1.5rem;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/Dropdown.tsx b/src/common/MultiselectMenu/Dropdown/Dropdown.tsx
new file mode 100644
index 000000000..9f82106d1
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Dropdown.tsx
@@ -0,0 +1,55 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import React from 'react';
+import Button from 'stremio/common/Button';
+import { useTranslation } from 'react-i18next';
+import classNames from 'classnames';
+import Option from './Option';
+import Icon from '@stremio/stremio-icons/react';
+import styles from './Dropdown.less';
+
+type Props = {
+ options: MultiselectMenuOption[];
+ selectedOption?: MultiselectMenuOption | null;
+ menuOpen: boolean | (() => void);
+ level: number;
+ setLevel: (level: number) => void;
+ onSelect: (value: number) => void;
+};
+
+const Dropdown = ({ level, setLevel, options, onSelect, selectedOption, menuOpen }: Props) => {
+ const { t } = useTranslation();
+
+ const onBackButtonClick = () => {
+ setLevel(level - 1);
+ };
+
+ return (
+
+ {
+ level > 0 ?
+
+ : null
+ }
+ {
+ options
+ .filter((option: MultiselectMenuOption) => !option.hidden)
+ .map((option: MultiselectMenuOption, index) => (
+
+ ))
+
+
+ }
+
+ );
+};
+
+export default Dropdown;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/Option/Option.less b/src/common/MultiselectMenu/Dropdown/Option/Option.less
new file mode 100644
index 000000000..a0ee1743f
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Option/Option.less
@@ -0,0 +1,29 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+.option {
+ font-size: var(--font-size-normal);
+ color: var(--primary-foreground-color);
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+ padding: 1rem;
+
+ .label {
+ flex: 1;
+ color: var(--primary-foreground-color);
+ }
+
+ .icon {
+ flex: none;
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 100%;
+ margin-left: 1rem;
+ background-color: var(--secondary-accent-color);
+ opacity: 1;
+ }
+
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.15);
+ }
+}
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/Option/Option.tsx b/src/common/MultiselectMenu/Dropdown/Option/Option.tsx
new file mode 100644
index 000000000..c7dbb4ebb
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Option/Option.tsx
@@ -0,0 +1,46 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import React, { useCallback, useMemo } from 'react';
+import classNames from 'classnames';
+import Button from 'stremio/common/Button';
+import styles from './Option.less';
+import Icon from '@stremio/stremio-icons/react';
+
+type Props = {
+ option: MultiselectMenuOption;
+ selectedOption?: MultiselectMenuOption | null;
+ onSelect: (value: number) => void;
+};
+
+const Option = ({ option, selectedOption, onSelect }: Props) => {
+ // consider using option.id === selectedOption?.id instead
+ const selected = useMemo(() => option?.value === selectedOption?.value, [option, selectedOption]);
+
+ const handleClick = useCallback(() => {
+ onSelect(option.value);
+ }, [onSelect, option.value]);
+
+ return (
+
+ );
+};
+
+export default Option;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/Option/index.ts b/src/common/MultiselectMenu/Dropdown/Option/index.ts
new file mode 100644
index 000000000..6004f7754
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Option/index.ts
@@ -0,0 +1,5 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import Option from './Option';
+
+export default Option;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/index.ts b/src/common/MultiselectMenu/Dropdown/index.ts
new file mode 100644
index 000000000..ce3622a25
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/index.ts
@@ -0,0 +1,5 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import Dropdown from './Dropdown';
+
+export default Dropdown;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/MultiselectMenu.less b/src/common/MultiselectMenu/MultiselectMenu.less
new file mode 100644
index 000000000..3c7b81b59
--- /dev/null
+++ b/src/common/MultiselectMenu/MultiselectMenu.less
@@ -0,0 +1,39 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+@border-radius: 2.75rem;
+
+.multiselect-menu {
+ position: relative;
+ min-width: 8.5rem;
+ overflow: visible;
+ border-radius: @border-radius;
+
+ &.disabled {
+ pointer-events: none;
+ opacity: 0.3;
+ }
+
+ .multiselect-button {
+ color: var(--primary-foreground-color);
+ padding: 0.75rem 1.5rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 0 0.5rem;
+ border-radius: @border-radius;
+
+ .icon {
+ width: 1rem;
+ color: var(--primary-foreground-color);
+ opacity: 0.6;
+
+ &.open {
+ transform: rotate(180deg);
+ }
+ }
+ }
+
+ &:hover {
+ background-color: var(--overlay-color);
+ }
+}
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/MultiselectMenu.tsx b/src/common/MultiselectMenu/MultiselectMenu.tsx
new file mode 100644
index 000000000..2bd298752
--- /dev/null
+++ b/src/common/MultiselectMenu/MultiselectMenu.tsx
@@ -0,0 +1,57 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import React from 'react';
+import Button from 'stremio/common/Button';
+import useBinaryState from 'stremio/common/useBinaryState';
+import Dropdown from './Dropdown';
+import classNames from 'classnames';
+import Icon from '@stremio/stremio-icons/react';
+import styles from './MultiselectMenu.less';
+import useOutsideClick from 'stremio/common/useOutsideClick';
+
+type Props = {
+ className?: string,
+ title?: string;
+ options: MultiselectMenuOption[];
+ selectedOption?: MultiselectMenuOption;
+ onSelect: (value: number) => void;
+};
+
+const MultiselectMenu = ({ className, title, options, selectedOption, onSelect }: Props) => {
+ const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false);
+ const multiselectMenuRef = useOutsideClick(() => closeMenu());
+ const [level, setLevel] = React.useState(0);
+
+ const onOptionSelect = (value: number) => {
+ level ? setLevel(level + 1) : onSelect(value), closeMenu();
+ };
+
+ return (
+
+
+ {
+ menuOpen ?
+
+ : null
+ }
+
+ );
+};
+
+export default MultiselectMenu;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/index.ts b/src/common/MultiselectMenu/index.ts
new file mode 100644
index 000000000..e526218cd
--- /dev/null
+++ b/src/common/MultiselectMenu/index.ts
@@ -0,0 +1,5 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import MultiselectMenu from './MultiselectMenu';
+
+export default MultiselectMenu;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/types.d.ts b/src/common/MultiselectMenu/types.d.ts
new file mode 100644
index 000000000..7ed039ddd
--- /dev/null
+++ b/src/common/MultiselectMenu/types.d.ts
@@ -0,0 +1,9 @@
+type MultiselectMenuOption = {
+ id?: number;
+ label: string;
+ value: number;
+ destination?: string;
+ default?: boolean;
+ hidden?: boolean;
+ level?: MultiselectMenuOption[];
+};
\ No newline at end of file
diff --git a/src/common/NavBar/VerticalNavBar/NavTabButton/NavTabButton.js b/src/common/NavBar/VerticalNavBar/NavTabButton/NavTabButton.js
index 7ecef38c6..415f563be 100644
--- a/src/common/NavBar/VerticalNavBar/NavTabButton/NavTabButton.js
+++ b/src/common/NavBar/VerticalNavBar/NavTabButton/NavTabButton.js
@@ -15,8 +15,17 @@ const NavTabButton = ({ className, logo, icon, label, href, selected, onClick })
:
null
), [icon]);
+ const onDoubleClick = () => {
+ const scrollableElements = document.querySelectorAll('div');
+
+ scrollableElements.forEach((element) => {
+ if (element.scrollTop > 0) {
+ element.scrollTop = 0;
+ }
+ });
+ };
return (
-