Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@width-mobile: 3rem;


.ratings-container {
.group-container {
display: flex;
flex-direction: row;
align-items: center;
Expand Down Expand Up @@ -45,7 +45,7 @@
}

@media @phone-landscape {
.ratings-container {
.group-container {
height: @height-mobile;

.icon-container {
Expand Down
45 changes: 45 additions & 0 deletions src/components/ActionsGroup/ActionsGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (C) 2017-2025 Smart code 203358507

import classNames from 'classnames';
import React from 'react';
import Icon from '@stremio/stremio-icons/react';
import { Tooltip } from 'stremio/common/Tooltips';
import styles from './ActionsGroup.less';

type Item = {
icon: string;
label?: string;
filled?: string;
disabled?: boolean;
className?: string;
onClick?: () => void;
};

type Props = {
items: Item[];
className?: string;
};

const ActionsGroup = ({ items, className }: Props) => {
return (
<div className={classNames(styles['group-container'], className)}>
{
items.map((item, index) => (
<div
key={index}
className={classNames(styles['icon-container'], item.className, { [styles['disabled']]: item.disabled })}
onClick={item.onClick}
>
{
item.label &&
<Tooltip label={item.label} position={'top'} />
}
<Icon name={item.icon} className={styles['icon']} />
</div>
))
}
</div>
);
};

export default ActionsGroup;
6 changes: 6 additions & 0 deletions src/components/ActionsGroup/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (C) 2017-2025 Smart code 203358507

import ActionsGroup from './ActionsGroup';

export default ActionsGroup;

37 changes: 22 additions & 15 deletions src/components/MetaPreview/MetaPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const { useTranslation } = require('react-i18next');
const { default: Icon } = require('@stremio/stremio-icons/react');
const { default: Button } = require('stremio/components/Button');
const { default: Image } = require('stremio/components/Image');
const { default: ActionsGroup } = require('stremio/components/ActionsGroup');
const ModalDialog = require('stremio/components/ModalDialog');
const SharePrompt = require('stremio/components/SharePrompt');
const CONSTANTS = require('stremio/common/CONSTANTS');
Expand All @@ -25,7 +26,7 @@ const ALLOWED_LINK_REDIRECTS = [
routesRegexp.metadetails.regexp
];

const MetaPreview = React.forwardRef(({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary, ratingInfo }, ref) => {
const MetaPreview = React.forwardRef(({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary, watched, toggleWatched, ratingInfo }, ref) => {
const { t } = useTranslation();
const [shareModalOpen, openShareModal, closeShareModal] = useBinaryState(false);
const linksGroups = React.useMemo(() => {
Expand Down Expand Up @@ -98,6 +99,18 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou
const renderLogoFallback = React.useCallback(() => (
<div className={styles['logo-placeholder']}>{name}</div>
), [name]);
const metaItemActions = React.useMemo(() => [
{
icon: inLibrary ? 'remove-from-library' : 'add-to-library',
label: inLibrary ? t('REMOVE_FROM_LIB') : t('ADD_TO_LIB'),
onClick: typeof toggleInLibrary === 'function' ? toggleInLibrary : null,
},
{
icon: watched ? 'eye-off' : 'eye',
label: watched ? t('CTX_MARK_UNWATCHED') : t('CTX_MARK_WATCHED'),
onClick: typeof toggleWatched === 'function' ? toggleWatched : undefined,
},
], [inLibrary, watched, toggleInLibrary, toggleWatched]);
return (
<div className={classnames(className, styles['meta-preview-container'], { [styles['compact']]: compact })} ref={ref}>
{
Expand Down Expand Up @@ -195,19 +208,6 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou
}
</div>
<div className={styles['action-buttons-container']}>
{
typeof toggleInLibrary === 'function' ?
<ActionButton
className={styles['action-button']}
icon={inLibrary ? 'remove-from-library' : 'add-to-library'}
label={inLibrary ? t('REMOVE_FROM_LIB') : t('ADD_TO_LIB')}
tooltip={compact}
tabIndex={compact ? -1 : 0}
onClick={toggleInLibrary}
/>
:
null
}
{
typeof trailerHref === 'string' ?
<ActionButton
Expand All @@ -221,6 +221,11 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou
:
null
}
{
typeof toggleInLibrary === 'function' && typeof toggleWatched === 'function'
? <ActionsGroup items={metaItemActions} className={styles['group-container']} />
: null
}
{
typeof showHref === 'string' && compact ?
<ActionButton
Expand All @@ -237,7 +242,7 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou
!compact && ratingInfo !== null ?
<Ratings
ratingInfo={ratingInfo}
className={styles['ratings']}
className={styles['group-container']}
/>
:
null
Expand Down Expand Up @@ -298,6 +303,8 @@ MetaPreview.propTypes = {
trailerStreams: PropTypes.array,
inLibrary: PropTypes.bool,
toggleInLibrary: PropTypes.func,
watched: PropTypes.bool,
toggleWatched: PropTypes.func,
ratingInfo: PropTypes.object,
};

Expand Down
26 changes: 15 additions & 11 deletions src/components/MetaPreview/Ratings/Ratings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import React, { useMemo } from 'react';
import useRating from './useRating';
import styles from './Ratings.less';
import Icon from '@stremio/stremio-icons/react';
import classNames from 'classnames';
import { ActionsGroup } from 'stremio/components';

type Props = {
metaId?: string;
Expand All @@ -16,15 +14,21 @@ const Ratings = ({ ratingInfo, className }: Props) => {
const { onLiked, onLoved, liked, loved } = useRating(ratingInfo);
const disabled = useMemo(() => ratingInfo?.type !== 'Ready', [ratingInfo]);

const items = useMemo(() => [
{
icon: liked ? 'thumbs-up' : 'thumbs-up-outline',
disabled,
onClick: onLiked,
},
{
icon: loved ? 'heart' : 'heart-outline',
disabled,
onClick: onLoved,
},
], [liked, loved, disabled]);

return (
<div className={classNames(styles['ratings-container'], className)}>
<div className={classNames(styles['icon-container'], { [styles['disabled']]: disabled })} onClick={onLiked}>
<Icon name={liked ? 'thumbs-up' : 'thumbs-up-outline'} className={styles['icon']} />
</div>
<div className={classNames(styles['icon-container'], { [styles['disabled']]: disabled })} onClick={onLoved}>
<Icon name={loved ? 'heart' : 'heart-outline'} className={styles['icon']} />
</div>
</div>
<ActionsGroup items={items} className={className} />
);
};

Expand Down
29 changes: 19 additions & 10 deletions src/components/MetaPreview/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
.action-buttons-container {
justify-content: space-between;

.action-button:not(:last-child) {
.action-button:not(:last-child), .group-container:not(:last-child) {
margin-right: 0;
}
}
Expand Down Expand Up @@ -207,11 +207,20 @@
}
}
}
}

.group-container {
margin-bottom: 1rem;

&:global(.wide) {
width: auto;
padding: 0 2rem;
border-radius: 4rem;
}

.ratings {
margin-bottom: 1rem;
margin-right: 1rem;
&:not(:last-child) {
margin-right: 1rem;
}
}
}
}

Expand All @@ -233,17 +242,13 @@
padding-top: 1.5rem;
gap: 0.5rem;

.action-button {
.action-button, .group-container {
padding: 0 1.5rem !important;
margin-right: 0rem !important;
height: 3rem;
border-radius: 2rem;
}
}

.ratings {
margin-right: 0;
}
}
}

Expand Down Expand Up @@ -272,6 +277,10 @@
&::-webkit-scrollbar {
display: none;
}

.action-button {
padding: 0 1rem !important;
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import TextInput from './TextInput';
import Toggle from './Toggle';
import Transition from './Transition';
import Video from './Video';
import ActionsGroup from './ActionsGroup';

export {
AddonDetailsModal,
Expand Down Expand Up @@ -65,4 +66,5 @@ export {
Toggle,
Transition,
Video,
ActionsGroup
};
36 changes: 28 additions & 8 deletions src/routes/Discover/Discover.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const { CONSTANTS, useBinaryState, useOnScrollToBottom, withCoreSuspender } = re
const { AddonDetailsModal, Button, DelayedRenderer, Image, MainNavBars, MetaItem, MetaPreview, ModalDialog, MultiselectMenu } = require('stremio/components');
const useDiscover = require('./useDiscover');
const useSelectableInputs = require('./useSelectableInputs');
const useMetaDetails = require('../MetaDetails/useMetaDetails');
const styles = require('./styles');

const SCROLL_TO_BOTTOM_THRESHOLD = 400;
Expand All @@ -23,6 +24,18 @@ const Discover = ({ urlParams, queryParams }) => {
const [addonModalOpen, openAddonModal, closeAddonModal] = useBinaryState(false);
const [selectedMetaItemIndex, setSelectedMetaItemIndex] = React.useState(0);

const { selectedMetaItem, metaDetailsParams } = React.useMemo(() => {
const item = discover.catalog?.content.type === 'Ready' &&
discover.catalog.content.content[selectedMetaItemIndex] || null;

return {
selectedMetaItem: item,
metaDetailsParams: item ? { type: item.type, id: item.id } : {}
};
}, [discover.catalog, selectedMetaItemIndex]);

useMetaDetails(metaDetailsParams);

const metasContainerRef = React.useRef();
const metaPreviewRef = React.useRef();

Expand All @@ -40,14 +53,6 @@ const Discover = ({ urlParams, queryParams }) => {
}
}
}, [hasNextPage, loadNextPage]);
const selectedMetaItem = React.useMemo(() => {
return discover.catalog !== null &&
discover.catalog.content.type === 'Ready' &&
discover.catalog.content.content[selectedMetaItemIndex] ?
discover.catalog.content.content[selectedMetaItemIndex]
:
null;
}, [discover.catalog, selectedMetaItemIndex]);
const addToLibrary = React.useCallback(() => {
if (selectedMetaItem === null) {
return;
Expand All @@ -74,6 +79,19 @@ const Discover = ({ urlParams, queryParams }) => {
}
});
}, [selectedMetaItem]);
const toggleWatched = React.useCallback(() => {
if (selectedMetaItem === null) {
return;
}

core.transport.dispatch({
action: 'MetaDetails',
args: {
action: 'MarkAsWatched',
args: !selectedMetaItem.watched
}
});
}, [selectedMetaItem]);
const metaItemsOnFocusCapture = React.useCallback((event) => {
if (event.target.dataset.index !== null && !isNaN(event.target.dataset.index)) {
setSelectedMetaItemIndex(parseInt(event.target.dataset.index, 10));
Expand Down Expand Up @@ -193,6 +211,8 @@ const Discover = ({ urlParams, queryParams }) => {
trailerStreams={selectedMetaItem.trailerStreams}
inLibrary={selectedMetaItem.inLibrary}
toggleInLibrary={selectedMetaItem.inLibrary ? removeFromLibrary : addToLibrary}
watched={selectedMetaItem.watched}
toggleWatched={toggleWatched}
metaId={selectedMetaItem.id}
like={selectedMetaItem.like}
/>
Expand Down
15 changes: 15 additions & 0 deletions src/routes/MetaDetails/MetaDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ const MetaDetails = ({ urlParams, queryParams }) => {
}
});
}, [metaDetails]);
const toggleWatched = React.useCallback(() => {
if (metaDetails.metaItem === null || metaDetails.metaItem.content.type !== 'Ready') {
return;
}

core.transport.dispatch({
action: 'MetaDetails',
args: {
action: 'MarkAsWatched',
args: !metaDetails.metaItem.content.content.watched
}
});
}, [metaDetails]);
const toggleNotifications = React.useCallback(() => {
if (metaDetails.libraryItem) {
core.transport.dispatch({
Expand Down Expand Up @@ -168,6 +181,8 @@ const MetaDetails = ({ urlParams, queryParams }) => {
trailerStreams={metaDetails.metaItem.content.content.trailerStreams}
inLibrary={metaDetails.metaItem.content.content.inLibrary}
toggleInLibrary={metaDetails.metaItem.content.content.inLibrary ? removeFromLibrary : addToLibrary}
watched={metaDetails.metaItem.content.content.watched}
toggleWatched={toggleWatched}
metaId={metaDetails.metaItem.content.content.id}
ratingInfo={metaDetails.ratingInfo}
/>
Expand Down