From f8bb43ab9a2384c2203e9c7d81ae7471bde5e377 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 20 Nov 2023 11:05:48 +0100 Subject: [PATCH 01/55] feat: implement search history --- src/routes/Search/Search.js | 3 +++ src/routes/Search/useSearchHistory.d.ts | 2 ++ src/routes/Search/useSearchHistory.js | 10 ++++++++++ src/types/models/Ctx.d.ts | 3 +++ 4 files changed, 18 insertions(+) create mode 100644 src/routes/Search/useSearchHistory.d.ts create mode 100644 src/routes/Search/useSearchHistory.js diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index a2f5b758e..99f7d412e 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -8,6 +8,7 @@ const { useTranslation } = require('react-i18next'); const { default: Icon } = require('@stremio/stremio-icons/react'); const { Image, MainNavBars, MetaRow, MetaItem, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common'); const useSearch = require('./useSearch'); +const useSearchHistory = require('./useSearchHistory'); const styles = require('./styles'); const THRESHOLD = 100; @@ -15,6 +16,8 @@ const THRESHOLD = 100; const Search = ({ queryParams }) => { const { t } = useTranslation(); const [search, loadSearchRows] = useSearch(queryParams); + const searchHistory = useSearchHistory(); + console.log(searchHistory); const query = React.useMemo(() => { return search.selected !== null ? search.selected.extra.reduceRight((query, [name, value]) => { diff --git a/src/routes/Search/useSearchHistory.d.ts b/src/routes/Search/useSearchHistory.d.ts new file mode 100644 index 000000000..5049b5046 --- /dev/null +++ b/src/routes/Search/useSearchHistory.d.ts @@ -0,0 +1,2 @@ +declare const useSearchHistory: () => SearchHistory; +export = useSearchHistory; \ No newline at end of file diff --git a/src/routes/Search/useSearchHistory.js b/src/routes/Search/useSearchHistory.js new file mode 100644 index 000000000..5c4833392 --- /dev/null +++ b/src/routes/Search/useSearchHistory.js @@ -0,0 +1,10 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const useModelState = require('stremio/common/useModelState'); + +const useSearchHistory = () => { + const { searchHistory } = useModelState({ model: 'ctx' }); + return searchHistory; +}; + +module.exports = useSearchHistory; diff --git a/src/types/models/Ctx.d.ts b/src/types/models/Ctx.d.ts index 54ee1dc4e..ceb291cc7 100644 --- a/src/types/models/Ctx.d.ts +++ b/src/types/models/Ctx.d.ts @@ -56,7 +56,10 @@ type NotificationItem = { videoReleased: string, } +type SearchHistory = string[]; + type Ctx = { profile: Profile, notifications: Notifications, + searchHistory: SearchHistory, }; \ No newline at end of file From e8d26e450e77d7714d3b0cc5a784014553360d39 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 22 Nov 2023 08:13:06 +0100 Subject: [PATCH 02/55] feat: implement remote https endpoint settings --- src/common/Multiselect/Multiselect.js | 10 ++--- src/routes/Settings/Settings.js | 29 +++++++++++++ .../useStreamingServerSettingsInputs.js | 43 +++++++++++++++++-- src/types/models/Ctx.d.ts | 1 + 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/common/Multiselect/Multiselect.js b/src/common/Multiselect/Multiselect.js index df2488a47..a308393e3 100644 --- a/src/common/Multiselect/Multiselect.js +++ b/src/common/Multiselect/Multiselect.js @@ -14,17 +14,13 @@ const Multiselect = ({ className, mode, direction, title, disabled, dataset, ren const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false); const options = React.useMemo(() => { return Array.isArray(props.options) ? - props.options.filter((option) => { - return option && typeof option.value === 'string'; - }) + props.options : []; }, [props.options]); const selected = React.useMemo(() => { return Array.isArray(props.selected) ? - props.selected.filter((value) => { - return typeof value === 'string'; - }) + props.selected : []; }, [props.selected]); @@ -161,7 +157,7 @@ Multiselect.propTypes = { direction: PropTypes.any, title: PropTypes.string, options: PropTypes.arrayOf(PropTypes.shape({ - value: PropTypes.string.isRequired, + value: PropTypes.string, title: PropTypes.string, label: PropTypes.string })), diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 1baeb5511..423594acb 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -45,6 +45,8 @@ const Settings = () => { streamingServerUrlInput } = useProfileSettingsInputs(profile); const { + streamingServerRemoteUrlInput, + remoteEndpointSelect, cacheSizeSelect, torrentProfileSelect } = useStreamingServerSettingsInputs(streamingServer); @@ -531,6 +533,33 @@ const Settings = () => { + { + streamingServerRemoteUrlInput.value !== null ? +
+
+
Remote url
+
+
+
{streamingServerRemoteUrlInput.value}
+
+
+ : + null + } + { + remoteEndpointSelect !== null ? +
+
+
{ t('SETTINGS_HTTPS_ENDPOINT') }
+
+ +
+ : + null + } { cacheSizeSelect !== null ?
diff --git a/src/routes/Settings/useStreamingServerSettingsInputs.js b/src/routes/Settings/useStreamingServerSettingsInputs.js index 442829dbf..5f13f4a0a 100644 --- a/src/routes/Settings/useStreamingServerSettingsInputs.js +++ b/src/routes/Settings/useStreamingServerSettingsInputs.js @@ -54,6 +54,43 @@ const TORRENT_PROFILES = { const useStreamingServerSettingsInputs = (streamingServer) => { const { core } = useServices(); // TODO combine those useMemo in one + + const streamingServerRemoteUrlInput = React.useMemo(() => ({ + value: streamingServer.remoteUrl, + }), [streamingServer.remoteUrl]); + + const remoteEndpointSelect= React.useMemo(() => { + if (streamingServer.settings?.type !== 'Ready' || streamingServer.networkInfo?.type !== 'Ready') { + return null; + } + + return { + options: [ + { + label: 'Disabled', + value: null, + }, + ...streamingServer.networkInfo.content.availableInterfaces.map((address) => ({ + label: address, + value: address, + })) + ], + selected: [streamingServer.settings.content.remoteHttps], + onSelect: (event) => { + core.transport.dispatch({ + action: 'StreamingServer', + args: { + action: 'UpdateSettings', + args: { + ...streamingServer.settings.content, + remoteHttps: event.value, + } + } + }); + } + }; + }, [streamingServer.settings, streamingServer.networkInfo]); + const cacheSizeSelect = React.useMemo(() => { if (streamingServer.settings === null || streamingServer.settings.type !== 'Ready') { return null; @@ -75,7 +112,7 @@ const useStreamingServerSettingsInputs = (streamingServer) => { action: 'UpdateSettings', args: { ...streamingServer.settings.content, - cacheSize: JSON.parse(event.value) + cacheSize: JSON.parse(event.value), } } }); @@ -121,14 +158,14 @@ const useStreamingServerSettingsInputs = (streamingServer) => { action: 'UpdateSettings', args: { ...streamingServer.settings.content, - ...JSON.parse(event.value) + ...JSON.parse(event.value), } } }); } }; }, [streamingServer.settings]); - return { cacheSizeSelect, torrentProfileSelect }; + return { streamingServerRemoteUrlInput, remoteEndpointSelect, cacheSizeSelect, torrentProfileSelect }; }; module.exports = useStreamingServerSettingsInputs; diff --git a/src/types/models/Ctx.d.ts b/src/types/models/Ctx.d.ts index 54ee1dc4e..235d6a6e7 100644 --- a/src/types/models/Ctx.d.ts +++ b/src/types/models/Ctx.d.ts @@ -28,6 +28,7 @@ type Settings = { seekTimeDuration: number, seekShortTimeDuration: number, streamingServerUrl: string, + remoteHttps: string | null, streamingServerWarningDismissed: Date | null, subtitlesBackgroundColor: string, subtitlesBold: boolean, From 87b6278894d8fff0282b6371339a8e2800b5a941 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 27 Nov 2023 19:14:06 +0100 Subject: [PATCH 03/55] refactor(Player): move statistics logic to a hook --- src/routes/Player/Player.js | 27 +------ .../Player/StatisticsMenu/StatisticsMenu.js | 31 ++----- src/routes/Player/useStatistics.js | 81 +++++++++++++++++++ 3 files changed, 91 insertions(+), 48 deletions(-) create mode 100644 src/routes/Player/useStatistics.js diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index bad9ff602..a7ac60687 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -22,6 +22,7 @@ const SpeedMenu = require('./SpeedMenu'); const Video = require('./Video'); const usePlayer = require('./usePlayer'); const useSettings = require('./useSettings'); +const useStatistics = require('./useStatistics'); const styles = require('./styles'); const Player = ({ urlParams, queryParams }) => { @@ -36,6 +37,7 @@ const Player = ({ urlParams, queryParams }) => { const [player, videoParamsChanged, timeChanged, pausedChanged, ended] = usePlayer(urlParams); const [settings, updateSettings] = useSettings(); const streamingServer = useStreamingServer(); + const statistics = useStatistics(player, streamingServer); const routeFocused = useRouteFocused(); const toast = useToast(); const [, , , toggleFullscreen] = useFullscreen(); @@ -363,26 +365,6 @@ const Player = ({ urlParams, queryParams }) => { } } }, [player.nextVideo, videoState.time, videoState.duration]); - React.useEffect(() => { - if (player.selected && player.selected.stream && typeof player.selected.stream.infoHash === 'string' && typeof player.selected.stream.fileIdx === 'number') { - const { infoHash, fileIdx } = player.selected.stream; - const getStatistics = () => { - core.transport.dispatch({ - action: 'StreamingServer', - args: { - action: 'GetStatistics', - args: { - infoHash, - fileIdx, - } - } - }); - }; - getStatistics(); - const statisticsInterval = setInterval(getStatistics, 5000); - return () => clearInterval(statisticsInterval); - } - }, [player.selected]); React.useEffect(() => { if (!defaultSubtitlesSelected.current) { const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang); @@ -692,7 +674,7 @@ const Player = ({ urlParams, queryParams }) => { metaItem={player.metaItem} nextVideo={player.nextVideo} stream={player.selected !== null ? player.selected.stream : null} - statistics={streamingServer.statistics} + statistics={statistics} onPlayRequested={onPlayRequested} onPauseRequested={onPauseRequested} onNextVideoRequested={onNextVideoRequested} @@ -725,8 +707,7 @@ const Player = ({ urlParams, queryParams }) => { statisticsMenuOpen ? : null diff --git a/src/routes/Player/StatisticsMenu/StatisticsMenu.js b/src/routes/Player/StatisticsMenu/StatisticsMenu.js index 5ea9b5fdf..6bab8ecf5 100644 --- a/src/routes/Player/StatisticsMenu/StatisticsMenu.js +++ b/src/routes/Player/StatisticsMenu/StatisticsMenu.js @@ -5,28 +5,7 @@ const classNames = require('classnames'); const PropTypes = require('prop-types'); const styles = require('./styles.less'); -const StatisticsMenu = ({ className, stream, statistics }) => { - const peers = React.useMemo(() => { - return statistics.type === 'Ready' && statistics.content?.peers ? - statistics.content.peers - : - 0; - }, [statistics]); - - const speed = React.useMemo(() => { - return statistics.type === 'Ready' && statistics.content?.downloadSpeed ? - (statistics.content.downloadSpeed / 1000 / 1000).toFixed(2) - : - 0; - }, [statistics]); - - const completed = React.useMemo(() => { - return statistics.type === 'Ready' && statistics.content?.streamProgress ? - (statistics.content.streamProgress * 100).toFixed(2) - : - 0; - }, [statistics]); - +const StatisticsMenu = ({ className, peers, speed, completed, infoHash }) => { return (
@@ -63,7 +42,7 @@ const StatisticsMenu = ({ className, stream, statistics }) => { Info Hash
- { stream.infoHash } + { infoHash }
@@ -72,8 +51,10 @@ const StatisticsMenu = ({ className, stream, statistics }) => { StatisticsMenu.propTypes = { className: PropTypes.string, - stream: PropTypes.object, - statistics: PropTypes.object, + peers: PropTypes.number, + speed: PropTypes.number, + completed: PropTypes.number, + infoHash: PropTypes.string, }; module.exports = StatisticsMenu; diff --git a/src/routes/Player/useStatistics.js b/src/routes/Player/useStatistics.js new file mode 100644 index 000000000..915ada2a8 --- /dev/null +++ b/src/routes/Player/useStatistics.js @@ -0,0 +1,81 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const React = require('react'); +const { useServices } = require('stremio/services'); + +const useStatistics = (player, streamingServer) => { + const { core } = useServices(); + + const statistics = React.useMemo(() => { + return streamingServer.statistics?.type === 'Ready' ? + streamingServer.statistics.content + : + null; + }, [streamingServer.statistics]); + + const stream = React.useMemo(() => { + return player?.selected?.stream ? + player.selected.stream + : + null; + }, [player.selected]); + + const infoHash = React.useMemo(() => { + return stream?.infoHash ? + stream?.infoHash + : + null; + }, [stream]); + + const peers = React.useMemo(() => { + return statistics?.peers ? + statistics.peers + : + 0; + }, [statistics]); + + const speed = React.useMemo(() => { + return statistics?.downloadSpeed ? + parseFloat((statistics.downloadSpeed / 1000 / 1000).toFixed(2)) + : + 0; + }, [statistics]); + + const completed = React.useMemo(() => { + return statistics?.streamProgress ? + parseFloat((statistics.streamProgress * 100).toFixed(2)) + : + 0; + }, [statistics]); + + React.useEffect(() => { + if (stream) { + const { infoHash, fileIdx } = stream; + const getStatistics = () => { + core.transport.dispatch({ + action: 'StreamingServer', + args: { + action: 'GetStatistics', + args: { + infoHash, + fileIdx, + } + } + }); + }; + getStatistics(); + + const statisticsInterval = setInterval(getStatistics, 5000); + return () => clearInterval(statisticsInterval); + } + }, [stream]); + + return { + infoHash, + peers, + speed, + completed, + }; +}; + +module.exports = useStatistics; From fcc3317dc83569f813444c9c3de449f973d34d54 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 20 Nov 2023 11:05:48 +0100 Subject: [PATCH 04/55] feat: implement search history --- src/routes/Search/Search.js | 3 +++ src/routes/Search/useSearchHistory.d.ts | 2 ++ src/routes/Search/useSearchHistory.js | 10 ++++++++++ src/types/models/Ctx.d.ts | 3 +++ 4 files changed, 18 insertions(+) create mode 100644 src/routes/Search/useSearchHistory.d.ts create mode 100644 src/routes/Search/useSearchHistory.js diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index a2f5b758e..99f7d412e 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -8,6 +8,7 @@ const { useTranslation } = require('react-i18next'); const { default: Icon } = require('@stremio/stremio-icons/react'); const { Image, MainNavBars, MetaRow, MetaItem, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common'); const useSearch = require('./useSearch'); +const useSearchHistory = require('./useSearchHistory'); const styles = require('./styles'); const THRESHOLD = 100; @@ -15,6 +16,8 @@ const THRESHOLD = 100; const Search = ({ queryParams }) => { const { t } = useTranslation(); const [search, loadSearchRows] = useSearch(queryParams); + const searchHistory = useSearchHistory(); + console.log(searchHistory); const query = React.useMemo(() => { return search.selected !== null ? search.selected.extra.reduceRight((query, [name, value]) => { diff --git a/src/routes/Search/useSearchHistory.d.ts b/src/routes/Search/useSearchHistory.d.ts new file mode 100644 index 000000000..5049b5046 --- /dev/null +++ b/src/routes/Search/useSearchHistory.d.ts @@ -0,0 +1,2 @@ +declare const useSearchHistory: () => SearchHistory; +export = useSearchHistory; \ No newline at end of file diff --git a/src/routes/Search/useSearchHistory.js b/src/routes/Search/useSearchHistory.js new file mode 100644 index 000000000..5c4833392 --- /dev/null +++ b/src/routes/Search/useSearchHistory.js @@ -0,0 +1,10 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const useModelState = require('stremio/common/useModelState'); + +const useSearchHistory = () => { + const { searchHistory } = useModelState({ model: 'ctx' }); + return searchHistory; +}; + +module.exports = useSearchHistory; diff --git a/src/types/models/Ctx.d.ts b/src/types/models/Ctx.d.ts index 54ee1dc4e..ceb291cc7 100644 --- a/src/types/models/Ctx.d.ts +++ b/src/types/models/Ctx.d.ts @@ -56,7 +56,10 @@ type NotificationItem = { videoReleased: string, } +type SearchHistory = string[]; + type Ctx = { profile: Profile, notifications: Notifications, + searchHistory: SearchHistory, }; \ No newline at end of file From 4f0d2e3991f2745c10e41d48c18a48d9f3b209ad Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 20 Nov 2023 11:05:48 +0100 Subject: [PATCH 05/55] feat: implement search history --- src/routes/Search/Search.js | 3 +++ src/routes/Search/useSearchHistory.d.ts | 2 ++ src/routes/Search/useSearchHistory.js | 26 +++++++++++++++++++++++++ src/types/models/Ctx.d.ts | 3 +++ 4 files changed, 34 insertions(+) create mode 100644 src/routes/Search/useSearchHistory.d.ts create mode 100644 src/routes/Search/useSearchHistory.js diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index a2f5b758e..99f7d412e 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -8,6 +8,7 @@ const { useTranslation } = require('react-i18next'); const { default: Icon } = require('@stremio/stremio-icons/react'); const { Image, MainNavBars, MetaRow, MetaItem, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common'); const useSearch = require('./useSearch'); +const useSearchHistory = require('./useSearchHistory'); const styles = require('./styles'); const THRESHOLD = 100; @@ -15,6 +16,8 @@ const THRESHOLD = 100; const Search = ({ queryParams }) => { const { t } = useTranslation(); const [search, loadSearchRows] = useSearch(queryParams); + const searchHistory = useSearchHistory(); + console.log(searchHistory); const query = React.useMemo(() => { return search.selected !== null ? search.selected.extra.reduceRight((query, [name, value]) => { diff --git a/src/routes/Search/useSearchHistory.d.ts b/src/routes/Search/useSearchHistory.d.ts new file mode 100644 index 000000000..2d317ab4e --- /dev/null +++ b/src/routes/Search/useSearchHistory.d.ts @@ -0,0 +1,2 @@ +declare const useSearchHistory: () => { items: SearchHistory, clear: () => {} }; +export = useSearchHistory; \ No newline at end of file diff --git a/src/routes/Search/useSearchHistory.js b/src/routes/Search/useSearchHistory.js new file mode 100644 index 000000000..99c6f4479 --- /dev/null +++ b/src/routes/Search/useSearchHistory.js @@ -0,0 +1,26 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const React = require('react'); +const useModelState = require('stremio/common/useModelState'); +const { useServices } = require('stremio/services'); + +const useSearchHistory = () => { + const { core } = useServices(); + const { searchHistory: items } = useModelState({ model: 'ctx' }); + + const clear = React.useCallback(() => { + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'ClearSearchHistory', + }, + }); + }, []); + + return { + items, + clear, + }; +}; + +module.exports = useSearchHistory; diff --git a/src/types/models/Ctx.d.ts b/src/types/models/Ctx.d.ts index 54ee1dc4e..ceb291cc7 100644 --- a/src/types/models/Ctx.d.ts +++ b/src/types/models/Ctx.d.ts @@ -56,7 +56,10 @@ type NotificationItem = { videoReleased: string, } +type SearchHistory = string[]; + type Ctx = { profile: Profile, notifications: Notifications, + searchHistory: SearchHistory, }; \ No newline at end of file From b906cfdc413a7ca6df0e0d2a7b9c51e62ad34e09 Mon Sep 17 00:00:00 2001 From: kKaskak <117831817+kKaskak@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:23:01 +0200 Subject: [PATCH 06/55] feature: search history beta v1 --- .../HorizontalNavBar/SearchBar/SearchBar.js | 53 ++++++++++++++-- .../HorizontalNavBar/SearchBar/styles.less | 61 +++++++++++++++++++ src/routes/Search/Search.js | 3 - 3 files changed, 110 insertions(+), 7 deletions(-) diff --git a/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js b/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js index 742c7a692..8f8b31c9c 100644 --- a/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js +++ b/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js @@ -11,11 +11,15 @@ const TextInput = require('stremio/common/TextInput'); const useTorrent = require('stremio/common/useTorrent'); const { withCoreSuspender } = require('stremio/common/CoreSuspender'); const styles = require('./styles'); +const useSearchHistory = require('../../../../routes/Search/useSearchHistory'); const SearchBar = ({ className, query, active }) => { const { t } = useTranslation(); const routeFocused = useRouteFocused(); + const searchHistory = useSearchHistory(); const { createTorrentFromMagnet } = useTorrent(); + const [inputValue, setInputValue] = React.useState(query || ''); + const [historyActive, setHistoryActive] = React.useState(true); const searchInputRef = React.useRef(null); const searchBarOnClick = React.useCallback(() => { if (!active) { @@ -23,6 +27,8 @@ const SearchBar = ({ className, query, active }) => { } }, [active]); const queryInputOnChange = React.useCallback(() => { + setInputValue(searchInputRef.current.value); + setHistoryActive(true); try { createTorrentFromMagnet(searchInputRef.current.value); // eslint-disable-next-line no-empty @@ -32,6 +38,7 @@ const SearchBar = ({ className, query, active }) => { if (searchInputRef.current !== null) { const queryParams = new URLSearchParams([['search', searchInputRef.current.value]]); window.location = `#/search?${queryParams.toString()}`; + setHistoryActive(false); } }, []); React.useEffect(() => { @@ -39,6 +46,18 @@ const SearchBar = ({ className, query, active }) => { searchInputRef.current.focus(); } }, [routeFocused, active, query]); + const queryInputClear = React.useCallback(() => { + searchInputRef.current.value = ''; + setInputValue(''); + window.location = '#/search'; + }, []); + const historyInputSearch = React.useCallback((event) => { + const queryParams = new URLSearchParams([['search', event.target.innerText]]); + window.location = `#/search?${queryParams.toString()}`; + setHistoryActive(false); + + }, []); + return ( + ); }; diff --git a/src/common/NavBar/HorizontalNavBar/SearchBar/styles.less b/src/common/NavBar/HorizontalNavBar/SearchBar/styles.less index 094e6de03..80c6c0190 100644 --- a/src/common/NavBar/HorizontalNavBar/SearchBar/styles.less +++ b/src/common/NavBar/HorizontalNavBar/SearchBar/styles.less @@ -9,6 +9,8 @@ height: var(--search-bar-size); border-radius: var(--search-bar-size); background-color: var(--overlay-color); + position: relative; + overflow: visible; .search-input { flex: 1; @@ -46,4 +48,63 @@ opacity: 0.6; } } + + .search-history { + position: absolute; + width: 100%; + height: auto; + top: 100%; + left: 0; + z-index: 10; + padding: 1rem; + background-color: var(--modal-background-color); + border-radius: var(--border-radius); + + .search-history-actions { + display: flex; + justify-content: space-between; + width: 100%; + opacity: 0.8; + margin-bottom: 1rem; + + .search-history-label { + font-size: 0.8rem; + color: var(--primary-foreground-color); + } + + .search-history-clear { + cursor: pointer; + color: var(--primary-foreground-color); + font-size: 0.8rem; + + &:hover { + opacity: 0.6; + } + } + } + + .search-history-items { + width: 90%; + margin: 0 auto; + display: flex; + justify-content: center; + align-items: flex-start; + flex-direction: column; + gap: 0.5rem 0 +; + .search-history-item { + color: var(--primary-foreground-color); + text-align: left; + text-decoration: none; + padding: 0.5rem 1rem; + border-radius: var(--border-radius); + width: 100%; + cursor: pointer; + + &:hover { + background-color: var(--secondary-background-color); + } + } + } + } } \ No newline at end of file diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index 99f7d412e..a2f5b758e 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -8,7 +8,6 @@ const { useTranslation } = require('react-i18next'); const { default: Icon } = require('@stremio/stremio-icons/react'); const { Image, MainNavBars, MetaRow, MetaItem, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common'); const useSearch = require('./useSearch'); -const useSearchHistory = require('./useSearchHistory'); const styles = require('./styles'); const THRESHOLD = 100; @@ -16,8 +15,6 @@ const THRESHOLD = 100; const Search = ({ queryParams }) => { const { t } = useTranslation(); const [search, loadSearchRows] = useSearch(queryParams); - const searchHistory = useSearchHistory(); - console.log(searchHistory); const query = React.useMemo(() => { return search.selected !== null ? search.selected.extra.reduceRight((query, [name, value]) => { From c327412deb334ea91304be83cded2dbff4bd6945 Mon Sep 17 00:00:00 2001 From: kKaskak <117831817+kKaskak@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:38:57 +0200 Subject: [PATCH 07/55] refactor & feature: search history beta v2 --- .../HorizontalNavBar/SearchBar/SearchBar.js | 39 ++++++++++++------- .../HorizontalNavBar/SearchBar/styles.less | 3 +- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js b/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js index 8f8b31c9c..a9b7555cc 100644 --- a/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js +++ b/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js @@ -21,6 +21,7 @@ const SearchBar = ({ className, query, active }) => { const [inputValue, setInputValue] = React.useState(query || ''); const [historyActive, setHistoryActive] = React.useState(true); const searchInputRef = React.useRef(null); + const searchHistoryRef = React.useRef(null); const searchBarOnClick = React.useCallback(() => { if (!active) { window.location = '#/search'; @@ -28,17 +29,22 @@ const SearchBar = ({ className, query, active }) => { }, [active]); const queryInputOnChange = React.useCallback(() => { setInputValue(searchInputRef.current.value); - setHistoryActive(true); try { createTorrentFromMagnet(searchInputRef.current.value); // eslint-disable-next-line no-empty } catch { } }, []); - const queryInputOnSubmit = React.useCallback(() => { + const queryInputOnSubmit = React.useCallback((event) => { if (searchInputRef.current !== null) { - const queryParams = new URLSearchParams([['search', searchInputRef.current.value]]); - window.location = `#/search?${queryParams.toString()}`; - setHistoryActive(false); + const searchValue = event.target.innerText ? event.target.innerText : searchInputRef.current.value; + if (searchValue) { + const queryParams = new URLSearchParams([['search', searchValue]]); + window.location = `#/search?${queryParams.toString()}`; + setInputValue(searchValue); + if (event.key === 'Enter') { + setHistoryActive(false); + } + } } }, []); React.useEffect(() => { @@ -51,15 +57,17 @@ const SearchBar = ({ className, query, active }) => { setInputValue(''); window.location = '#/search'; }, []); - const historyInputSearch = React.useCallback((event) => { - const queryParams = new URLSearchParams([['search', event.target.innerText]]); - window.location = `#/search?${queryParams.toString()}`; - setHistoryActive(false); - - }, []); + const handleBlur = (event) => { + if (!searchHistoryRef?.current?.contains(event.relatedTarget)) { + setHistoryActive(false); + } + }; + const handleClick = () => { + setHistoryActive((prev) => !prev); + }; return ( -