Merge branch 'development' of https://github.com/Stremio/stremio-web into refactor/components

This commit is contained in:
Tim 2025-01-06 14:28:54 +01:00
commit ccf593d644
29 changed files with 298 additions and 156 deletions

13
package-lock.json generated
View file

@ -1,18 +1,18 @@
{ {
"name": "stremio", "name": "stremio",
"version": "5.0.0-beta.15", "version": "5.0.0-beta.16",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "stremio", "name": "stremio",
"version": "5.0.0-beta.15", "version": "5.0.0-beta.16",
"license": "gpl-2.0", "license": "gpl-2.0",
"dependencies": { "dependencies": {
"@babel/runtime": "7.26.0", "@babel/runtime": "7.26.0",
"@sentry/browser": "8.42.0", "@sentry/browser": "8.42.0",
"@stremio/stremio-colors": "5.2.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-icons": "5.4.1",
"@stremio/stremio-video": "0.0.48", "@stremio/stremio-video": "0.0.48",
"a-color-picker": "1.2.1", "a-color-picker": "1.2.1",
@ -3371,9 +3371,10 @@
"integrity": "sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg==" "integrity": "sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg=="
}, },
"node_modules/@stremio/stremio-core-web": { "node_modules/@stremio/stremio-core-web": {
"version": "0.48.3", "version": "0.48.4",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.3.tgz", "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.4.tgz",
"integrity": "sha512-JL8pOLOEVACYG+33Dtp/mrB2/vuc7RoYZdxX1BQa5MPR8EzsODjpvL5uETmdxo/swgtMZyx2A6/e1B53eKA4oQ==", "integrity": "sha512-848OLm0dtP75aAlYhUB0KoOqwosJIj+ubB8/abuaAzH/N3dtxs40vu2AezmMpGjwR4V60rlOUkUZeWFvrUOjrw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "7.24.1" "@babel/runtime": "7.24.1"
} }

View file

@ -1,7 +1,7 @@
{ {
"name": "stremio", "name": "stremio",
"displayName": "Stremio", "displayName": "Stremio",
"version": "5.0.0-beta.15", "version": "5.0.0-beta.16",
"author": "Smart Code OOD", "author": "Smart Code OOD",
"private": true, "private": true,
"license": "gpl-2.0", "license": "gpl-2.0",
@ -16,7 +16,7 @@
"@babel/runtime": "7.26.0", "@babel/runtime": "7.26.0",
"@sentry/browser": "8.42.0", "@sentry/browser": "8.42.0",
"@stremio/stremio-colors": "5.2.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-icons": "5.4.1",
"@stremio/stremio-video": "0.0.48", "@stremio/stremio-video": "0.0.48",
"a-color-picker": "1.2.1", "a-color-picker": "1.2.1",

View file

@ -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/common/screen-sizes.less';
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; @import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
@ -13,6 +13,20 @@
@import (once, less) '~stremio-router/styles.css'; @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 { :root {
--landscape-shape-ratio: 0.5625; --landscape-shape-ratio: 0.5625;
--poster-shape-ratio: 1.464; --poster-shape-ratio: 1.464;
@ -40,6 +54,15 @@
--modal-background-color: rgba(15, 13, 32, 1); --modal-background-color: rgba(15, 13, 32, 1);
--outer-glow: 0px 0px 15px rgba(123, 91, 245, 0.37); --outer-glow: 0px 0px 15px rgba(123, 91, 245, 0.37);
--border-radius: 0.75rem; --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 { html {
width: 100%; width: @html-width;
height: 100%; height: @html-height;
min-width: 640px; min-width: 640px;
min-height: 480px; min-height: 480px;
font-family: 'PlusJakartaSans', 'sans-serif'; font-family: 'PlusJakartaSans', 'sans-serif';
overflow: auto; overflow: auto;
overscroll-behavior: none; overscroll-behavior: none;
user-select: none;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
body { body {
width: 100%; width: 100%;
@ -106,9 +132,9 @@ html {
.toasts-container { .toasts-container {
position: absolute; position: absolute;
top: calc(1.2 * var(--horizontal-nav-bar-size)); top: calc(1.2 * var(--horizontal-nav-bar-size) + var(--safe-area-inset-top));
right: 0; right: var(--safe-area-inset-right);
bottom: calc(1.2 * var(--horizontal-nav-bar-size)); bottom: calc(1.2 * var(--horizontal-nav-bar-size) + var(--calculated-bottom-safe-inset, 0rem));
left: auto; left: auto;
z-index: 1; z-index: 1;
padding: 0 calc(0.5 * var(--horizontal-nav-bar-size)); 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;
}
} }

View file

@ -23,7 +23,6 @@
opacity: 0.8; opacity: 0.8;
transition: opacity 0.1s ease-out; transition: opacity 0.1s ease-out;
cursor: pointer; cursor: pointer;
-webkit-tap-highlight-color: transparent;
} }
.container { .container {

View file

@ -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/stremio-colors/less/stremio-colors.less';
@ -6,7 +6,6 @@
outline-width: var(--focus-outline-size); outline-width: var(--focus-outline-size);
outline-color: @color-surface-light5; outline-color: @color-surface-light5;
outline-offset: calc(-1 * var(--focus-outline-size)); outline-offset: calc(-1 * var(--focus-outline-size));
-webkit-tap-highlight-color: transparent;
cursor: pointer; cursor: pointer;
&:focus { &:focus {

View file

@ -16,7 +16,6 @@
text-transform: capitalize; text-transform: capitalize;
padding: 0 1.75rem; padding: 0 1.75rem;
border-radius: @height; border-radius: @height;
-webkit-tap-highlight-color: transparent;
background-color: transparent; background-color: transparent;
user-select: none; user-select: none;
overflow: hidden; overflow: hidden;

View file

@ -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'; @import (reference) '~stremio/common/screen-sizes.less';
.main-nav-bars-container { .main-nav-bars-container {
position: relative; position: relative;
z-index: 0; 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 { .horizontal-nav-bar {
position: absolute; position: absolute;
@ -17,18 +22,20 @@
.vertical-nav-bar { .vertical-nav-bar {
position: absolute; position: absolute;
top: var(--horizontal-nav-bar-size); top: var(--horizontal-nav-bar-size);
bottom: 0; bottom: var(--calculated-bottom-safe-inset);
left: 0; left: 0;
z-index: 1; z-index: 1;
} }
.nav-content-container { .nav-content-container {
position: absolute; 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; right: 0;
bottom: 0; bottom: 0;
left: var(--vertical-nav-bar-size); left: var(--vertical-nav-bar-size);
z-index: 0; z-index: 0;
overflow: scroll;
} }
} }
@ -36,7 +43,7 @@
.main-nav-bars-container { .main-nav-bars-container {
.nav-content-container { .nav-content-container {
left: 0; left: 0;
bottom: var(--vertical-nav-bar-size); padding-bottom: var(--vertical-nav-bar-size);
} }
.vertical-nav-bar { .vertical-nav-bar {

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -18,7 +18,6 @@
.meta-item-container { .meta-item-container {
padding: 1rem; padding: 1rem;
overflow: visible; overflow: visible;
-webkit-tap-highlight-color: transparent;
&:hover, &:focus, &:global(.active), &:global(.selected) { &:hover, &:focus, &:global(.active), &:global(.selected) {
outline-style: none; outline-style: none;

View file

@ -1,6 +1,6 @@
// Copyright (C) 2017-2024 Smart code 203358507 // Copyright (C) 2017-2024 Smart code 203358507
import React from 'react'; import React, { useRef, useEffect, useCallback } from 'react';
import { Button } from 'stremio/components'; import { Button } from 'stremio/components';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import classNames from 'classnames'; import classNames from 'classnames';
@ -19,33 +19,57 @@ type Props = {
const Dropdown = ({ level, setLevel, options, onSelect, selectedOption, menuOpen }: Props) => { const Dropdown = ({ level, setLevel, options, onSelect, selectedOption, menuOpen }: Props) => {
const { t } = useTranslation(); 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 - 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 ( return (
<div className={classNames(styles['dropdown'], { [styles['open']]: menuOpen })} role={'listbox'}> <div
{ className={classNames(styles['dropdown'], { [styles['open']]: menuOpen })}
level > 0 ? role={'listbox'}
<Button className={styles['back-button']} onClick={onBackButtonClick}> ref={containerRef}
<Icon name={'caret-left'} className={styles['back-button-icon']} /> >
{t('BACK')} {level > 0 ?
</Button> <Button className={styles['back-button']} onClick={handleBackClick}>
: null <Icon name={'caret-left'} className={styles['back-button-icon']} />
{t('BACK')}
</Button>
: null
} }
{ {options
options .filter((option: MultiselectMenuOption) => !option.hidden)
.filter((option: MultiselectMenuOption) => !option.hidden) .map((option: MultiselectMenuOption) => (
.map((option: MultiselectMenuOption, index) => ( <Option
<Option key={option.id}
key={index} ref={handleSetOptionRef(option.value)}
option={option} option={option}
onSelect={onSelect} onSelect={onSelect}
selectedOption={selectedOption} selectedOption={selectedOption}
/> />
)) ))
} }
</div> </div>
); );

View file

@ -1,6 +1,6 @@
// Copyright (C) 2017-2024 Smart code 203358507 // 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 classNames from 'classnames';
import { Button } from 'stremio/components'; import { Button } from 'stremio/components';
import styles from './Option.less'; import styles from './Option.less';
@ -12,7 +12,7 @@ type Props = {
onSelect: (value: number) => void; onSelect: (value: number) => void;
}; };
const Option = ({ option, selectedOption, onSelect }: Props) => { const Option = forwardRef<HTMLButtonElement, Props>(({ option, selectedOption, onSelect }, ref) => {
// consider using option.id === selectedOption?.id instead // consider using option.id === selectedOption?.id instead
const selected = useMemo(() => option?.value === selectedOption?.value, [option, selectedOption]); const selected = useMemo(() => option?.value === selectedOption?.value, [option, selectedOption]);
@ -22,6 +22,7 @@ const Option = ({ option, selectedOption, onSelect }: Props) => {
return ( return (
<Button <Button
ref={ref}
className={classNames(styles['option'], { [styles['selected']]: selected })} className={classNames(styles['option'], { [styles['selected']]: selected })}
key={option.id} key={option.id}
onClick={handleClick} onClick={handleClick}
@ -32,7 +33,6 @@ const Option = ({ option, selectedOption, onSelect }: Props) => {
selected && !option.level ? selected && !option.level ?
<div className={styles['icon']} /> <div className={styles['icon']} />
: null : null
} }
{ {
option.level ? option.level ?
@ -41,6 +41,8 @@ const Option = ({ option, selectedOption, onSelect }: Props) => {
} }
</Button> </Button>
); );
}; });
Option.displayName = 'Option';
export default Option; export default Option;

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -12,6 +12,8 @@
padding-right: 1rem; padding-right: 1rem;
background-color: transparent; background-color: transparent;
overflow: visible; overflow: visible;
padding-top: var(--safe-area-inset-top);
box-sizing: content-box;
.logo-container { .logo-container {
flex: none; flex: none;
@ -32,7 +34,7 @@
} }
.back-button-container { .back-button-container {
margin-left: 1rem; margin-left: max(0rem, calc(1rem - var(--safe-area-inset-left)));
} }
.title { .title {

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -11,11 +11,13 @@
background-color: transparent; background-color: transparent;
border-radius: 0.75rem; border-radius: 0.75rem;
&:hover { @media (pointer: fine) {
background-color: var(--overlay-color); &:hover {
background-color: var(--overlay-color);
.label { .label {
opacity: 0.6; opacity: 0.6;
}
} }
} }

View file

@ -24,7 +24,6 @@
outline-width: var(--focus-outline-size); outline-width: var(--focus-outline-size);
outline-color: @color-surface-light5; outline-color: @color-surface-light5;
outline-offset: calc(-1 * var(--focus-outline-size)); outline-offset: calc(-1 * var(--focus-outline-size));
-webkit-tap-highlight-color: transparent;
input[type='radio'] { input[type='radio'] {
opacity: 0; opacity: 0;

View file

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0, user-scalable=no, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Stremio"> <meta name="apple-mobile-web-app-title" content="Stremio">
<link rel="icon" type="image/x-icon" href="<%= htmlWebpackPlugin.options.faviconsPath %>/favicon.ico"> <link rel="icon" type="image/x-icon" href="<%= htmlWebpackPlugin.options.faviconsPath %>/favicon.ico">

View file

@ -8,19 +8,43 @@ const { default: Icon } = require('@stremio/stremio-icons/react');
const { Button, Image } = require('stremio/components'); const { Button, Image } = require('stremio/components');
const styles = require('./styles'); 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 { t } = useTranslation();
const toggleButtonOnClick = React.useCallback((event) => { const onInstallClick = React.useCallback((event) => {
if (typeof onToggle === 'function') { event.stopPropagation();
onToggle({ if (typeof onInstall === 'function') {
type: 'toggle', onInstall({
type: 'install',
nativeEvent: event.nativeEvent, nativeEvent: event.nativeEvent,
reactEvent: event, reactEvent: event,
dataset: dataset 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) => { const configureButtonOnClick = React.useCallback((event) => {
event.stopPropagation();
if (typeof onConfigure === 'function') { if (typeof onConfigure === 'function') {
onConfigure({ onConfigure({
type: 'configure', type: 'configure',
@ -31,6 +55,7 @@ const Addon = ({ className, id, name, version, logo, description, types, behavio
} }
}, [onConfigure, dataset]); }, [onConfigure, dataset]);
const shareButtonOnClick = React.useCallback((event) => { const shareButtonOnClick = React.useCallback((event) => {
event.stopPropagation();
if (typeof onShare === 'function') { if (typeof onShare === 'function') {
onShare({ onShare({
type: 'share', type: 'share',
@ -41,20 +66,15 @@ const Addon = ({ className, id, name, version, logo, description, types, behavio
} }
}, [onShare, dataset]); }, [onShare, dataset]);
const onKeyDown = React.useCallback((event) => { const onKeyDown = React.useCallback((event) => {
if (event.key === 'Enter' && typeof onToggle === 'function') { if (event.key === 'Enter') {
onToggle({ onOpenClick(event);
type: 'toggle',
nativeEvent: event.nativeEvent,
reactEvent: event,
dataset: dataset
});
} }
}, [onToggle, dataset]); }, [onOpenClick]);
const renderLogoFallback = React.useCallback(() => ( const renderLogoFallback = React.useCallback(() => (
<Icon className={styles['icon']} name={'addons'} /> <Icon className={styles['icon']} name={'addons'} />
), []); ), []);
return ( return (
<Button className={classnames(className, styles['addon-container'])} onKeyDown={onKeyDown}> <Button className={classnames(className, styles['addon-container'])} onKeyDown={onKeyDown} onClick={onOpenClick}>
<div className={styles['logo-container']}> <div className={styles['logo-container']}>
<Image <Image
className={styles['logo']} className={styles['logo']}
@ -107,7 +127,7 @@ const Addon = ({ className, id, name, version, logo, description, types, behavio
className={installed ? styles['uninstall-button-container'] : styles['install-button-container']} className={installed ? styles['uninstall-button-container'] : styles['install-button-container']}
title={installed ? t('ADDON_UNINSTALL') : behaviorHints.configurationRequired ? t('ADDON_CONFIGURE') : t('ADDON_INSTALL')} title={installed ? t('ADDON_UNINSTALL') : behaviorHints.configurationRequired ? t('ADDON_CONFIGURE') : t('ADDON_INSTALL')}
tabIndex={-1} tabIndex={-1}
onClick={!installed && behaviorHints.configurationRequired ? configureButtonOnClick : toggleButtonOnClick} onClick={installed ? onUninstallClick : behaviorHints.configurationRequired ? configureButtonOnClick : onInstallClick}
> >
<div className={styles['label']}>{installed ? t('ADDON_UNINSTALL') : behaviorHints.configurationRequired ? t('ADDON_CONFIGURE') : t('ADDON_INSTALL')}</div> <div className={styles['label']}>{installed ? t('ADDON_UNINSTALL') : behaviorHints.configurationRequired ? t('ADDON_CONFIGURE') : t('ADDON_INSTALL')}</div>
</Button> </Button>
@ -137,7 +157,10 @@ Addon.propTypes = {
}), }),
installed: PropTypes.bool, installed: PropTypes.bool,
onToggle: PropTypes.func, onToggle: PropTypes.func,
onInstall: PropTypes.func,
onUninstall: PropTypes.func,
onConfigure: PropTypes.func, onConfigure: PropTypes.func,
onOpen: PropTypes.func,
onShare: PropTypes.func, onShare: PropTypes.func,
dataset: PropTypes.object dataset: PropTypes.object
}; };

View file

@ -8,9 +8,15 @@
flex-direction: row; flex-direction: row;
align-items: flex-start; align-items: flex-start;
padding: 1.5rem; padding: 1.5rem;
border: 0.15rem solid transparent;
border-radius: var(--border-radius); border-radius: var(--border-radius);
background-color: var(--overlay-color); background-color: var(--overlay-color);
cursor: inherit; transition: border-color 0.1s ease-out;
cursor: pointer;
&:hover {
border-color: var(--overlay-color);
}
.logo-container { .logo-container {
flex: none; flex: none;

View file

@ -7,6 +7,7 @@ const { useTranslation } = require('react-i18next');
const { default: Icon } = require('@stremio/stremio-icons/react'); const { default: Icon } = require('@stremio/stremio-icons/react');
const { usePlatform, useBinaryState, withCoreSuspender } = require('stremio/common'); const { usePlatform, useBinaryState, withCoreSuspender } = require('stremio/common');
const { AddonDetailsModal, Button, Image, MainNavBars, Multiselect, ModalDialog, SearchBar, SharePrompt, TextInput } = require('stremio/components'); const { AddonDetailsModal, Button, Image, MainNavBars, Multiselect, ModalDialog, SearchBar, SharePrompt, TextInput } = require('stremio/components');
const { useServices } = require('stremio/services');
const Addon = require('./Addon'); const Addon = require('./Addon');
const useInstalledAddons = require('./useInstalledAddons'); const useInstalledAddons = require('./useInstalledAddons');
const useRemoteAddons = require('./useRemoteAddons'); const useRemoteAddons = require('./useRemoteAddons');
@ -18,6 +19,7 @@ const { AddonPlaceholder } = require('./AddonPlaceholder');
const Addons = ({ urlParams, queryParams }) => { const Addons = ({ urlParams, queryParams }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const platform = usePlatform(); const platform = usePlatform();
const { core } = useServices();
const installedAddons = useInstalledAddons(urlParams); const installedAddons = useInstalledAddons(urlParams);
const remoteAddons = useRemoteAddons(urlParams); const remoteAddons = useRemoteAddons(urlParams);
const [addonDetailsTransportUrl, setAddonDetailsTransportUrl] = useAddonDetailsTransportUrl(urlParams, queryParams); const [addonDetailsTransportUrl, setAddonDetailsTransportUrl] = useAddonDetailsTransportUrl(urlParams, queryParams);
@ -58,12 +60,30 @@ const Addons = ({ urlParams, queryParams }) => {
const onAddonShare = React.useCallback((event) => { const onAddonShare = React.useCallback((event) => {
setSharedAddon(event.dataset.addon); setSharedAddon(event.dataset.addon);
}, []); }, []);
const onAddonToggle = React.useCallback((event) => { const onAddonInstall = React.useCallback((event) => {
setAddonDetailsTransportUrl(event.dataset.addon.transportUrl); core.transport.dispatch({
}, [setAddonDetailsTransportUrl]); action: 'Ctx',
args: {
action: 'InstallAddon',
args: event.dataset.addon,
}
});
}, []);
const onAddonUninstall = React.useCallback((event) => {
core.transport.dispatch({
action: 'Ctx',
args: {
action: 'UninstallAddon',
args: event.dataset.addon,
}
});
}, []);
const onAddonConfigure = React.useCallback((event) => { const onAddonConfigure = React.useCallback((event) => {
platform.openExternal(event.dataset.addon.transportUrl.replace('manifest.json', 'configure')); platform.openExternal(event.dataset.addon.transportUrl.replace('manifest.json', 'configure'));
}, []); }, []);
const onAddonOpen = React.useCallback((event) => {
setAddonDetailsTransportUrl(event.dataset.addon.transportUrl);
}, [setAddonDetailsTransportUrl]);
const closeAddonDetails = React.useCallback(() => { const closeAddonDetails = React.useCallback(() => {
setAddonDetailsTransportUrl(null); setAddonDetailsTransportUrl(null);
}, [setAddonDetailsTransportUrl]); }, [setAddonDetailsTransportUrl]);
@ -136,8 +156,10 @@ const Addons = ({ urlParams, queryParams }) => {
types={addon.manifest.types} types={addon.manifest.types}
behaviorHints={addon.manifest.behaviorHints} behaviorHints={addon.manifest.behaviorHints}
installed={addon.installed} installed={addon.installed}
onToggle={onAddonToggle} onInstall={onAddonInstall}
onUninstall={onAddonUninstall}
onConfigure={onAddonConfigure} onConfigure={onAddonConfigure}
onOpen={onAddonOpen}
onShare={onAddonShare} onShare={onAddonShare}
dataset={{ addon }} dataset={{ addon }}
/> />
@ -174,8 +196,10 @@ const Addons = ({ urlParams, queryParams }) => {
types={addon.manifest.types} types={addon.manifest.types}
behaviorHints={addon.manifest.behaviorHints} behaviorHints={addon.manifest.behaviorHints}
installed={addon.installed} installed={addon.installed}
onToggle={onAddonToggle} onInstall={onAddonInstall}
onUninstall={onAddonUninstall}
onConfigure={onAddonConfigure} onConfigure={onAddonConfigure}
onOpen={onAddonOpen}
onShare={onAddonShare} onShare={onAddonShare}
dataset={{ addon }} dataset={{ addon }}
/> />

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -22,17 +22,22 @@
} }
.addons-container { .addons-container {
width: 100%;
height: 100%; height: 100%;
background-color: transparent; background-color: transparent;
.addons-content { .addons-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-bottom: calc(var(--bottom-overlay-size) * -1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-self: stretch; align-self: stretch;
.addons-list-container {
padding: 0 1.5rem;
z-index: 1;
}
.selectable-inputs-container { .selectable-inputs-container {
flex: none; flex: none;
align-self: stretch; align-self: stretch;
@ -41,6 +46,7 @@
justify-content: space-between; justify-content: space-between;
padding: 1.5rem; padding: 1.5rem;
overflow: visible; overflow: visible;
z-index: 2;
.add-button-container { .add-button-container {
flex: none; flex: none;
@ -53,6 +59,7 @@
padding: 0 1.5rem; padding: 0 1.5rem;
border-radius: 3rem; border-radius: 3rem;
background-color: var(--secondary-accent-color); background-color: var(--secondary-accent-color);
z-index: 3;
&:hover { &:hover {
outline: var(--focus-outline-size) solid var(--secondary-accent-color); outline: var(--focus-outline-size) solid var(--secondary-accent-color);
@ -133,7 +140,6 @@
.addons-list-container { .addons-list-container {
flex: 1; flex: 1;
align-self: stretch; align-self: stretch;
padding: 0 1.5rem;
overflow-y: auto; overflow-y: auto;
.addon { .addon {

View file

@ -69,14 +69,17 @@ const Board = () => {
); );
} }
case 'Err': { case 'Err': {
return ( if (catalog.content.content !== 'EmptyContent') {
<MetaRow return (
key={index} <MetaRow
className={classnames(styles['board-row'], 'animation-fade-in')} key={index}
catalog={catalog} className={classnames(styles['board-row'], 'animation-fade-in')}
message={catalog.content.content} catalog={catalog}
/> message={catalog.content.content}
); />
);
}
return null;
} }
default: { default: {
return ( return (

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -23,8 +23,8 @@
background: transparent; background: transparent;
.board-content { .board-content {
width: 100%;
height: 100%; height: 100%;
width: 100%;
padding: 0 1rem; padding: 0 1rem;
overflow-y: auto; overflow-y: auto;
@ -38,6 +38,7 @@
.board-warning-container { .board-warning-container {
flex: none; flex: none;
align-self: stretch; align-self: stretch;
margin-bottom: var(--calculated-bottom-safe-inset, 0rem);
} }
} }
@ -191,15 +192,7 @@
z-index: 0; z-index: 0;
.board-content-container { .board-content-container {
&:only-child {
.board-content {
height: 100%;
}
}
.board-content { .board-content {
height: calc(100% - 4rem);
.board-row { .board-row {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
@ -226,8 +219,9 @@
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
bottom: var(--vertical-nav-bar-size); bottom: calc(var(--vertical-nav-bar-size) + var(--calculated-bottom-safe-inset, 0rem));
height: 4rem; height: 4rem;
margin-bottom: 0rem;
} }
} }
} }

View file

@ -16,7 +16,6 @@
font-size: 1rem; font-size: 1rem;
font-weight: 500; font-weight: 500;
color: var(--primary-foreground-color); color: var(--primary-foreground-color);
-webkit-tap-highlight-color: transparent;
.name { .name {
flex: auto; flex: auto;

View file

@ -22,7 +22,6 @@
gap: 0.5rem; gap: 0.5rem;
border-radius: 0.5rem; border-radius: 0.5rem;
transition: background-color 0.1s ease-out; transition: background-color 0.1s ease-out;
-webkit-tap-highlight-color: transparent;
.label, .icon { .label, .icon {
color: var(--primary-foreground-color); color: var(--primary-foreground-color);

View file

@ -13,7 +13,6 @@
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
transition: border-color 0.1s ease-out; transition: border-color 0.1s ease-out;
-webkit-tap-highlight-color: transparent;
&:first-child { &:first-child {
border-radius: var(--border-radius) 0 0 0; border-radius: var(--border-radius) 0 0 0;

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -17,13 +17,13 @@
} }
.discover-container { .discover-container {
width: 100%;
height: 100%; height: 100%;
background-color: transparent; background-color: transparent;
.discover-content { .discover-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-bottom: calc(var(--bottom-overlay-size) * -1);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -32,6 +32,12 @@
align-self: stretch; align-self: stretch;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
contain: strict;
.meta-items-container {
padding: 0 1.5rem;
z-index: 1;
}
.selectable-inputs-container { .selectable-inputs-container {
flex: none; flex: none;
@ -40,6 +46,7 @@
flex-direction: row; flex-direction: row;
padding: 1.5rem; padding: 1.5rem;
overflow: visible; overflow: visible;
z-index: 2;
.select-input { .select-input {
flex: 0 1 15rem; flex: 0 1 15rem;
@ -162,7 +169,6 @@
align-items: center; align-items: center;
grid-gap: 0.5rem; grid-gap: 0.5rem;
margin-right: 1.5rem; margin-right: 1.5rem;
padding: 0 1.5rem;
overflow-y: auto; overflow-y: auto;
.meta-item-placeholder { .meta-item-placeholder {

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -13,16 +13,21 @@
} }
.library-container { .library-container {
width: 100%;
height: 100%; height: 100%;
background-color: transparent; background-color: transparent;
.library-content { .library-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-bottom: calc(var(--bottom-overlay-size) * -1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.meta-items-container {
padding: 0 1.5rem;
z-index: 1;
}
.selectable-inputs-container { .selectable-inputs-container {
flex: none; flex: none;
align-self: stretch; align-self: stretch;
@ -30,6 +35,7 @@
flex-direction: row; flex-direction: row;
padding: 1.5rem; padding: 1.5rem;
overflow: visible; overflow: visible;
z-index: 2;
.select-input-container { .select-input-container {
flex-grow: 0; flex-grow: 0;
@ -119,7 +125,6 @@
grid-auto-rows: max-content; grid-auto-rows: max-content;
align-items: center; align-items: center;
grid-gap: 0.5rem; grid-gap: 0.5rem;
padding: 0 1.5rem;
overflow-y: auto; overflow-y: auto;
} }
} }

View file

@ -77,8 +77,29 @@ const MetaDetails = ({ urlParams, queryParams }) => {
setSeason(event.value); setSeason(event.value);
}, [setSeason]); }, [setSeason]);
const renderBackgroundImageFallback = React.useCallback(() => null, []); const renderBackgroundImageFallback = React.useCallback(() => null, []);
const renderBackground = React.useMemo(() => !!(
metaPath &&
metaDetails?.metaItem &&
metaDetails.metaItem.content.type !== 'Loading' &&
typeof metaDetails.metaItem.content.content?.background === 'string' &&
metaDetails.metaItem.content.content.background.length > 0
), [metaPath, metaDetails]);
return ( return (
<div className={styles['metadetails-container']}> <div className={styles['metadetails-container']}>
{
renderBackground ?
<div className={styles['background-image-layer']}>
<Image
className={styles['background-image']}
src={metaDetails.metaItem.content.content.background}
renderFallback={renderBackgroundImageFallback}
alt={' '}
/>
</div>
:
null
}
<HorizontalNavBar <HorizontalNavBar
className={styles['nav-bar']} className={styles['nav-bar']}
backButton={true} backButton={true}
@ -121,20 +142,6 @@ const MetaDetails = ({ urlParams, queryParams }) => {
<MetaPreview.Placeholder className={styles['meta-preview']} /> <MetaPreview.Placeholder className={styles['meta-preview']} />
: :
<React.Fragment> <React.Fragment>
{
typeof metaDetails.metaItem.content.content.background === 'string' &&
metaDetails.metaItem.content.content.background.length > 0 ?
<div className={styles['background-image-layer']}>
<Image
className={styles['background-image']}
src={metaDetails.metaItem.content.content.background}
renderFallback={renderBackgroundImageFallback}
alt={' '}
/>
</div>
:
null
}
<MetaPreview <MetaPreview
className={classnames(styles['meta-preview'], 'animation-fade-in')} className={classnames(styles['meta-preview'], 'animation-fade-in')}
name={metaDetails.metaItem.content.content.name} name={metaDetails.metaItem.content.content.name}

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -13,6 +13,29 @@
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
height: 100%; height: 100%;
padding-left: var(--safe-area-inset-left);
padding-right: var(--safe-area-inset-right);
box-sizing: border-box;
.background-image-layer {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
background-color: var(--modal-background-color);
.background-image {
pointer-events: none;
display: block;
width: 100%;
height: 100%;
object-fit: cover;
object-position: top left;
opacity: 0.3;
}
}
.nav-bar { .nav-bar {
z-index: 1; z-index: 1;
@ -27,32 +50,13 @@
z-index: 0; z-index: 0;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin-top: calc(var(--top-overlay-size) * -1);
padding-top: var(--top-overlay-size);
padding-bottom: var(--calculated-bottom-safe-inset, 0rem);
.vertical-nav-bar { .vertical-nav-bar {
--vertical-nav-bar-size: 6rem;
flex: none; flex: none;
} }
.background-image-layer {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
background-color: var(--modal-background-color);
.background-image {
pointer-events: none;
display: block;
width: 100%;
height: 100%;
object-fit: cover;
object-position: top left;
opacity: 0.3;
}
}
.meta-message-container { .meta-message-container {
align-self: stretch; align-self: stretch;
display: flex; display: flex;
@ -87,6 +91,8 @@
flex: 0 1 auto; flex: 0 1 auto;
align-self: stretch; align-self: stretch;
padding: 0 4rem 2rem 4rem; padding: 0 4rem 2rem 4rem;
padding-left: max(1rem, calc(4rem - var(--safe-area-inset-left)));
} }
.spacing { .spacing {

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -12,13 +12,13 @@
} }
.search-container { .search-container {
width: 100%;
height: 100%; height: 100%;
width: 100%;
background-color: transparent; background-color: transparent;
.search-content { .search-content {
width: 100%;
height: 100%; height: 100%;
width: 100%;
overflow-y: auto; overflow-y: auto;
.search-row { .search-row {

View file

@ -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/stremio-colors/less/stremio-colors.less';
@import (reference) '~stremio/common/screen-sizes.less'; @import (reference) '~stremio/common/screen-sizes.less';
@ -13,13 +13,13 @@
} }
.settings-container { .settings-container {
width: 100%;
height: 100%; height: 100%;
width: 100%;
background-color: transparent; background-color: transparent;
.settings-content { .settings-content {
width: 100%;
height: 100%; height: 100%;
width: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;