From 344b14d14341a62d55e8bca9947bfe00e882df57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Wed, 5 Apr 2023 16:37:12 +0200 Subject: [PATCH 01/10] Add usePreloader hook and implement it into Container.tsx. Also add prop preloadCount: number which defaults to 1, meaning it will load one story forward by default --- .idea/.gitignore | 5 +++ .../inspectionProfiles/profiles_settings.xml | 5 +++ .idea/modules.xml | 8 ++++ .idea/react-insta-stories.iml | 12 +++++ .idea/vcs.xml | 6 +++ example/package-lock.json | 4 +- example/src/App.js | 19 ++++++++ src/components/Container.tsx | 5 +++ src/index.tsx | 10 +++-- src/interfaces.tsx | 2 + src/util/usePreLoader.ts | 45 +++++++++++++++++++ 11 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/react-insta-stories.iml create mode 100644 .idea/vcs.xml create mode 100644 src/util/usePreLoader.ts diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..b58b603f --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..0eefe328 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..ac313a62 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/react-insta-stories.iml b/.idea/react-insta-stories.iml new file mode 100644 index 00000000..0c8867d7 --- /dev/null +++ b/.idea/react-insta-stories.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/example/package-lock.json b/example/package-lock.json index 79ea84bf..8bc3ad67 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -28,7 +28,7 @@ } }, "..": { - "version": "2.5.0", + "version": "2.5.8", "license": "MIT", "devDependencies": { "@babel/core": "^7.5.5", @@ -48,6 +48,7 @@ "ts-loader": "^9.4.2", "typescript": "^4.9.5", "webpack": "^5.76.2", + "webpack-bundle-analyzer": "^4.8.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.12.0" }, @@ -11484,6 +11485,7 @@ "ts-loader": "^9.4.2", "typescript": "^4.9.5", "webpack": "^5.76.2", + "webpack-bundle-analyzer": "^4.8.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.12.0" }, diff --git a/example/src/App.js b/example/src/App.js index 38b02553..fa67bb5e 100644 --- a/example/src/App.js +++ b/example/src/App.js @@ -198,6 +198,7 @@ function App() {
(0); @@ -29,9 +30,12 @@ export default function () { preventDefault, storyContainerStyles = {}, onAllStoriesEnd, + preloadCount, } = useContext(GlobalContext); const { stories } = useContext(StoriesContext); + usePreLoader(stories, currentId, preloadCount); + useEffect(() => { if (typeof currentIndex === "number") { if (currentIndex >= 0 && currentIndex < stories.length) { @@ -45,6 +49,7 @@ export default function () { } }, [currentIndex]); + useEffect(() => { if (typeof isPaused === "boolean") { setPause(isPaused); diff --git a/src/index.tsx b/src/index.tsx index f79d11d8..c1dd7e0e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -29,9 +29,12 @@ const ReactInstaStories = function (props: ReactInstaStoriesProps) { onStoryEnd: props.onStoryEnd, onAllStoriesEnd: props.onAllStoriesEnd, keyboardNavigation: props.keyboardNavigation, - preventDefault: props.preventDefault + preventDefault: props.preventDefault, + preloadCount: props.preloadCount, } const [stories, setStories] = useState<{ stories: Story[] }>({ stories: generateStories(props.stories, renderers) }); + + useEffect(() => { setStories({ stories: generateStories(props.stories, renderers) }); }, [props.stories, props.renderers]); @@ -64,10 +67,11 @@ const generateStories = (stories: Story[], renderers: { renderer: Renderer, test ReactInstaStories.defaultProps = { width: 360, height: 640, - defaultInterval: 4000 + defaultInterval: 4000, + preloadCount: 1, } export const WithHeader = withHeader; export const WithSeeMore = withSeeMore; -export default ReactInstaStories \ No newline at end of file +export default ReactInstaStories diff --git a/src/interfaces.tsx b/src/interfaces.tsx index af171c99..e352def1 100644 --- a/src/interfaces.tsx +++ b/src/interfaces.tsx @@ -25,6 +25,7 @@ export interface ReactInstaStoriesProps { onStoryEnd?: Function; keyboardNavigation?: boolean; preventDefault?: boolean; + preloadCount?: number; } export interface GlobalCtx { @@ -51,6 +52,7 @@ export interface GlobalCtx { onStoryEnd?: Function; keyboardNavigation?: boolean; preventDefault?: boolean; + preloadCount?: number; } type NumberOrString = number | string; diff --git a/src/util/usePreLoader.ts b/src/util/usePreLoader.ts new file mode 100644 index 00000000..9dc868d6 --- /dev/null +++ b/src/util/usePreLoader.ts @@ -0,0 +1,45 @@ +import {Story} from "../interfaces"; +import {useEffect, useState} from "react"; + + +// Caches given Story[] using HTMLImageElement and HTMLVideoElement +const cacheContent = async (contents: Story[]) => { + const promises = contents.map((content) => { + return new Promise(function (resolve, reject) { + if(!content.url) return + + if(content.type === 'video') { + const video = document.createElement('video'); + video.src = content.url; + video.onloadeddata = resolve; + video.onerror = reject; + return; + } + + const img = new Image(); + img.src = content.url; + img.onload = resolve; + img.onerror = reject; + }) + }) + + await Promise.all(promises); +} + + +// Preloads images and videos from given Story[] using a cursor and preloadCount +// Preload count is the number of images/videos to preload after the cursor +// Cursor is the current index to start preloading from +export const usePreLoader = (contents: Story[], cursor: number, preloadCount: number) => { + useEffect(() => { + const start = cursor + 1; + const end = cursor + preloadCount + 1; + const toPreload = contents.slice(start, end); + + cacheContent(toPreload).then(() => { + console.log('Preloaded from cursor:', cursor, 'preloadCount:', preloadCount) + }).catch((e) => { + console.error('Error preloading', e) + }) + }, [cursor, preloadCount, contents]) +} From f8edb576d4ff10ce5b07f9c37d96fa5feed90455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Wed, 5 Apr 2023 16:38:17 +0200 Subject: [PATCH 02/10] Remove .idea/ --- .idea/.gitignore | 5 ----- .idea/inspectionProfiles/profiles_settings.xml | 5 ----- .idea/modules.xml | 8 -------- .idea/react-insta-stories.iml | 12 ------------ .idea/vcs.xml | 6 ------ 5 files changed, 36 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/react-insta-stories.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index b58b603f..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 0eefe328..00000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index ac313a62..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/react-insta-stories.iml b/.idea/react-insta-stories.iml deleted file mode 100644 index 0c8867d7..00000000 --- a/.idea/react-insta-stories.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddf..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 182d342dcc8c0738fb09bb37de49f51593f3daff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Thu, 6 Apr 2023 13:23:25 +0200 Subject: [PATCH 03/10] Add readme description for preloadCount and remove then/catch from preloading --- .idea/vcs.xml | 6 ++++ .idea/workspace.xml | 68 ++++++++++++++++++++++++++++++++++++++++ example/src/App.js | 10 ++++++ src/util/usePreLoader.ts | 11 +++---- 4 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 00000000..673320ec --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + { + "keyToString": { + "RunOnceActivity.OpenProjectViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "WebServerToolWindowFactoryState": "false", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "settings.editor.selected.configurable": "com.github.copilot.settings.ApplicationConfigurable", + "ts.external.directory.path": "/Users/macbook/open-source/react-insta-stories/node_modules/typescript/lib", + "vue.rearranger.settings.migration": "true" + } +} + + + + + 1680702538376 + + + + + + + + + + file://$PROJECT_DIR$/src/components/Container.tsx + + + + + \ No newline at end of file diff --git a/example/src/App.js b/example/src/App.js index fa67bb5e..7d479294 100644 --- a/example/src/App.js +++ b/example/src/App.js @@ -318,6 +318,16 @@ const stories2 = [ "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4", type: "video", }, + { + url: + "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4", + type: "video", + }, + { + url: + "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4", + type: "video", + }, { content: Story2, }, diff --git a/src/util/usePreLoader.ts b/src/util/usePreLoader.ts index 9dc868d6..03bad8b4 100644 --- a/src/util/usePreLoader.ts +++ b/src/util/usePreLoader.ts @@ -1,5 +1,5 @@ import {Story} from "../interfaces"; -import {useEffect, useState} from "react"; +import {useEffect, useRef, useState} from "react"; // Caches given Story[] using HTMLImageElement and HTMLVideoElement @@ -31,15 +31,14 @@ const cacheContent = async (contents: Story[]) => { // Preload count is the number of images/videos to preload after the cursor // Cursor is the current index to start preloading from export const usePreLoader = (contents: Story[], cursor: number, preloadCount: number) => { + const urlsLoaded = useRef([] as string[]).current; + useEffect(() => { const start = cursor + 1; const end = cursor + preloadCount + 1; const toPreload = contents.slice(start, end); - cacheContent(toPreload).then(() => { - console.log('Preloaded from cursor:', cursor, 'preloadCount:', preloadCount) - }).catch((e) => { - console.error('Error preloading', e) - }) + // todo remove then/catch after testing + cacheContent(toPreload) }, [cursor, preloadCount, contents]) } From 31d08f4ae30b6d9124f15ed3ab9728022ce08c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Thu, 6 Apr 2023 13:26:54 +0200 Subject: [PATCH 04/10] Update readme + remove todo --- .idea/workspace.xml | 5 +++-- README.md | 47 ++++++++++++++++++++-------------------- src/util/usePreLoader.ts | 1 - 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 673320ec..ec9be04a 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,7 +5,8 @@ - + + diff --git a/README.md b/README.md index dd2c03ba..c39c504c 100644 --- a/README.md +++ b/README.md @@ -45,29 +45,30 @@ Here `stories` is an array of story objects, which can be of various types as de ## Props -| Property | Type | Default | Description | -| ---------------------- | --------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `stories` | [String/Object] | `required` | An array of image urls or array of story objects (options described below) | -| `renderers` ⚡️ | [Object] | `[]` | An array of renderer objects (options described below) | -| `defaultInterval` | Number | 1200 | Milliseconds duration for which a story persists | -| `loader` | Component | Ripple loader | A loader component as a fallback until image loads from url | -| `header` | Component | Default header as in demo | A header component which sits at the top of each story. It receives the `header` object from the `story` object. Data for header to be sent with each story object. | -| `storyContainerStyles` | Object | `{}` | Styles object for the outer container | -| `width` | Number/String | 360 | Width of the component, e.g. 600 or '100vw' or 'inherit' | -| `height` | Number/String | 640 | Height of the component, e.g. 1000 or '100%' or 'inherit' | -| `storyStyles` | Object | none | Override the default story styles mentioned below. | -| `progressContainerStyles` | Object | `{}` | Styles object for the container wrapping the progress bars | -| `progressWrapperStyles` | Object | `{}` | Styles object for the container wrapping each progress bar bars | -| `progressStyles` | Object | `{}` | Styles object for the progress bars | -| `loop` | Boolean | false | The last story loop to the first one and restart the stories. | -| **New props** | ⭐️ | ⭐️ | ⭐️ | -| `isPaused` | Boolean | false | Toggle story playing state | -| `currentIndex` | Number | undefined | Set the current story index | -| `onStoryStart` | Function | - | Callback when a story starts | -| `onStoryEnd` | Function | - | Callback when a story ends | -| `onAllStoriesEnd` | Function | - | Callback when all stories in the array have ended | -| `keyboardNavigation` | Boolean | false | Attaches arrow key listeners to navigate between stories if true. Also adds up arrow key listener for opening See More and Escape/down arrow for closing it | -| `preventDefault` | Boolean | false | Disable the default behavior when user click the component | +| Property | Type | Default | Description | +|---------------------------|-----------------|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `stories` | [String/Object] | `required` | An array of image urls or array of story objects (options described below) | +| `renderers` ⚡️ | [Object] | `[]` | An array of renderer objects (options described below) | +| `defaultInterval` | Number | 1200 | Milliseconds duration for which a story persists | +| `loader` | Component | Ripple loader | A loader component as a fallback until image loads from url | +| `header` | Component | Default header as in demo | A header component which sits at the top of each story. It receives the `header` object from the `story` object. Data for header to be sent with each story object. | +| `storyContainerStyles` | Object | `{}` | Styles object for the outer container | +| `width` | Number/String | 360 | Width of the component, e.g. 600 or '100vw' or 'inherit' | +| `height` | Number/String | 640 | Height of the component, e.g. 1000 or '100%' or 'inherit' | +| `storyStyles` | Object | none | Override the default story styles mentioned below. | +| `progressContainerStyles` | Object | `{}` | Styles object for the container wrapping the progress bars | +| `progressWrapperStyles` | Object | `{}` | Styles object for the container wrapping each progress bar bars | +| `progressStyles` | Object | `{}` | Styles object for the progress bars | +| `loop` | Boolean | false | The last story loop to the first one and restart the stories. | +| **New props** | ⭐️ | ⭐️ | ⭐️ | +| `isPaused` | Boolean | false | Toggle story playing state | +| `currentIndex` | Number | undefined | Set the current story index | +| `onStoryStart` | Function | - | Callback when a story starts | +| `onStoryEnd` | Function | - | Callback when a story ends | +| `onAllStoriesEnd` | Function | - | Callback when all stories in the array have ended | +| `keyboardNavigation` | Boolean | false | Attaches arrow key listeners to navigate between stories if true. Also adds up arrow key listener for opening See More and Escape/down arrow for closing it | +| `preventDefault` | Boolean | false | Disable the default behavior when user click the component | +| `preloadCount` | number | 1 | Determines how many stories should be preloaded ahead of the current story index. | ### Story object diff --git a/src/util/usePreLoader.ts b/src/util/usePreLoader.ts index 03bad8b4..6a7b391f 100644 --- a/src/util/usePreLoader.ts +++ b/src/util/usePreLoader.ts @@ -38,7 +38,6 @@ export const usePreLoader = (contents: Story[], cursor: number, preloadCount: nu const end = cursor + preloadCount + 1; const toPreload = contents.slice(start, end); - // todo remove then/catch after testing cacheContent(toPreload) }, [cursor, preloadCount, contents]) } From 4b0f423f0b1aef0da046b43dcdfdde04fe1827e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Thu, 6 Apr 2023 14:21:58 +0200 Subject: [PATCH 05/10] Keep track of loaded urls and make sure to not load them again --- .idea/workspace.xml | 3 +-- src/util/usePreLoader.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index ec9be04a..ecae7fd4 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -6,7 +6,6 @@ - diff --git a/src/util/usePreLoader.ts b/src/util/usePreLoader.ts index 6a7b391f..05f2b28f 100644 --- a/src/util/usePreLoader.ts +++ b/src/util/usePreLoader.ts @@ -33,10 +33,17 @@ const cacheContent = async (contents: Story[]) => { export const usePreLoader = (contents: Story[], cursor: number, preloadCount: number) => { const urlsLoaded = useRef([] as string[]).current; + // Pushes urls to urlsLoaded + const markUrlsLoaded = (contents: Story[]) => urlsLoaded.push(...contents.map((content) => content.url)) + useEffect(() => { const start = cursor + 1; const end = cursor + preloadCount + 1; - const toPreload = contents.slice(start, end); + + const toPreload = contents.slice(start, end) + .filter((content) => !urlsLoaded.includes(content.url)); // Only preload urls that haven't been loaded yet + + markUrlsLoaded(toPreload) cacheContent(toPreload) }, [cursor, preloadCount, contents]) From fa7ff400ae003a7a2012f255f0934c97607b21a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Thu, 6 Apr 2023 14:43:34 +0200 Subject: [PATCH 06/10] Remove .idea and add to .gitignore --- .gitignore | 3 +- .idea/vcs.xml | 6 ---- .idea/workspace.xml | 68 --------------------------------------------- 3 files changed, 2 insertions(+), 75 deletions(-) delete mode 100644 .idea/vcs.xml delete mode 100644 .idea/workspace.xml diff --git a/.gitignore b/.gitignore index 08feb25a..8b8ef335 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules dist stats.json -report.html \ No newline at end of file +report.html +.idea diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddf..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index ecae7fd4..00000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - { - "keyToString": { - "RunOnceActivity.OpenProjectViewOnStart": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "WebServerToolWindowFactoryState": "false", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "settings.editor.selected.configurable": "com.github.copilot.settings.ApplicationConfigurable", - "ts.external.directory.path": "/Users/macbook/open-source/react-insta-stories/node_modules/typescript/lib", - "vue.rearranger.settings.migration": "true" - } -} - - - - - 1680702538376 - - - - - - - - - - file://$PROJECT_DIR$/src/components/Container.tsx - - - - - \ No newline at end of file From 9b333fbe8ddd77b3e8a1e62fb7d49f9d4c8f222d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Mon, 17 Apr 2023 15:31:28 +0200 Subject: [PATCH 07/10] Add global set to replace ref hook --- src/interfaces.tsx | 4 +++- src/util/usePreLoader.ts | 15 +++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/interfaces.tsx b/src/interfaces.tsx index e352def1..f200b930 100644 --- a/src/interfaces.tsx +++ b/src/interfaces.tsx @@ -113,7 +113,9 @@ export interface Story { duration?: number; styles?: object; content?: Renderer; - originalContent?: Renderer; + originalContent?: Renderer + // Whether to preload the resource or not, defaults to true for images and false for videos + preloadResource?: boolean; } export interface Header { diff --git a/src/util/usePreLoader.ts b/src/util/usePreLoader.ts index 05f2b28f..23706db1 100644 --- a/src/util/usePreLoader.ts +++ b/src/util/usePreLoader.ts @@ -20,28 +20,35 @@ const cacheContent = async (contents: Story[]) => { img.src = content.url; img.onload = resolve; img.onerror = reject; + }) }) await Promise.all(promises); } +// Keeps track of urls that have been loaded +const urlsLoaded = new Set(); + +// Pushes urls to urlsLoaded +const markUrlsLoaded = (contents: Story[]) => { + contents.forEach((content) => { + urlsLoaded.add(content.url) + }) +} // Preloads images and videos from given Story[] using a cursor and preloadCount // Preload count is the number of images/videos to preload after the cursor // Cursor is the current index to start preloading from export const usePreLoader = (contents: Story[], cursor: number, preloadCount: number) => { - const urlsLoaded = useRef([] as string[]).current; - // Pushes urls to urlsLoaded - const markUrlsLoaded = (contents: Story[]) => urlsLoaded.push(...contents.map((content) => content.url)) useEffect(() => { const start = cursor + 1; const end = cursor + preloadCount + 1; const toPreload = contents.slice(start, end) - .filter((content) => !urlsLoaded.includes(content.url)); // Only preload urls that haven't been loaded yet + .filter((content) => !urlsLoaded.has(content.url)); // Only preload urls that haven't been loaded yet markUrlsLoaded(toPreload) From e51c11c96fb525d90b313a2416fd29b47fd92fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Mon, 17 Apr 2023 15:46:11 +0200 Subject: [PATCH 08/10] Add controlled preloading pr. story item, using preloadResource bool on type Story - Video defaults to false, images to true. --- example/src/App.js | 13 +++---------- src/util/usePreLoader.ts | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/example/src/App.js b/example/src/App.js index 7d479294..63bf3733 100644 --- a/example/src/App.js +++ b/example/src/App.js @@ -318,16 +318,6 @@ const stories2 = [ "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4", type: "video", }, - { - url: - "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4", - type: "video", - }, - { - url: - "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4", - type: "video", - }, { content: Story2, }, @@ -342,12 +332,15 @@ const stories2 = [ }, { url: "https://images.unsplash.com/photo-1676316698468-a907099ad5bc?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=928&q=80", + preloadResource: false, }, { url: "https://images.unsplash.com/photo-1676310483825-daa08914445e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2920&q=80", + preloadResource: false, }, { url: "https://images.unsplash.com/photo-1676321685222-0b527e61d5c7?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80", + preloadResource: false, }, ]; diff --git a/src/util/usePreLoader.ts b/src/util/usePreLoader.ts index 23706db1..4ae59686 100644 --- a/src/util/usePreLoader.ts +++ b/src/util/usePreLoader.ts @@ -37,6 +37,17 @@ const markUrlsLoaded = (contents: Story[]) => { }) } + +// Returns true if given Story should be preloaded +const shouldPreload = (content: Story) => { + if (!content.url) return false + if (urlsLoaded.has(content.url)) return false + if (content.preloadResource !== undefined) return content.preloadResource + if (content.type === 'video') return false + + return true +} + // Preloads images and videos from given Story[] using a cursor and preloadCount // Preload count is the number of images/videos to preload after the cursor // Cursor is the current index to start preloading from @@ -47,8 +58,7 @@ export const usePreLoader = (contents: Story[], cursor: number, preloadCount: nu const start = cursor + 1; const end = cursor + preloadCount + 1; - const toPreload = contents.slice(start, end) - .filter((content) => !urlsLoaded.has(content.url)); // Only preload urls that haven't been loaded yet + const toPreload = contents.slice(start, end).filter(shouldPreload); // Only preload urls that haven't been loaded yet markUrlsLoaded(toPreload) From f6bc59f3263c71266069006525de4e59a5838bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Mon, 17 Apr 2023 15:52:10 +0200 Subject: [PATCH 09/10] Add preloadResource readme --- README.md | 3 ++- src/interfaces.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c39c504c..fd9ef1f8 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Here `stories` is an array of story objects, which can be of various types as de Instead of simple string url, a comprehensive 'story object' can also be passed in the `stories` array. | Property | Description | -| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | +|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| | `url` | The url of the resource, be it image or video. | | `type` | Optional. Type of the story. `type: 'video' | 'image'`. Type `video` is necessary for a video story. | | `duration` | Optional. Duration for which a story should persist. | @@ -83,6 +83,7 @@ Instead of simple string url, a comprehensive 'story object' can also be passed | `seeMore` | Optional. Adds a see more icon at the bottom of the story. On clicking, opens up this component. (v2: updated to Function instead of element) | | `seeMoreCollapsed` | Optional. Send custom component to be rendered instead of the default 'See More' text. | | | `styles` | Optional. Override the default story styles mentioned below. | +| `preloadResource` | Optional. Whether to preload the resource or not, defaults to `true` for images and `false` for videos (video preloading is experimental) | ### Default story styles diff --git a/src/interfaces.tsx b/src/interfaces.tsx index f200b930..34082994 100644 --- a/src/interfaces.tsx +++ b/src/interfaces.tsx @@ -114,7 +114,7 @@ export interface Story { styles?: object; content?: Renderer; originalContent?: Renderer - // Whether to preload the resource or not, defaults to true for images and false for videos + // Whether to preload the resource or not, defaults to `true` for images and `false` for videos (video preloading is experimental) preloadResource?: boolean; } From 0149d1993f6d1c04ba8b8d5914d985e3574ab518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schl=C3=BCter?= Date: Mon, 17 Apr 2023 16:39:13 +0200 Subject: [PATCH 10/10] Clean up --- src/util/usePreLoader.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/util/usePreLoader.ts b/src/util/usePreLoader.ts index 4ae59686..c75abd48 100644 --- a/src/util/usePreLoader.ts +++ b/src/util/usePreLoader.ts @@ -1,5 +1,5 @@ import {Story} from "../interfaces"; -import {useEffect, useRef, useState} from "react"; +import {useEffect} from "react"; // Caches given Story[] using HTMLImageElement and HTMLVideoElement @@ -52,16 +52,15 @@ const shouldPreload = (content: Story) => { // Preload count is the number of images/videos to preload after the cursor // Cursor is the current index to start preloading from export const usePreLoader = (contents: Story[], cursor: number, preloadCount: number) => { - - useEffect(() => { const start = cursor + 1; const end = cursor + preloadCount + 1; - const toPreload = contents.slice(start, end).filter(shouldPreload); // Only preload urls that haven't been loaded yet + const toPreload = contents + .slice(start, end) + .filter(shouldPreload); markUrlsLoaded(toPreload) - cacheContent(toPreload) }, [cursor, preloadCount, contents]) }