mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-08-09 00:27:21 +00:00
Merge pull request #707 from Stremio/feat/manage-streaming-urls
Feat: Manage multiple streaming URLs in the Settings
This commit is contained in:
commit
d3dec89ff4
41 changed files with 894 additions and 300 deletions
29
package-lock.json
generated
29
package-lock.json
generated
|
|
@ -12,8 +12,8 @@
|
||||||
"@babel/runtime": "7.16.0",
|
"@babel/runtime": "7.16.0",
|
||||||
"@sentry/browser": "6.13.3",
|
"@sentry/browser": "6.13.3",
|
||||||
"@stremio/stremio-colors": "5.0.1",
|
"@stremio/stremio-colors": "5.0.1",
|
||||||
"@stremio/stremio-core-web": "0.48.0",
|
"@stremio/stremio-core-web": "0.48.1",
|
||||||
"@stremio/stremio-icons": "5.2.0",
|
"@stremio/stremio-icons": "5.4.0",
|
||||||
"@stremio/stremio-video": "0.0.46",
|
"@stremio/stremio-video": "0.0.46",
|
||||||
"a-color-picker": "1.2.1",
|
"a-color-picker": "1.2.1",
|
||||||
"bowser": "2.11.0",
|
"bowser": "2.11.0",
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
"react-i18next": "^12.1.1",
|
"react-i18next": "^12.1.1",
|
||||||
"react-is": "18.2.0",
|
"react-is": "18.2.0",
|
||||||
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
||||||
"stremio-translations": "github:Stremio/stremio-translations#57d66ecc8e2df4e73a613dc5e17123ce62ae63f7",
|
"stremio-translations": "github:Stremio/stremio-translations#f666d9a97cafa5aa150878b5c51a2896b5f4f1b2",
|
||||||
"url": "0.11.0",
|
"url": "0.11.0",
|
||||||
"use-long-press": "^3.1.5"
|
"use-long-press": "^3.1.5"
|
||||||
},
|
},
|
||||||
|
|
@ -3132,9 +3132,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@stremio/stremio-core-web": {
|
"node_modules/@stremio/stremio-core-web": {
|
||||||
"version": "0.48.0",
|
"version": "0.48.1",
|
||||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.0.tgz",
|
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.1.tgz",
|
||||||
"integrity": "sha512-UEVxb5weAIZ22Hz0iNKM8O1QkALcLShG9AyCe1P2WhZhyiridbwE7MtP5itBtLcLm9f/D6UeRrpUWMCS01n18Q==",
|
"integrity": "sha512-bdWxBuuOOC0NdG1Mg60lEhpK7Bw/Ea6D89bRcvIvM3WnJrUpGA4jbx4xWj3KQRM08PM3WWCY9/FzctlWCxFMRg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.24.1"
|
"@babel/runtime": "7.24.1"
|
||||||
}
|
}
|
||||||
|
|
@ -3158,9 +3158,15 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@stremio/stremio-icons": {
|
"node_modules/@stremio/stremio-icons": {
|
||||||
"version": "5.2.0",
|
"version": "5.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-icons/-/stremio-icons-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@stremio/stremio-icons/-/stremio-icons-5.4.0.tgz",
|
||||||
"integrity": "sha512-rABlPBTFF17QcSm/4IizVoE/jh+REt+waqA0RvIxuGjQppXlvj7CalqVvTam0CC2wgY00zNG1v/9kVHUDVzo4Q=="
|
"integrity": "sha512-rRWNER+wLgMjxd6sKT0MMq4lzXDOobY3GNdT3NDeeymBtB/CD0YmYqQuUOyYDjEZ1btIbNaniUOBoPW9d3ZQ8A==",
|
||||||
|
"workspaces": [
|
||||||
|
"react",
|
||||||
|
"react-native",
|
||||||
|
"solid",
|
||||||
|
"angularjs"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@stremio/stremio-video": {
|
"node_modules/@stremio/stremio-video": {
|
||||||
"version": "0.0.46",
|
"version": "0.0.46",
|
||||||
|
|
@ -13732,9 +13738,8 @@
|
||||||
},
|
},
|
||||||
"node_modules/stremio-translations": {
|
"node_modules/stremio-translations": {
|
||||||
"version": "1.44.9",
|
"version": "1.44.9",
|
||||||
"resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#57d66ecc8e2df4e73a613dc5e17123ce62ae63f7",
|
"resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#f666d9a97cafa5aa150878b5c51a2896b5f4f1b2",
|
||||||
"integrity": "sha512-Q3Q++Tx3quu71tgTfS8CEP6CajdGyig92SdtRyGMsLHHkgBgzP9ggYBUHVbKAfXcKUegABIkW8CxMueEw758Xg==",
|
"integrity": "sha512-SzaIGUMqQuMAq58sI9L/RKSs5O4eF8VKPMqnWFddBSg/tZOU9xuNYqjRPKT07cp8MRfzzGQmCKMByozTYfjdIA=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/string_decoder": {
|
"node_modules/string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@
|
||||||
"@babel/runtime": "7.16.0",
|
"@babel/runtime": "7.16.0",
|
||||||
"@sentry/browser": "6.13.3",
|
"@sentry/browser": "6.13.3",
|
||||||
"@stremio/stremio-colors": "5.0.1",
|
"@stremio/stremio-colors": "5.0.1",
|
||||||
"@stremio/stremio-core-web": "0.48.0",
|
"@stremio/stremio-core-web": "0.48.1",
|
||||||
"@stremio/stremio-icons": "5.2.0",
|
"@stremio/stremio-icons": "5.4.0",
|
||||||
"@stremio/stremio-video": "0.0.46",
|
"@stremio/stremio-video": "0.0.46",
|
||||||
"a-color-picker": "1.2.1",
|
"a-color-picker": "1.2.1",
|
||||||
"bowser": "2.11.0",
|
"bowser": "2.11.0",
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
"react-i18next": "^12.1.1",
|
"react-i18next": "^12.1.1",
|
||||||
"react-is": "18.2.0",
|
"react-is": "18.2.0",
|
||||||
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
||||||
"stremio-translations": "github:Stremio/stremio-translations#57d66ecc8e2df4e73a613dc5e17123ce62ae63f7",
|
"stremio-translations": "github:Stremio/stremio-translations#f666d9a97cafa5aa150878b5c51a2896b5f4f1b2",
|
||||||
"url": "0.11.0",
|
"url": "0.11.0",
|
||||||
"use-long-press": "^3.1.5"
|
"use-long-press": "^3.1.5"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (C) 2017-2023 Smart code 203358507
|
// Copyright (C) 2017-2023 Smart code 203358507
|
||||||
|
|
||||||
const CHROMECAST_RECEIVER_APP_ID = '1634F54B';
|
const CHROMECAST_RECEIVER_APP_ID = '1634F54B';
|
||||||
|
const DEFAULT_STREAMING_SERVER_URL = 'http://127.0.0.1:11470/';
|
||||||
const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250];
|
const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250];
|
||||||
const SUBTITLES_FONTS = ['PlusJakartaSans', 'Arial', 'Halvetica', 'Times New Roman', 'Verdana', 'Courier', 'Lucida Console', 'sans-serif', 'serif', 'monospace'];
|
const SUBTITLES_FONTS = ['PlusJakartaSans', 'Arial', 'Halvetica', 'Times New Roman', 'Verdana', 'Courier', 'Lucida Console', 'sans-serif', 'serif', 'monospace'];
|
||||||
const SEEK_TIME_DURATIONS = [3000, 5000, 10000, 15000, 20000, 30000];
|
const SEEK_TIME_DURATIONS = [3000, 5000, 10000, 15000, 20000, 30000];
|
||||||
|
|
@ -97,6 +98,7 @@ const WHITELISTED_HOSTS = ['stremio.com', 'strem.io', 'stremio.zendesk.com', 'go
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
CHROMECAST_RECEIVER_APP_ID,
|
CHROMECAST_RECEIVER_APP_ID,
|
||||||
|
DEFAULT_STREAMING_SERVER_URL,
|
||||||
SUBTITLES_SIZES,
|
SUBTITLES_SIZES,
|
||||||
SUBTITLES_FONTS,
|
SUBTITLES_FONTS,
|
||||||
SEEK_TIME_DURATIONS,
|
SEEK_TIME_DURATIONS,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
// Copyright (C) 2017-2023 Smart code 203358507
|
|
||||||
|
|
||||||
const Checkbox = require('./Checkbox');
|
|
||||||
|
|
||||||
module.exports = Checkbox;
|
|
||||||
|
|
@ -8,13 +8,13 @@ const { useTranslation } = require('react-i18next');
|
||||||
const { default: Icon } = require('@stremio/stremio-icons/react');
|
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
const { useRouteFocused } = require('stremio-router');
|
const { useRouteFocused } = require('stremio-router');
|
||||||
const Button = require('stremio/common/Button');
|
const Button = require('stremio/common/Button');
|
||||||
const TextInput = require('stremio/common/TextInput');
|
|
||||||
const useTorrent = require('stremio/common/useTorrent');
|
const useTorrent = require('stremio/common/useTorrent');
|
||||||
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
|
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
|
||||||
const useSearchHistory = require('./useSearchHistory');
|
const useSearchHistory = require('./useSearchHistory');
|
||||||
const useLocalSearch = require('./useLocalSearch');
|
const useLocalSearch = require('./useLocalSearch');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
const useBinaryState = require('stremio/common/useBinaryState');
|
const useBinaryState = require('stremio/common/useBinaryState');
|
||||||
|
const { default: TextInput } = require('stremio/common/TextInput');
|
||||||
|
|
||||||
const SearchBar = React.memo(({ className, query, active }) => {
|
const SearchBar = React.memo(({ className, query, active }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
|
||||||
66
src/common/RadioButton/RadioButton.less
Normal file
66
src/common/RadioButton/RadioButton.less
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||||
|
|
||||||
|
.radio-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
|
.radio-container {
|
||||||
|
position: relative;
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
border: 3px solid var(--color-placeholder);
|
||||||
|
border-radius: 1rem;
|
||||||
|
background-color: transparent;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
user-select: none;
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
outline-width: var(--focus-outline-size);
|
||||||
|
outline-color: @color-surface-light5;
|
||||||
|
outline-offset: calc(-1 * var(--focus-outline-size));
|
||||||
|
|
||||||
|
input[type='radio'] {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-circle {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
border-radius: 0.675rem;
|
||||||
|
border: 2px solid var(--secondary-background-color);
|
||||||
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
background-color: transparent;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
border-color: var(--color-trakt);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
.inner-circle {
|
||||||
|
background-color: var(--primary-accent-color);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline-style: solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/common/RadioButton/RadioButton.tsx
Normal file
58
src/common/RadioButton/RadioButton.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import React, { useCallback, ChangeEvent, KeyboardEvent } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import styles from './RadioButton.less';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
disabled?: boolean;
|
||||||
|
selected?: boolean;
|
||||||
|
className?: string;
|
||||||
|
onChange?: (checked: boolean) => void;
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RadioButton = ({ disabled, selected, className, onChange, error }: Props) => {
|
||||||
|
|
||||||
|
const handleSelect = useCallback(({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (!disabled && onChange) {
|
||||||
|
onChange(target.checked);
|
||||||
|
}
|
||||||
|
}, [disabled, onChange]);
|
||||||
|
|
||||||
|
const onKeyDown = useCallback(({ key }: KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if ((key === 'Enter' || key === ' ') && !disabled) {
|
||||||
|
onChange && onChange(!selected);
|
||||||
|
}
|
||||||
|
}, [disabled, selected, onChange]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles['radio-button'], className)}>
|
||||||
|
<label>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
styles['radio-container'],
|
||||||
|
{ [styles['selected']]: selected },
|
||||||
|
{ [styles['disabled']]: disabled },
|
||||||
|
{ [styles['error']]: error }
|
||||||
|
)}
|
||||||
|
role={'radio'}
|
||||||
|
tabIndex={disabled ? -1 : 0}
|
||||||
|
aria-checked={selected}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type={'radio'}
|
||||||
|
checked={selected}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={handleSelect}
|
||||||
|
className={styles['input']}
|
||||||
|
/>
|
||||||
|
<span className={styles['inner-circle']} />
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RadioButton;
|
||||||
5
src/common/RadioButton/index.ts
Normal file
5
src/common/RadioButton/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import RadioButton from './RadioButton';
|
||||||
|
|
||||||
|
export default RadioButton;
|
||||||
|
|
@ -4,7 +4,7 @@ const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const classnames = require('classnames');
|
const classnames = require('classnames');
|
||||||
const { default: Icon } = require('@stremio/stremio-icons/react');
|
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
const TextInput = require('stremio/common/TextInput');
|
const { default: TextInput } = require('../TextInput');
|
||||||
const SearchBarPlaceholder = require('./SearchBarPlaceholder');
|
const SearchBarPlaceholder = require('./SearchBarPlaceholder');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
const { useRouteFocused } = require('stremio-router');
|
const { useRouteFocused } = require('stremio-router');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const useToast = require('stremio/common/Toast/useToast');
|
const useToast = require('stremio/common/Toast/useToast');
|
||||||
|
const { default: TextInput } = require('../TextInput');
|
||||||
const Button = require('stremio/common/Button');
|
const Button = require('stremio/common/Button');
|
||||||
const TextInput = require('stremio/common/TextInput');
|
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
||||||
const SharePrompt = ({ className, url }) => {
|
const SharePrompt = ({ className, url }) => {
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// Copyright (C) 2017-2023 Smart code 203358507
|
|
||||||
|
|
||||||
const React = require('react');
|
|
||||||
const PropTypes = require('prop-types');
|
|
||||||
const classnames = require('classnames');
|
|
||||||
const styles = require('./styles');
|
|
||||||
|
|
||||||
const TextInput = React.forwardRef((props, ref) => {
|
|
||||||
const onKeyDown = React.useCallback((event) => {
|
|
||||||
if (typeof props.onKeyDown === 'function') {
|
|
||||||
props.onKeyDown(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === 'Enter' && !event.nativeEvent.submitPrevented && typeof props.onSubmit === 'function') {
|
|
||||||
props.onSubmit(event);
|
|
||||||
}
|
|
||||||
}, [props.onKeyDown, props.onSubmit]);
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
size={1}
|
|
||||||
autoCorrect={'off'}
|
|
||||||
autoCapitalize={'off'}
|
|
||||||
autoComplete={'off'}
|
|
||||||
spellCheck={false}
|
|
||||||
tabIndex={0}
|
|
||||||
{...props}
|
|
||||||
ref={ref}
|
|
||||||
className={classnames(props.className, styles['text-input'], { 'disabled': props.disabled })}
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
TextInput.displayName = 'TextInput';
|
|
||||||
|
|
||||||
TextInput.propTypes = {
|
|
||||||
className: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
onKeyDown: PropTypes.func,
|
|
||||||
onSubmit: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = TextInput;
|
|
||||||
49
src/common/TextInput/TextInput.tsx
Normal file
49
src/common/TextInput/TextInput.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import styles from './styles.less';
|
||||||
|
|
||||||
|
type Props = React.InputHTMLAttributes<HTMLInputElement> & {
|
||||||
|
className?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||||
|
onSubmit?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TextInput = React.forwardRef<HTMLInputElement, Props>((props, ref) => {
|
||||||
|
const { onSubmit, className, disabled, ...rest } = props;
|
||||||
|
|
||||||
|
const onKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (typeof props.onKeyDown === 'function') {
|
||||||
|
props.onKeyDown(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.key === 'Enter' &&
|
||||||
|
!(event.nativeEvent as any).submitPrevented &&
|
||||||
|
typeof onSubmit === 'function'
|
||||||
|
) {
|
||||||
|
onSubmit(event);
|
||||||
|
}
|
||||||
|
}, [props.onKeyDown, onSubmit]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
size={1}
|
||||||
|
autoCorrect={'off'}
|
||||||
|
autoCapitalize={'off'}
|
||||||
|
autoComplete={'off'}
|
||||||
|
spellCheck={false}
|
||||||
|
tabIndex={0}
|
||||||
|
ref={ref}
|
||||||
|
className={classnames(className, styles['text-input'], { disabled })}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
TextInput.displayName = 'TextInput';
|
||||||
|
|
||||||
|
export default TextInput;
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
// Copyright (C) 2017-2023 Smart code 203358507
|
|
||||||
|
|
||||||
const TextInput = require('./TextInput');
|
|
||||||
|
|
||||||
module.exports = TextInput;
|
|
||||||
5
src/common/TextInput/index.ts
Normal file
5
src/common/TextInput/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import TextInput from './TextInput';
|
||||||
|
|
||||||
|
export default TextInput;
|
||||||
|
|
@ -6,21 +6,21 @@ const classnames = require('classnames');
|
||||||
const Button = require('stremio/common/Button');
|
const Button = require('stremio/common/Button');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
||||||
const Checkbox = React.forwardRef(({ className, checked, children, ...props }, ref) => {
|
const Toggle = React.forwardRef(({ className, checked, children, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<Button {...props} ref={ref} className={classnames(className, styles['checkbox-container'], { 'checked': checked })}>
|
<Button {...props} ref={ref} className={classnames(className, styles['toggle-container'], { 'checked': checked })}>
|
||||||
<div className={styles['toggle']} />
|
<div className={styles['toggle']} />
|
||||||
{children}
|
{children}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Checkbox.displayName = 'Checkbox';
|
Toggle.displayName = 'Toggle';
|
||||||
|
|
||||||
Checkbox.propTypes = {
|
Toggle.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
checked: PropTypes.bool,
|
checked: PropTypes.bool,
|
||||||
children: PropTypes.node
|
children: PropTypes.node
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Checkbox;
|
module.exports = Toggle;
|
||||||
5
src/common/Toggle/index.js
Normal file
5
src/common/Toggle/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright (C) 2017-2023 Smart code 203358507
|
||||||
|
|
||||||
|
const Toggle = require('./Toggle');
|
||||||
|
|
||||||
|
module.exports = Toggle;
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
@thumb-size: calc(@height - @thumb-margin);
|
@thumb-size: calc(@height - @thumb-margin);
|
||||||
|
|
||||||
.checkbox-container {
|
.toggle-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.toggle {
|
.toggle {
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
const AddonDetailsModal = require('./AddonDetailsModal');
|
const AddonDetailsModal = require('./AddonDetailsModal');
|
||||||
const { default: BottomSheet } = require('./BottomSheet');
|
const { default: BottomSheet } = require('./BottomSheet');
|
||||||
const Button = require('./Button');
|
const Button = require('./Button');
|
||||||
const Checkbox = require('./Checkbox');
|
const Toggle = require('./Toggle');
|
||||||
const { default: Chips } = require('./Chips');
|
const { default: Chips } = require('./Chips');
|
||||||
const ColorInput = require('./ColorInput');
|
const ColorInput = require('./ColorInput');
|
||||||
const ContinueWatchingItem = require('./ContinueWatchingItem');
|
const ContinueWatchingItem = require('./ContinueWatchingItem');
|
||||||
|
|
@ -26,7 +26,7 @@ const SearchBar = require('./SearchBar');
|
||||||
const StreamingServerWarning = require('./StreamingServerWarning');
|
const StreamingServerWarning = require('./StreamingServerWarning');
|
||||||
const SharePrompt = require('./SharePrompt');
|
const SharePrompt = require('./SharePrompt');
|
||||||
const Slider = require('./Slider');
|
const Slider = require('./Slider');
|
||||||
const TextInput = require('./TextInput');
|
const { default: TextInput } = require('./TextInput');
|
||||||
const { ToastProvider, useToast } = require('./Toast');
|
const { ToastProvider, useToast } = require('./Toast');
|
||||||
const { TooltipProvider, Tooltip } = require('./Tooltips');
|
const { TooltipProvider, Tooltip } = require('./Tooltips');
|
||||||
const comparatorWithPriorities = require('./comparatorWithPriorities');
|
const comparatorWithPriorities = require('./comparatorWithPriorities');
|
||||||
|
|
@ -53,7 +53,7 @@ module.exports = {
|
||||||
AddonDetailsModal,
|
AddonDetailsModal,
|
||||||
BottomSheet,
|
BottomSheet,
|
||||||
Button,
|
Button,
|
||||||
Checkbox,
|
Toggle,
|
||||||
Chips,
|
Chips,
|
||||||
ColorInput,
|
ColorInput,
|
||||||
ContinueWatchingItem,
|
ContinueWatchingItem,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
// Copyright (C) 2017-2023 Smart code 203358507
|
|
||||||
|
|
||||||
const ConsentCheckbox = require('./ConsentCheckbox');
|
|
||||||
|
|
||||||
module.exports = ConsentCheckbox;
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const classnames = require('classnames');
|
const classnames = require('classnames');
|
||||||
const { Button, Checkbox } = require('stremio/common');
|
const { Button, Toggle } = require('stremio/common');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
||||||
const ConsentCheckbox = React.forwardRef(({ className, label, link, href, onToggle, ...props }, ref) => {
|
const ConsentToggle = React.forwardRef(({ className, label, link, href, onToggle, ...props }, ref) => {
|
||||||
const checkboxOnClick = React.useCallback((event) => {
|
const toggleOnClick = React.useCallback((event) => {
|
||||||
if (typeof props.onClick === 'function') {
|
if (typeof props.onClick === 'function') {
|
||||||
props.onClick(event);
|
props.onClick(event);
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ const ConsentCheckbox = React.forwardRef(({ className, label, link, href, onTogg
|
||||||
event.nativeEvent.togglePrevented = true;
|
event.nativeEvent.togglePrevented = true;
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Checkbox {...props} ref={ref} className={classnames(className, styles['consent-checkbox-container'])} onClick={checkboxOnClick}>
|
<Toggle {...props} ref={ref} className={classnames(className, styles['consent-toggle-container'])} onClick={toggleOnClick}>
|
||||||
<div className={styles['label']}>
|
<div className={styles['label']}>
|
||||||
{label}
|
{label}
|
||||||
{' '}
|
{' '}
|
||||||
|
|
@ -37,13 +37,13 @@ const ConsentCheckbox = React.forwardRef(({ className, label, link, href, onTogg
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</Checkbox>
|
</Toggle>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ConsentCheckbox.displayName = 'ConsentCheckbox';
|
ConsentToggle.displayName = 'ConsentToggle';
|
||||||
|
|
||||||
ConsentCheckbox.propTypes = {
|
ConsentToggle.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
checked: PropTypes.bool,
|
checked: PropTypes.bool,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
|
|
@ -53,4 +53,4 @@ ConsentCheckbox.propTypes = {
|
||||||
onClick: PropTypes.func
|
onClick: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = ConsentCheckbox;
|
module.exports = ConsentToggle;
|
||||||
5
src/routes/Intro/ConsentToggle/index.js
Normal file
5
src/routes/Intro/ConsentToggle/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright (C) 2017-2023 Smart code 203358507
|
||||||
|
|
||||||
|
const ConsentToggle = require('./ConsentToggle');
|
||||||
|
|
||||||
|
module.exports = ConsentToggle;
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||||
|
|
||||||
:import('~stremio/common/Checkbox/styles.less') {
|
:import('~stremio/common/Toggle/styles.less') {
|
||||||
checkbox-icon: icon;
|
checkbox-icon: icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
.consent-checkbox-container {
|
.consent-toggle-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const { TextInput } = require('stremio/common');
|
const { default: TextInput } = require('stremio/common/TextInput');
|
||||||
|
|
||||||
const CredentialsTextInput = React.forwardRef((props, ref) => {
|
const CredentialsTextInput = React.forwardRef((props, ref) => {
|
||||||
const onKeyDown = React.useCallback((event) => {
|
const onKeyDown = React.useCallback((event) => {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ const { Modal, useRouteFocused } = require('stremio-router');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const { Button, Image, useBinaryState } = require('stremio/common');
|
const { Button, Image, useBinaryState } = require('stremio/common');
|
||||||
const CredentialsTextInput = require('./CredentialsTextInput');
|
const CredentialsTextInput = require('./CredentialsTextInput');
|
||||||
const ConsentCheckbox = require('./ConsentCheckbox');
|
const ConsentToggle = require('./ConsentToggle');
|
||||||
const PasswordResetModal = require('./PasswordResetModal');
|
const PasswordResetModal = require('./PasswordResetModal');
|
||||||
const useFacebookLogin = require('./useFacebookLogin');
|
const useFacebookLogin = require('./useFacebookLogin');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
@ -307,27 +307,27 @@ const Intro = ({ queryParams }) => {
|
||||||
onChange={confirmPasswordOnChange}
|
onChange={confirmPasswordOnChange}
|
||||||
onSubmit={confirmPasswordOnSubmit}
|
onSubmit={confirmPasswordOnSubmit}
|
||||||
/>
|
/>
|
||||||
<ConsentCheckbox
|
<ConsentToggle
|
||||||
ref={termsRef}
|
ref={termsRef}
|
||||||
className={styles['consent-checkbox']}
|
className={styles['consent-toggle']}
|
||||||
label={'I have read and agree with the Stremio'}
|
label={'I have read and agree with the Stremio'}
|
||||||
link={'Terms and conditions'}
|
link={'Terms and conditions'}
|
||||||
href={'https://www.stremio.com/tos'}
|
href={'https://www.stremio.com/tos'}
|
||||||
checked={state.termsAccepted}
|
checked={state.termsAccepted}
|
||||||
onToggle={toggleTermsAccepted}
|
onToggle={toggleTermsAccepted}
|
||||||
/>
|
/>
|
||||||
<ConsentCheckbox
|
<ConsentToggle
|
||||||
ref={privacyPolicyRef}
|
ref={privacyPolicyRef}
|
||||||
className={styles['consent-checkbox']}
|
className={styles['consent-toggle']}
|
||||||
label={'I have read and agree with the Stremio'}
|
label={'I have read and agree with the Stremio'}
|
||||||
link={'Privacy Policy'}
|
link={'Privacy Policy'}
|
||||||
href={'https://www.stremio.com/privacy'}
|
href={'https://www.stremio.com/privacy'}
|
||||||
checked={state.privacyPolicyAccepted}
|
checked={state.privacyPolicyAccepted}
|
||||||
onToggle={togglePrivacyPolicyAccepted}
|
onToggle={togglePrivacyPolicyAccepted}
|
||||||
/>
|
/>
|
||||||
<ConsentCheckbox
|
<ConsentToggle
|
||||||
ref={marketingRef}
|
ref={marketingRef}
|
||||||
className={styles['consent-checkbox']}
|
className={styles['consent-toggle']}
|
||||||
label={'I agree to receive marketing communications from Stremio'}
|
label={'I agree to receive marketing communications from Stremio'}
|
||||||
checked={state.marketingAccepted}
|
checked={state.marketingAccepted}
|
||||||
onToggle={toggleMarketingAccepted}
|
onToggle={toggleMarketingAccepted}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const classnames = require('classnames');
|
const classnames = require('classnames');
|
||||||
const { t } = require('i18next');
|
const { t } = require('i18next');
|
||||||
const { Image, SearchBar, Checkbox } = require('stremio/common');
|
const { Image, SearchBar, Toggle } = require('stremio/common');
|
||||||
const SeasonsBar = require('./SeasonsBar');
|
const SeasonsBar = require('./SeasonsBar');
|
||||||
const Video = require('./Video');
|
const Video = require('./Video');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
@ -84,9 +84,9 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect,
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{
|
{
|
||||||
showNotificationsToggle && libraryItem ?
|
showNotificationsToggle && libraryItem ?
|
||||||
<Checkbox className={styles['notifications-checkbox']} checked={!libraryItem.state.noNotif} onClick={toggleNotifications}>
|
<Toggle className={styles['notifications-toggle']} checked={!libraryItem.state.noNotif} onClick={toggleNotifications}>
|
||||||
{t('DETAIL_RECEIVE_NOTIF_SERIES')}
|
{t('DETAIL_RECEIVE_NOTIF_SERIES')}
|
||||||
</Checkbox>
|
</Toggle>
|
||||||
:
|
:
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notifications-checkbox {
|
.notifications-toggle {
|
||||||
flex: none;
|
flex: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ const ControlBar = ({
|
||||||
}) => {
|
}) => {
|
||||||
const { chromecast } = useServices();
|
const { chromecast } = useServices();
|
||||||
const [chromecastServiceActive, setChromecastServiceActive] = React.useState(() => chromecast.active);
|
const [chromecastServiceActive, setChromecastServiceActive] = React.useState(() => chromecast.active);
|
||||||
const [buttonsMenuOpen, , , toogleButtonsMenu] = useBinaryState(false);
|
const [buttonsMenuOpen, , , toggleButtonsMenu] = useBinaryState(false);
|
||||||
const onSubtitlesButtonMouseDown = React.useCallback((event) => {
|
const onSubtitlesButtonMouseDown = React.useCallback((event) => {
|
||||||
event.nativeEvent.subtitlesMenuClosePrevented = true;
|
event.nativeEvent.subtitlesMenuClosePrevented = true;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -141,7 +141,7 @@ const ControlBar = ({
|
||||||
onVolumeChangeRequested={onVolumeChangeRequested}
|
onVolumeChangeRequested={onVolumeChangeRequested}
|
||||||
/>
|
/>
|
||||||
<div className={styles['spacing']} />
|
<div className={styles['spacing']} />
|
||||||
<Button className={styles['control-bar-buttons-menu-button']} onClick={toogleButtonsMenu}>
|
<Button className={styles['control-bar-buttons-menu-button']} onClick={toggleButtonsMenu}>
|
||||||
<Icon className={styles['icon']} name={'more-vertical'} />
|
<Icon className={styles['icon']} name={'more-vertical'} />
|
||||||
</Button>
|
</Button>
|
||||||
<div className={classnames(styles['control-bar-buttons-menu-container'], { 'open': buttonsMenuOpen })}>
|
<div className={classnames(styles['control-bar-buttons-menu-container'], { 'open': buttonsMenuOpen })}>
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,12 @@ const { useTranslation } = require('react-i18next');
|
||||||
const { default: Icon } = require('@stremio/stremio-icons/react');
|
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
const { useRouteFocused } = require('stremio-router');
|
const { useRouteFocused } = require('stremio-router');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const { Button, Checkbox, MainNavBars, Multiselect, ColorInput, TextInput, ModalDialog, useProfile, usePlatform, useStreamingServer, useBinaryState, withCoreSuspender, useToast } = require('stremio/common');
|
const { Button, Toggle, MainNavBars, Multiselect, ColorInput, useProfile, usePlatform, useStreamingServer, withCoreSuspender, useToast } = require('stremio/common');
|
||||||
const useProfileSettingsInputs = require('./useProfileSettingsInputs');
|
const useProfileSettingsInputs = require('./useProfileSettingsInputs');
|
||||||
const useStreamingServerSettingsInputs = require('./useStreamingServerSettingsInputs');
|
const useStreamingServerSettingsInputs = require('./useStreamingServerSettingsInputs');
|
||||||
const useDataExport = require('./useDataExport');
|
const useDataExport = require('./useDataExport');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
const { default: URLsManager } = require('./URLsManager/URLsManager');
|
||||||
|
|
||||||
const GENERAL_SECTION = 'general';
|
const GENERAL_SECTION = 'general';
|
||||||
const PLAYER_SECTION = 'player';
|
const PLAYER_SECTION = 'player';
|
||||||
|
|
@ -35,16 +36,15 @@ const Settings = () => {
|
||||||
subtitlesBackgroundColorInput,
|
subtitlesBackgroundColorInput,
|
||||||
subtitlesOutlineColorInput,
|
subtitlesOutlineColorInput,
|
||||||
audioLanguageSelect,
|
audioLanguageSelect,
|
||||||
surroundSoundCheckbox,
|
surroundSoundToggle,
|
||||||
seekTimeDurationSelect,
|
seekTimeDurationSelect,
|
||||||
seekShortTimeDurationSelect,
|
seekShortTimeDurationSelect,
|
||||||
escExitFullscreenCheckbox,
|
escExitFullscreenToggle,
|
||||||
playInExternalPlayerSelect,
|
playInExternalPlayerSelect,
|
||||||
nextVideoPopupDurationSelect,
|
nextVideoPopupDurationSelect,
|
||||||
bingeWatchingCheckbox,
|
bingeWatchingToggle,
|
||||||
playInBackgroundCheckbox,
|
playInBackgroundToggle,
|
||||||
hardwareDecodingCheckbox,
|
hardwareDecodingToggle,
|
||||||
streamingServerUrlInput
|
|
||||||
} = useProfileSettingsInputs(profile);
|
} = useProfileSettingsInputs(profile);
|
||||||
const {
|
const {
|
||||||
streamingServerRemoteUrlInput,
|
streamingServerRemoteUrlInput,
|
||||||
|
|
@ -53,34 +53,11 @@ const Settings = () => {
|
||||||
torrentProfileSelect,
|
torrentProfileSelect,
|
||||||
transcodingProfileSelect,
|
transcodingProfileSelect,
|
||||||
} = useStreamingServerSettingsInputs(streamingServer);
|
} = useStreamingServerSettingsInputs(streamingServer);
|
||||||
const [configureServerUrlModalOpen, openConfigureServerUrlModal, closeConfigureServerUrlModal] = useBinaryState(false);
|
|
||||||
const configureServerUrlInputRef = React.useRef(null);
|
|
||||||
const configureServerUrlOnSubmit = React.useCallback(() => {
|
|
||||||
streamingServerUrlInput.onChange(configureServerUrlInputRef.current.value);
|
|
||||||
closeConfigureServerUrlModal();
|
|
||||||
}, [streamingServerUrlInput]);
|
|
||||||
const [traktAuthStarted, setTraktAuthStarted] = React.useState(false);
|
const [traktAuthStarted, setTraktAuthStarted] = React.useState(false);
|
||||||
const isTraktAuthenticated = React.useMemo(() => {
|
const isTraktAuthenticated = React.useMemo(() => {
|
||||||
return profile.auth !== null && profile.auth.user !== null && profile.auth.user.trakt !== null &&
|
return profile.auth !== null && profile.auth.user !== null && profile.auth.user.trakt !== null &&
|
||||||
(Date.now() / 1000) < (profile.auth.user.trakt.created_at + profile.auth.user.trakt.expires_in);
|
(Date.now() / 1000) < (profile.auth.user.trakt.created_at + profile.auth.user.trakt.expires_in);
|
||||||
}, [profile.auth]);
|
}, [profile.auth]);
|
||||||
const configureServerUrlModalButtons = React.useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
className: styles['cancel-button'],
|
|
||||||
label: 'Cancel',
|
|
||||||
props: {
|
|
||||||
onClick: closeConfigureServerUrlModal
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Submit',
|
|
||||||
props: {
|
|
||||||
onClick: configureServerUrlOnSubmit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}, [configureServerUrlOnSubmit]);
|
|
||||||
const logoutButtonOnClick = React.useCallback(() => {
|
const logoutButtonOnClick = React.useCallback(() => {
|
||||||
core.transport.dispatch({
|
core.transport.dispatch({
|
||||||
action: 'Ctx',
|
action: 'Ctx',
|
||||||
|
|
@ -118,14 +95,6 @@ const Settings = () => {
|
||||||
const exportDataOnClick = React.useCallback(() => {
|
const exportDataOnClick = React.useCallback(() => {
|
||||||
loadDataExport();
|
loadDataExport();
|
||||||
}, []);
|
}, []);
|
||||||
const reloadStreamingServer = React.useCallback(() => {
|
|
||||||
core.transport.dispatch({
|
|
||||||
action: 'StreamingServer',
|
|
||||||
args: {
|
|
||||||
action: 'Reload'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
const onCopyRemoteUrlClick = React.useCallback(() => {
|
const onCopyRemoteUrlClick = React.useCallback(() => {
|
||||||
if (streamingServer.remoteUrl) {
|
if (streamingServer.remoteUrl) {
|
||||||
navigator.clipboard.writeText(streamingServer.remoteUrl);
|
navigator.clipboard.writeText(streamingServer.remoteUrl);
|
||||||
|
|
@ -192,7 +161,6 @@ const Settings = () => {
|
||||||
if (routeFocused) {
|
if (routeFocused) {
|
||||||
updateSelectedSectionId();
|
updateSelectedSectionId();
|
||||||
}
|
}
|
||||||
closeConfigureServerUrlModal();
|
|
||||||
}, [routeFocused]);
|
}, [routeFocused]);
|
||||||
return (
|
return (
|
||||||
<MainNavBars className={styles['settings-container']} route={'settings'}>
|
<MainNavBars className={styles['settings-container']} route={'settings'}>
|
||||||
|
|
@ -369,9 +337,9 @@ const Settings = () => {
|
||||||
<div className={styles['option-name-container']}>
|
<div className={styles['option-name-container']}>
|
||||||
<div className={styles['label']}>{ t('SETTINGS_FULLSCREEN_EXIT') }</div>
|
<div className={styles['label']}>{ t('SETTINGS_FULLSCREEN_EXIT') }</div>
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<Toggle
|
||||||
className={classnames(styles['option-input-container'], styles['checkbox-container'])}
|
className={classnames(styles['option-input-container'], styles['toggle-container'])}
|
||||||
{...escExitFullscreenCheckbox}
|
{...escExitFullscreenToggle}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
|
|
@ -432,10 +400,10 @@ const Settings = () => {
|
||||||
<div className={styles['option-name-container']}>
|
<div className={styles['option-name-container']}>
|
||||||
<div className={styles['label']}>{ t('SETTINGS_SURROUND_SOUND') }</div>
|
<div className={styles['label']}>{ t('SETTINGS_SURROUND_SOUND') }</div>
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<Toggle
|
||||||
className={classnames(styles['option-input-container'], styles['checkbox-container'])}
|
className={classnames(styles['option-input-container'], styles['toggle-container'])}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
{...surroundSoundCheckbox}
|
{...surroundSoundToggle}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -466,11 +434,11 @@ const Settings = () => {
|
||||||
<div className={styles['option-name-container']}>
|
<div className={styles['option-name-container']}>
|
||||||
<div className={styles['label']}>{ t('SETTINGS_PLAY_IN_BACKGROUND') }</div>
|
<div className={styles['label']}>{ t('SETTINGS_PLAY_IN_BACKGROUND') }</div>
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<Toggle
|
||||||
className={classnames(styles['option-input-container'], styles['checkbox-container'])}
|
className={classnames(styles['option-input-container'], styles['toggle-container'])}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
{...playInBackgroundCheckbox}
|
{...playInBackgroundToggle}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -483,9 +451,9 @@ const Settings = () => {
|
||||||
<div className={styles['option-name-container']}>
|
<div className={styles['option-name-container']}>
|
||||||
<div className={styles['label']}>{ t('AUTO_PLAY') }</div>
|
<div className={styles['label']}>{ t('AUTO_PLAY') }</div>
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<Toggle
|
||||||
className={classnames(styles['option-input-container'], styles['checkbox-container'])}
|
className={classnames(styles['option-input-container'], styles['toggle-container'])}
|
||||||
{...bingeWatchingCheckbox}
|
{...bingeWatchingToggle}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles['option-container']}>
|
<div className={styles['option-container']}>
|
||||||
|
|
@ -517,53 +485,17 @@ const Settings = () => {
|
||||||
<div className={styles['option-name-container']}>
|
<div className={styles['option-name-container']}>
|
||||||
<div className={styles['label']}>{ t('SETTINGS_HWDEC') }</div>
|
<div className={styles['label']}>{ t('SETTINGS_HWDEC') }</div>
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<Toggle
|
||||||
className={classnames(styles['option-input-container'], styles['checkbox-container'])}
|
className={classnames(styles['option-input-container'], styles['toggle-container'])}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
{...hardwareDecodingCheckbox}
|
{...hardwareDecodingToggle}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref={streamingServerSectionRef} className={styles['section-container']}>
|
<div ref={streamingServerSectionRef} className={styles['section-container']}>
|
||||||
<div className={styles['section-title']}>{ t('SETTINGS_NAV_STREAMING') }</div>
|
<div className={styles['section-title']}>{ t('SETTINGS_NAV_STREAMING') }</div>
|
||||||
<div className={styles['option-container']}>
|
<URLsManager />
|
||||||
<Button className={classnames(styles['option-input-container'], styles['button-container'])} title={'Reload'} onClick={reloadStreamingServer}>
|
|
||||||
<div className={styles['label']}>{ t('RELOAD') }</div>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className={styles['option-container']}>
|
|
||||||
<div className={styles['option-name-container']}>
|
|
||||||
<div className={styles['label']}>{ t('STATUS') }</div>
|
|
||||||
</div>
|
|
||||||
<div className={classnames(styles['option-input-container'], styles['info-container'])}>
|
|
||||||
<div className={styles['label']}>
|
|
||||||
{
|
|
||||||
streamingServer.settings === null ?
|
|
||||||
'NotLoaded'
|
|
||||||
:
|
|
||||||
streamingServer.settings.type === 'Ready' ?
|
|
||||||
t('SETTINGS_SERVER_STATUS_ONLINE')
|
|
||||||
:
|
|
||||||
streamingServer.settings.type === 'Err' ?
|
|
||||||
t('SETTINGS_SERVER_STATUS_ERROR')
|
|
||||||
:
|
|
||||||
streamingServer.settings.type
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles['option-container']}>
|
|
||||||
<div className={styles['option-name-container']}>
|
|
||||||
<div className={styles['label']}>Url</div>
|
|
||||||
</div>
|
|
||||||
<div className={classnames(styles['option-input-container'], styles['configure-input-container'])}>
|
|
||||||
<div className={styles['label']} title={streamingServerUrlInput.value}>{streamingServerUrlInput.value}</div>
|
|
||||||
<Button className={styles['configure-button-container']} title={'Configure server url'} onClick={openConfigureServerUrlModal}>
|
|
||||||
<Icon className={styles['icon']} name={'settings'} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{
|
{
|
||||||
streamingServerRemoteUrlInput.value !== null ?
|
streamingServerRemoteUrlInput.value !== null ?
|
||||||
<div className={styles['option-container']}>
|
<div className={styles['option-container']}>
|
||||||
|
|
@ -779,26 +711,6 @@ const Settings = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{
|
|
||||||
configureServerUrlModalOpen ?
|
|
||||||
<ModalDialog
|
|
||||||
className={styles['configure-server-url-modal-container']}
|
|
||||||
title={t('SETTINGS_SERVER_CONFIGURE_TITLE')}
|
|
||||||
buttons={configureServerUrlModalButtons}
|
|
||||||
onCloseRequest={closeConfigureServerUrlModal}>
|
|
||||||
<TextInput
|
|
||||||
ref={configureServerUrlInputRef}
|
|
||||||
autoFocus={true}
|
|
||||||
className={styles['server-url-input']}
|
|
||||||
type={'text'}
|
|
||||||
defaultValue={streamingServerUrlInput.value}
|
|
||||||
placeholder={t('SETTINGS_SERVER_CONFIGURE_INPUT')}
|
|
||||||
onSubmit={configureServerUrlOnSubmit}
|
|
||||||
/>
|
|
||||||
</ModalDialog>
|
|
||||||
:
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</MainNavBars>
|
</MainNavBars>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
89
src/routes/Settings/URLsManager/AddItem/AddItem.less
Normal file
89
src/routes/Settings/URLsManager/AddItem/AddItem.less
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
@import (reference) '~stremio/common/screen-sizes.less';
|
||||||
|
|
||||||
|
.add-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
gap: 1rem;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
transition: 0.3s all ease-in-out;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.input {
|
||||||
|
background-color: var(--overlay-color);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
width: 70%;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border: 1px solid var(--primary-foreground-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-right: 0;
|
||||||
|
|
||||||
|
.add, .cancel {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.25rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: transparent;
|
||||||
|
transition: 0.3s all ease-in-out;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
width: 3rem;
|
||||||
|
opacity: 0.6;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: var(--overlay-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add {
|
||||||
|
.icon {
|
||||||
|
width: 1.8rem;
|
||||||
|
height: 1.8rem;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.icon {
|
||||||
|
color: var(--secondary-accent-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel {
|
||||||
|
&:hover {
|
||||||
|
.icon {
|
||||||
|
color: var(--color-trakt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 2px solid transparent;
|
||||||
|
background-color: var(--overlay-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: @minimum) {
|
||||||
|
.add-item {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/routes/Settings/URLsManager/AddItem/AddItem.tsx
Normal file
46
src/routes/Settings/URLsManager/AddItem/AddItem.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import React, { ChangeEvent, useCallback, useState } from 'react';
|
||||||
|
import Button from 'stremio/common/Button';
|
||||||
|
import Icon from '@stremio/stremio-icons/react';
|
||||||
|
import TextInput from 'stremio/common/TextInput';
|
||||||
|
import styles from './AddItem.less';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onCancel: () => void;
|
||||||
|
handleAddUrl: (url: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddItem = ({ onCancel, handleAddUrl }: Props) => {
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
|
||||||
|
const handleValueChange = useCallback(({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setInputValue(target.value);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onSumbit = useCallback(() => {
|
||||||
|
handleAddUrl(inputValue);
|
||||||
|
}, [inputValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles['add-item']}>
|
||||||
|
<TextInput
|
||||||
|
className={styles['input']}
|
||||||
|
value={inputValue}
|
||||||
|
onChange={handleValueChange}
|
||||||
|
onSubmit={onSumbit}
|
||||||
|
placeholder={'Enter URL'}
|
||||||
|
/>
|
||||||
|
<div className={styles['actions']}>
|
||||||
|
<Button className={styles['add']} onClick={onSumbit}>
|
||||||
|
<Icon name={'checkmark'} className={styles['icon']} />
|
||||||
|
</Button>
|
||||||
|
<Button className={styles['cancel']} onClick={onCancel}>
|
||||||
|
<Icon name={'close'} className={styles['icon']} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddItem;
|
||||||
5
src/routes/Settings/URLsManager/AddItem/index.ts
Normal file
5
src/routes/Settings/URLsManager/AddItem/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import AddItem from './AddItem';
|
||||||
|
|
||||||
|
export default AddItem;
|
||||||
131
src/routes/Settings/URLsManager/Item/Item.less
Normal file
131
src/routes/Settings/URLsManager/Item/Item.less
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
@import (reference) '~stremio/common/screen-sizes.less';
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
transition: 0.3s all ease-in-out;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
max-width: 60%;
|
||||||
|
|
||||||
|
.selectable {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-right: 5rem;
|
||||||
|
|
||||||
|
.status {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 0.75rem;
|
||||||
|
height: 0.75rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
|
||||||
|
&.ready {
|
||||||
|
background-color: var(--secondary-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
background-color: var(--color-trakt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
right: 1.5rem;
|
||||||
|
top: 50%;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 0.25rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: transparent;
|
||||||
|
transition: 0.3s all ease-in-out;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 3rem;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.3s all ease-in-out;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
background-color: var(--overlay-color);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
color: var(--color-trakt);
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--overlay-color);
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
.delete {
|
||||||
|
.icon {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: @minimum) {
|
||||||
|
.item {
|
||||||
|
padding: 1rem 0.5rem;
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin-right: 4rem;
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
right: 0.5rem;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/routes/Settings/URLsManager/Item/Item.tsx
Normal file
77
src/routes/Settings/URLsManager/Item/Item.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import { useProfile } from 'stremio/common';
|
||||||
|
import { DEFAULT_STREAMING_SERVER_URL } from 'stremio/common/CONSTANTS';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Button from 'stremio/common/Button';
|
||||||
|
import useStreamingServer from 'stremio/common/useStreamingServer';
|
||||||
|
import Icon from '@stremio/stremio-icons/react';
|
||||||
|
import styles from './Item.less';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import RadioButton from 'stremio/common/RadioButton/RadioButton';
|
||||||
|
import useStreamingServerUrls from '../useStreamingServerUrls';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Item = ({ url }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const profile = useProfile();
|
||||||
|
const streamingServer = useStreamingServer();
|
||||||
|
const { deleteServerUrl, selectServerUrl } = useStreamingServerUrls();
|
||||||
|
|
||||||
|
const selected = useMemo(() => profile.settings.streamingServerUrl === url, [url, profile.settings]);
|
||||||
|
const defaultUrl = useMemo(() => url === DEFAULT_STREAMING_SERVER_URL, [url]);
|
||||||
|
|
||||||
|
const handleDelete = useCallback(() => {
|
||||||
|
deleteServerUrl(url);
|
||||||
|
selectServerUrl(DEFAULT_STREAMING_SERVER_URL);
|
||||||
|
}, [url]);
|
||||||
|
|
||||||
|
const handleSelect = useCallback(() => {
|
||||||
|
selectServerUrl(url);
|
||||||
|
}, [url]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles['item']}>
|
||||||
|
<div className={styles['content']}>
|
||||||
|
<RadioButton className={styles['selectable']} selected={selected} onChange={handleSelect} disabled={selected} />
|
||||||
|
<div className={styles['label']}>{url}</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles['actions']}>
|
||||||
|
{
|
||||||
|
selected ?
|
||||||
|
<div className={styles['status']}>
|
||||||
|
<div className={classNames(styles['icon'], { [styles['ready']]: streamingServer.settings?.type === 'Ready' }, { [styles['error']]: streamingServer.settings?.type === 'Err' })} />
|
||||||
|
<div className={styles['label']}>
|
||||||
|
{
|
||||||
|
streamingServer.settings === null ?
|
||||||
|
'NotLoaded'
|
||||||
|
:
|
||||||
|
streamingServer.settings.type === 'Ready' ?
|
||||||
|
t('SETTINGS_SERVER_STATUS_ONLINE')
|
||||||
|
:
|
||||||
|
streamingServer.settings.type === 'Err' ?
|
||||||
|
t('SETTINGS_SERVER_STATUS_ERROR')
|
||||||
|
:
|
||||||
|
streamingServer.settings.type
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!defaultUrl ?
|
||||||
|
<Button className={styles['delete']} onClick={handleDelete}>
|
||||||
|
<Icon name={'bin'} className={styles['icon']} />
|
||||||
|
</Button>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Item;
|
||||||
5
src/routes/Settings/URLsManager/Item/index.ts
Normal file
5
src/routes/Settings/URLsManager/Item/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import Item from './Item';
|
||||||
|
|
||||||
|
export default Item;
|
||||||
92
src/routes/Settings/URLsManager/URLsManager.less
Normal file
92
src/routes/Settings/URLsManager/URLsManager.less
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: 35rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
font-weight: 400;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
transition: 0.3s all ease-in-out;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.add-url {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
background-color: var(--secondary-accent-color);
|
||||||
|
transition: 0.3s all ease-in-out;
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid var(--primary-foreground-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reload {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
background-color: var(--overlay-color);
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
transition: 0.3s all ease-in-out;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid var(--primary-foreground-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/routes/Settings/URLsManager/URLsManager.tsx
Normal file
62
src/routes/Settings/URLsManager/URLsManager.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import styles from './URLsManager.less';
|
||||||
|
import Button from 'stremio/common/Button';
|
||||||
|
import Item from './Item';
|
||||||
|
import AddItem from './AddItem';
|
||||||
|
import Icon from '@stremio/stremio-icons/react';
|
||||||
|
import useStreamingServerUrls from './useStreamingServerUrls';
|
||||||
|
|
||||||
|
const URLsManager = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [addMode, setAddMode] = useState(false);
|
||||||
|
const { streamingServerUrls, addServerUrl, reloadServer } = useStreamingServerUrls();
|
||||||
|
|
||||||
|
const onAdd = () => {
|
||||||
|
setAddMode(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
setAddMode(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddUrl = useCallback((url: string) => {
|
||||||
|
addServerUrl(url);
|
||||||
|
setAddMode(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles['wrapper']}>
|
||||||
|
<div className={styles['header']}>
|
||||||
|
<div className={styles['label']}>URL</div>
|
||||||
|
<div className={styles['label']}>{t('STATUS')}</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles['content']}>
|
||||||
|
{
|
||||||
|
streamingServerUrls.map((item: StreamingServerUrl) => (
|
||||||
|
<Item key={item.url} {...item} />
|
||||||
|
))
|
||||||
|
}
|
||||||
|
{
|
||||||
|
addMode ?
|
||||||
|
<AddItem onCancel={onCancel} handleAddUrl={handleAddUrl} />
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className={styles['footer']}>
|
||||||
|
<Button label={'Add URL'} className={styles['add-url']} onClick={onAdd}>
|
||||||
|
<Icon name={'add'} className={styles['icon']} />
|
||||||
|
{t('SETTINGS_SERVER_ADD_URL')}
|
||||||
|
</Button>
|
||||||
|
<Button className={styles['reload']} title={'Reload'} onClick={reloadServer}>
|
||||||
|
<Icon name={'reset'} className={styles['icon']} />
|
||||||
|
<div className={styles['label']}>{t('RELOAD')}</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default URLsManager;
|
||||||
5
src/routes/Settings/URLsManager/index.ts
Normal file
5
src/routes/Settings/URLsManager/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import URLsManager from './URLsManager';
|
||||||
|
|
||||||
|
export default URLsManager;
|
||||||
89
src/routes/Settings/URLsManager/useStreamingServerUrls.js
Normal file
89
src/routes/Settings/URLsManager/useStreamingServerUrls.js
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright (C) 2017-2024 Smart code 203358507
|
||||||
|
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useModelState, useToast } from 'stremio/common';
|
||||||
|
import useProfile from 'stremio/common/useProfile';
|
||||||
|
import { useServices } from 'stremio/services';
|
||||||
|
|
||||||
|
const useStreamingServerUrls = () => {
|
||||||
|
const { core } = useServices();
|
||||||
|
const profile = useProfile();
|
||||||
|
const toast = useToast();
|
||||||
|
const ctx = useModelState({ model: 'ctx' });
|
||||||
|
const streamingServerUrls = ctx.streamingServerUrls;
|
||||||
|
|
||||||
|
const addServerUrl = useCallback((url) => {
|
||||||
|
const isValidUrl = (url) => {
|
||||||
|
try {
|
||||||
|
new URL(url);
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isValidUrl(url)) {
|
||||||
|
toast.show({
|
||||||
|
type: 'success',
|
||||||
|
title: 'New URL added',
|
||||||
|
message: 'The new URL has been added successfully',
|
||||||
|
timeout: 4000
|
||||||
|
});
|
||||||
|
|
||||||
|
core.transport.dispatch({
|
||||||
|
action: 'Ctx',
|
||||||
|
args: {
|
||||||
|
action: 'AddServerUrl',
|
||||||
|
args: url,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast.show({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Invalid URL',
|
||||||
|
message: 'Please provide a valid URL',
|
||||||
|
timeout: 4000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const deleteServerUrl = useCallback((url) => {
|
||||||
|
core.transport.dispatch({
|
||||||
|
action: 'Ctx',
|
||||||
|
args: {
|
||||||
|
action: 'DeleteServerUrl',
|
||||||
|
args: url,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
const selectServerUrl = useCallback((url) => {
|
||||||
|
core.transport.dispatch({
|
||||||
|
action: 'Ctx',
|
||||||
|
args: {
|
||||||
|
action: 'UpdateSettings',
|
||||||
|
args: {
|
||||||
|
...profile.settings,
|
||||||
|
streamingServerUrl: url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [profile.settings]);
|
||||||
|
const reloadServer = useCallback(() => {
|
||||||
|
core.transport.dispatch({
|
||||||
|
action: 'StreamingServer',
|
||||||
|
args: {
|
||||||
|
action: 'Reload'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
streamingServerUrls,
|
||||||
|
addServerUrl,
|
||||||
|
deleteServerUrl,
|
||||||
|
selectServerUrl,
|
||||||
|
reloadServer
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useStreamingServerUrls;
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||||
@import (reference) '~stremio/common/screen-sizes.less';
|
@import (reference) '~stremio/common/screen-sizes.less';
|
||||||
|
|
||||||
:import('~stremio/common/Checkbox/styles.less') {
|
:import('~stremio/common/Toggle/styles.less') {
|
||||||
checkbox-icon: icon;
|
checkbox-icon: icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -12,11 +12,6 @@
|
||||||
multiselect-label: label;
|
multiselect-label: label;
|
||||||
}
|
}
|
||||||
|
|
||||||
:import('~stremio/common/ModalDialog/styles.less') {
|
|
||||||
configure-server-url-modal-content: modal-dialog-content;
|
|
||||||
cancel-button-label: label;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-container {
|
.settings-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
@ -407,48 +402,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.configure-server-url-modal-container {
|
|
||||||
.configure-server-url-modal-content {
|
|
||||||
width: 30rem;
|
|
||||||
|
|
||||||
.server-url-input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 1rem;
|
|
||||||
color: var(--primary-foreground-color);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
background-color: var(--overlay-color);
|
|
||||||
outline: var(--focus-outline-size) solid var(--overlay-color);
|
|
||||||
outline-offset: calc(-1 * var(--focus-outline-size));
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
outline-color: var(--primary-foreground-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline-color: var(--primary-foreground-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancel-button {
|
|
||||||
background-color: transparent;
|
|
||||||
opacity: 0.3;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
outline: var(--focus-outline-size) solid var(--primary-foreground-color) inset;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline-color: var(--primary-foreground-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancel-button-label {
|
|
||||||
color: var(--primary-foreground-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: @xsmall) {
|
@media only screen and (max-width: @xsmall) {
|
||||||
.settings-container {
|
.settings-container {
|
||||||
.settings-content {
|
.settings-content {
|
||||||
|
|
@ -494,10 +447,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.configure-server-url-modal-container {
|
|
||||||
.configure-server-url-modal-content {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +136,7 @@ const useProfileSettingsInputs = (profile) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}), [profile.settings]);
|
}), [profile.settings]);
|
||||||
const surroundSoundCheckbox = React.useMemo(() => ({
|
const surroundSoundToggle = React.useMemo(() => ({
|
||||||
checked: profile.settings.surroundSound,
|
checked: profile.settings.surroundSound,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
core.transport.dispatch({
|
core.transport.dispatch({
|
||||||
|
|
@ -151,7 +151,7 @@ const useProfileSettingsInputs = (profile) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}), [profile.settings]);
|
}), [profile.settings]);
|
||||||
const escExitFullscreenCheckbox = React.useMemo(() => ({
|
const escExitFullscreenToggle = React.useMemo(() => ({
|
||||||
checked: profile.settings.escExitFullscreen,
|
checked: profile.settings.escExitFullscreen,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
core.transport.dispatch({
|
core.transport.dispatch({
|
||||||
|
|
@ -261,7 +261,7 @@ const useProfileSettingsInputs = (profile) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}), [profile.settings]);
|
}), [profile.settings]);
|
||||||
const bingeWatchingCheckbox = React.useMemo(() => ({
|
const bingeWatchingToggle = React.useMemo(() => ({
|
||||||
checked: profile.settings.bingeWatching,
|
checked: profile.settings.bingeWatching,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
core.transport.dispatch({
|
core.transport.dispatch({
|
||||||
|
|
@ -276,7 +276,7 @@ const useProfileSettingsInputs = (profile) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}), [profile.settings]);
|
}), [profile.settings]);
|
||||||
const playInBackgroundCheckbox = React.useMemo(() => ({
|
const playInBackgroundToggle = React.useMemo(() => ({
|
||||||
checked: profile.settings.playInBackground,
|
checked: profile.settings.playInBackground,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
core.transport.dispatch({
|
core.transport.dispatch({
|
||||||
|
|
@ -291,7 +291,7 @@ const useProfileSettingsInputs = (profile) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}), [profile.settings]);
|
}), [profile.settings]);
|
||||||
const hardwareDecodingCheckbox = React.useMemo(() => ({
|
const hardwareDecodingToggle = React.useMemo(() => ({
|
||||||
checked: profile.settings.hardwareDecoding,
|
checked: profile.settings.hardwareDecoding,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
core.transport.dispatch({
|
core.transport.dispatch({
|
||||||
|
|
@ -306,21 +306,6 @@ const useProfileSettingsInputs = (profile) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}), [profile.settings]);
|
}), [profile.settings]);
|
||||||
const streamingServerUrlInput = React.useMemo(() => ({
|
|
||||||
value: profile.settings.streamingServerUrl,
|
|
||||||
onChange: (value) => {
|
|
||||||
core.transport.dispatch({
|
|
||||||
action: 'Ctx',
|
|
||||||
args: {
|
|
||||||
action: 'UpdateSettings',
|
|
||||||
args: {
|
|
||||||
...profile.settings,
|
|
||||||
streamingServerUrl: value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}), [profile.settings]);
|
|
||||||
return {
|
return {
|
||||||
interfaceLanguageSelect,
|
interfaceLanguageSelect,
|
||||||
subtitlesLanguageSelect,
|
subtitlesLanguageSelect,
|
||||||
|
|
@ -329,16 +314,15 @@ const useProfileSettingsInputs = (profile) => {
|
||||||
subtitlesBackgroundColorInput,
|
subtitlesBackgroundColorInput,
|
||||||
subtitlesOutlineColorInput,
|
subtitlesOutlineColorInput,
|
||||||
audioLanguageSelect,
|
audioLanguageSelect,
|
||||||
surroundSoundCheckbox,
|
surroundSoundToggle,
|
||||||
escExitFullscreenCheckbox,
|
escExitFullscreenToggle,
|
||||||
seekTimeDurationSelect,
|
seekTimeDurationSelect,
|
||||||
seekShortTimeDurationSelect,
|
seekShortTimeDurationSelect,
|
||||||
playInExternalPlayerSelect,
|
playInExternalPlayerSelect,
|
||||||
nextVideoPopupDurationSelect,
|
nextVideoPopupDurationSelect,
|
||||||
bingeWatchingCheckbox,
|
bingeWatchingToggle,
|
||||||
playInBackgroundCheckbox,
|
playInBackgroundToggle,
|
||||||
hardwareDecodingCheckbox,
|
hardwareDecodingToggle,
|
||||||
streamingServerUrlInput
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
8
src/types/models/Ctx.d.ts
vendored
8
src/types/models/Ctx.d.ts
vendored
|
|
@ -67,8 +67,16 @@ type SearchHistoryItem = {
|
||||||
|
|
||||||
type SearchHistory = SearchHistoryItem[];
|
type SearchHistory = SearchHistoryItem[];
|
||||||
|
|
||||||
|
type StreamingServerUrl = {
|
||||||
|
url: string,
|
||||||
|
mtime: Date,
|
||||||
|
};
|
||||||
|
|
||||||
|
type StreamingServerUrls = StreamingServerUrl[];
|
||||||
|
|
||||||
type Ctx = {
|
type Ctx = {
|
||||||
profile: Profile,
|
profile: Profile,
|
||||||
notifications: Notifications,
|
notifications: Notifications,
|
||||||
searchHistory: SearchHistory,
|
searchHistory: SearchHistory,
|
||||||
|
streamingServerUrls: StreamingServerUrls,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue