diff --git a/package-lock.json b/package-lock.json index 39a17e5aa..c560b3166 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,8 @@ "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.49.3", - "@stremio/stremio-icons": "5.4.1", + "@stremio/stremio-core-web": "0.49.4", + "@stremio/stremio-icons": "5.7.1", "@stremio/stremio-video": "0.0.60", "a-color-picker": "1.2.1", "bowser": "2.11.0", @@ -3374,10 +3374,9 @@ "integrity": "sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg==" }, "node_modules/@stremio/stremio-core-web": { - "version": "0.49.3", - "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.49.3.tgz", - "integrity": "sha512-Ql/08LbwU99IUL6fOLy+v1Iv75boHXpunEPScKgXJALdq/OV5tZLG/IycN0O+5+50Nc/NHrI6HslnMNLTWA8JQ==", - "license": "MIT", + "version": "0.49.4", + "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.49.4.tgz", + "integrity": "sha512-K9LJGKXs8juV3pZOHH6thWTwOShAhjFt9bLL6K1VlORAe6AiieZ2uRp9wdOwFmPX+UgzWLIOd0r2aFXJ4OsJCw==", "dependencies": { "@babel/runtime": "7.24.1" } @@ -3401,9 +3400,10 @@ "license": "MIT" }, "node_modules/@stremio/stremio-icons": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/@stremio/stremio-icons/-/stremio-icons-5.4.1.tgz", - "integrity": "sha512-7g4JP7tPRT1UDZxbuH/Urq7fc6te3joy8qyx/NGWIW7wO169TTISO7ZWdejzESvUVgZ/7i6rzkRmXZ3wefWcBg==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@stremio/stremio-icons/-/stremio-icons-5.7.1.tgz", + "integrity": "sha512-Z96p36LLX3G+ewMnFKmNZVsO/AtcHA33WQ3wGOYFubxiYADPRAkcLVU5rHIfiGSC9IUaUVhxQWTPVB9ScY4Q5Q==", + "license": "MIT", "workspaces": [ "react", "react-native", diff --git a/package.json b/package.json index 9599f3587..082111216 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.49.3", - "@stremio/stremio-icons": "5.4.1", + "@stremio/stremio-core-web": "0.49.4", + "@stremio/stremio-icons": "5.7.1", "@stremio/stremio-video": "0.0.60", "a-color-picker": "1.2.1", "bowser": "2.11.0", diff --git a/src/components/MetaPreview/MetaPreview.js b/src/components/MetaPreview/MetaPreview.js index c0e9fb165..13717919a 100644 --- a/src/components/MetaPreview/MetaPreview.js +++ b/src/components/MetaPreview/MetaPreview.js @@ -17,6 +17,7 @@ const ActionButton = require('./ActionButton'); const MetaLinks = require('./MetaLinks'); const MetaPreviewPlaceholder = require('./MetaPreviewPlaceholder'); const styles = require('./styles'); +const { Ratings } = require('./Ratings'); const ALLOWED_LINK_REDIRECTS = [ routesRegexp.search.regexp, @@ -24,7 +25,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 }, ref) => { +const MetaPreview = React.forwardRef(({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary, ratingInfo }, ref) => { const { t } = useTranslation(); const [shareModalOpen, openShareModal, closeShareModal] = useBinaryState(false); const linksGroups = React.useMemo(() => { @@ -232,6 +233,15 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou : null } + { + !compact && ratingInfo !== null ? + + : + null + } { linksGroups.has(CONSTANTS.SHARE_LINK_CATEGORY) && !compact ? @@ -287,7 +297,8 @@ MetaPreview.propTypes = { })), trailerStreams: PropTypes.array, inLibrary: PropTypes.bool, - toggleInLibrary: PropTypes.func + toggleInLibrary: PropTypes.func, + ratingInfo: PropTypes.object, }; module.exports = MetaPreview; diff --git a/src/components/MetaPreview/Ratings/Ratings.less b/src/components/MetaPreview/Ratings/Ratings.less new file mode 100644 index 000000000..f4c7d57c2 --- /dev/null +++ b/src/components/MetaPreview/Ratings/Ratings.less @@ -0,0 +1,62 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +@import (reference) '~stremio/common/screen-sizes.less'; + +@height: 4rem; +@width: 4rem; +@height-mobile: 3rem; +@width-mobile: 3rem; + + +.ratings-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + background-color: var(--overlay-color); + border-radius: 2rem; + height: @height; + width: fit-content; + + .icon-container { + display: flex; + justify-content: center; + align-items: center; + height: @height; + width: @width; + padding: 0 1rem; + cursor: pointer; + + .icon { + width: calc(@width / 2); + height: calc(@height / 2); + color: var(--primary-foreground-color); + opacity: 0.7; + transition: 0.3s all ease-in-out; + + &:hover { + opacity: 1; + } + } + + &.disabled { + pointer-events: none; + } + } +} + +@media @phone-landscape { + .ratings-container { + height: @height-mobile; + + .icon-container { + height: @height-mobile; + width: @width-mobile; + + .icon { + width: 1.75rem; + height: 1.75rem; + } + } + } +} \ No newline at end of file diff --git a/src/components/MetaPreview/Ratings/Ratings.tsx b/src/components/MetaPreview/Ratings/Ratings.tsx new file mode 100644 index 000000000..6bef0cc6d --- /dev/null +++ b/src/components/MetaPreview/Ratings/Ratings.tsx @@ -0,0 +1,31 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +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'; + +type Props = { + metaId?: string; + ratingInfo?: Loadable; + className?: string; +}; + +const Ratings = ({ ratingInfo, className }: Props) => { + const { onLiked, onLoved, liked, loved } = useRating(ratingInfo); + const disabled = useMemo(() => ratingInfo?.type !== 'Ready', [ratingInfo]); + + return ( +
+
+ +
+
+ +
+
+ ); +}; + +export default Ratings; diff --git a/src/components/MetaPreview/Ratings/index.ts b/src/components/MetaPreview/Ratings/index.ts new file mode 100644 index 000000000..0a00e9c2f --- /dev/null +++ b/src/components/MetaPreview/Ratings/index.ts @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +import Ratings from './Ratings'; + +export { Ratings }; diff --git a/src/components/MetaPreview/Ratings/useRating.ts b/src/components/MetaPreview/Ratings/useRating.ts new file mode 100644 index 000000000..286ad7284 --- /dev/null +++ b/src/components/MetaPreview/Ratings/useRating.ts @@ -0,0 +1,48 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +import { useMemo, useCallback } from 'react'; +import { useServices } from 'stremio/services'; + +const useRating = (ratingInfo?: Loadable) => { + const { core } = useServices(); + + const setRating = useCallback((status: Rating) => { + core.transport.dispatch({ + action: 'MetaDetails', + args: { + action: 'Rate', + args: status, + }, + }); + }, []); + + const status = useMemo(() => { + const content = ratingInfo?.type === 'Ready' ? ratingInfo.content as RatingInfo : null; + return content?.status; + }, [ratingInfo]); + + const liked = useMemo(() => { + return status === 'liked'; + }, [status]); + + const loved = useMemo(() => { + return status === 'loved'; + }, [status]); + + const onLiked = useCallback(() => { + setRating(status === 'liked' ? null : 'liked'); + }, [status]); + + const onLoved = useCallback(() => { + setRating(status === 'loved' ? null : 'loved'); + }, [status]); + + return { + onLiked, + onLoved, + liked, + loved, + }; +}; + +export default useRating; diff --git a/src/components/MetaPreview/styles.less b/src/components/MetaPreview/styles.less index 0725a384e..a614acf25 100644 --- a/src/components/MetaPreview/styles.less +++ b/src/components/MetaPreview/styles.less @@ -159,7 +159,6 @@ display: flex; flex-direction: row; align-items: flex-end; - max-height: 15rem; flex-wrap: wrap; padding-top: 3.5rem; overflow: visible; @@ -209,6 +208,11 @@ } } } + + .ratings { + margin-bottom: 1rem; + margin-right: 1rem; + } } .share-prompt { diff --git a/src/routes/Discover/Discover.js b/src/routes/Discover/Discover.js index 1fc16c706..6f32d1f9a 100644 --- a/src/routes/Discover/Discover.js +++ b/src/routes/Discover/Discover.js @@ -193,6 +193,8 @@ const Discover = ({ urlParams, queryParams }) => { trailerStreams={selectedMetaItem.trailerStreams} inLibrary={selectedMetaItem.inLibrary} toggleInLibrary={selectedMetaItem.inLibrary ? removeFromLibrary : addToLibrary} + metaId={selectedMetaItem.id} + like={selectedMetaItem.like} /> : discover.catalog !== null && discover.catalog.content.type === 'Loading' ? diff --git a/src/routes/MetaDetails/MetaDetails.js b/src/routes/MetaDetails/MetaDetails.js index da79df285..d806ffed5 100644 --- a/src/routes/MetaDetails/MetaDetails.js +++ b/src/routes/MetaDetails/MetaDetails.js @@ -168,6 +168,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} + metaId={metaDetails.metaItem.content.content.id} + ratingInfo={metaDetails.ratingInfo} />
} diff --git a/src/types/models/MetaDetails.d.ts b/src/types/models/MetaDetails.d.ts index c7eeafb1b..570ca5e88 100644 --- a/src/types/models/MetaDetails.d.ts +++ b/src/types/models/MetaDetails.d.ts @@ -24,4 +24,5 @@ type MetaDetails = { content: Loadable }[], title: string | null, + ratingInfo: Loadable | null, }; diff --git a/src/types/types.d.ts b/src/types/types.d.ts index 8f6d55730..06bbb0e19 100644 --- a/src/types/types.d.ts +++ b/src/types/types.d.ts @@ -68,3 +68,10 @@ type AudioTrack = { lang: string, origin: string, }; + +type Rating = 'liked' | 'loved' | null; + +type RatingInfo = { + metaId: string, + status: Rating, +};