Skip to content

Commit d6b9f3b

Browse files
author
Sebastián Rojas Ricaurte
committed
stories preload
1 parent 293cbfc commit d6b9f3b

File tree

12 files changed

+336
-217
lines changed

12 files changed

+336
-217
lines changed

CONTRIBUTING.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
1-
# add contributing guidelines
1+
# Configuring your environment
2+
3+
## Install
4+
1. Run `npm run install`
5+
2. Run `npm run build:stories`
6+
3. In another terminal run `npm run start:docs` (`http://localhost:3000/stories-react` should be opened)
7+
8+
You can start making changes and see it in action in the documentation site.
9+
10+
Note: if you change the `types.d.ts` file in the `stories` package, you need to re run `npm run build:stories`.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"workspaces": [
44
"packages/*"
55
],
6-
"version": "1.1.2",
6+
"version": "1.2.0",
77
"description": "instagram style stories in react js",
88
"scripts": {
99
"start:docs": "cd ./packages/documentation/ && yarn start",

packages/documentation/docs/api.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ sidebar_position: 3
1616
| `onStoriesStart` | `function` | `-` | Callback called when first story is rendered. it get called only once, |
1717
| `onAllStoriesEnd` | `function` | `-` | Callback called when last story gets completed. it will get called only once |
1818
| `currentIndex` | `number` | `-` | Current index of the story which should be selected first |
19-
| `defaultDuration` | `number` | `10000` | default duration in ms of stories if duration is not provided in the `IStoryObject` |
20-
| `classNames` | `IStoryClassNames` | `{}` | classnames to overide different sections of a story renderer |
21-
| `pauseStoryWhenInActiveWindow`| `boolean` | `true` | pauses story when window goes out of focus (user changes tab/minimizes browser etc |
19+
| `defaultDuration` | `number` | `10000` | Default duration in ms of stories if duration is not provided in the `IStoryObject` |
20+
| `classNames` | `IStoryClassNames` | `{}` | Class names to overide different sections of a story renderer |
21+
| `pauseStoryWhenInActiveWindow`| `boolean` | `true` | Pauses story when window goes out of focus (user changes tab/minimizes browser etc |
22+
| `preloadNextStory` | `boolean` | `false` | Preloads next story using `@remotion/preload` **(Applies only for image and video stories)** |
2223

2324
## IStoryObject
2425

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
sidebar_position: 3
3+
---
4+
5+
# Stories Preload
6+
7+
import StoriesPreloadStories from '../../src/components/StoriesPreloadStories';
8+
9+
<StoriesPreloadStories />
10+
11+
12+
```jsx
13+
14+
import React from 'react';
15+
import Stories from 'stories-react';
16+
import 'stories-react/dist/index.css';
17+
18+
export default function StoriesPreloadStories() {
19+
const stories = [
20+
{
21+
type: 'image',
22+
duration: 6000,
23+
url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300',
24+
},
25+
{
26+
type: 'video',
27+
url: 'https://assets.mixkit.co/videos/preview/mixkit-tree-with-yellow-flowers-1173-large.mp4',
28+
duration: 27000,
29+
},
30+
{
31+
type: 'image',
32+
duration: 6000,
33+
url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300',
34+
},
35+
{
36+
type: 'image',
37+
duration: 6000,
38+
url: 'https://images.pexels.com/photos/14664626/pexels-photo-14664626.jpeg',
39+
},
40+
{
41+
type: 'video',
42+
url: 'https://assets.mixkit.co/videos/preview/mixkit-mother-with-her-little-daughter-eating-a-marshmallow-in-nature-39764-large.mp4',
43+
duration: 10000,
44+
},
45+
{
46+
type: 'video',
47+
url: 'https://assets.mixkit.co/videos/preview/mixkit-family-walking-together-in-nature-39767-large.mp4',
48+
duration: 10000,
49+
},
50+
51+
{
52+
type: 'video',
53+
duration: 6000,
54+
url: 'https://assets.mixkit.co/videos/preview/mixkit-girl-in-neon-sign-1232-large.mp4',
55+
},
56+
{
57+
duration: 7000,
58+
type: 'image',
59+
url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300',
60+
},
61+
];
62+
return (
63+
<div
64+
style={{
65+
display: 'flex',
66+
justifyContent: 'center',
67+
width: '100%',
68+
marginBottom: '16px',
69+
}}
70+
>
71+
<Stories width="400px" height="600px" stories={stories} preloadNextStory={true} />
72+
</div>
73+
);
74+
}
75+
76+
77+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React from 'react';
2+
import Stories from 'stories-react';
3+
import 'stories-react/dist/index.css';
4+
5+
export default function ImageVideoStories() {
6+
const stories = [
7+
{
8+
type: 'image',
9+
duration: 6000,
10+
url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300',
11+
},
12+
{
13+
type: 'video',
14+
url: 'https://assets.mixkit.co/videos/preview/mixkit-tree-with-yellow-flowers-1173-large.mp4',
15+
duration: 27000,
16+
},
17+
{
18+
type: 'image',
19+
duration: 6000,
20+
url: 'https://images.pexels.com/photos/5015227/pexels-photo-5015227.jpeg',
21+
},
22+
{
23+
type: 'image',
24+
duration: 6000,
25+
url: 'https://images.pexels.com/photos/14664626/pexels-photo-14664626.jpeg',
26+
},
27+
{
28+
type: 'video',
29+
url: 'https://assets.mixkit.co/videos/preview/mixkit-mother-with-her-little-daughter-eating-a-marshmallow-in-nature-39764-large.mp4',
30+
duration: 10000,
31+
},
32+
{
33+
type: 'video',
34+
url: 'https://assets.mixkit.co/videos/preview/mixkit-family-walking-together-in-nature-39767-large.mp4',
35+
duration: 10000,
36+
},
37+
38+
{
39+
type: 'video',
40+
duration: 6000,
41+
url: 'https://assets.mixkit.co/videos/preview/mixkit-girl-in-neon-sign-1232-large.mp4',
42+
},
43+
{
44+
duration: 7000,
45+
type: 'image',
46+
url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300',
47+
},
48+
];
49+
return (
50+
<div
51+
style={{
52+
display: 'flex',
53+
justifyContent: 'center',
54+
width: '100%',
55+
marginBottom: '16px',
56+
}}
57+
>
58+
<Stories
59+
width="400px"
60+
height="600px"
61+
stories={stories}
62+
preloadNextStory={true}
63+
/>
64+
</div>
65+
);
66+
}

packages/stories/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export default function ImagesStories() {
9494
| `defaultDuration` | `number` | `10000` | default duration in ms of stories if duration is not provided in the `IStoryObject` |
9595
| `classNames` | `IStoryClassNames` | `{}` | classnames to overide different sections of a story renderer |
9696
| `pauseStoryWhenInActiveWindow` | `boolean` | `true` | pauses story when window goes out of focus (user changes tab/minimizes browser etc |
97+
| `preloadNextStory` | `boolean` | `false` | Preloads next story using `@remotion/preload` **(Applies only for image and video stories)** |
9798

9899
## IStoryObject
99100

packages/stories/package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "stories-react",
3-
"version": "1.1.2",
3+
"version": "1.2.0",
44
"description": "instagram style stories in react js",
55
"main": "dist/index.js",
66
"module": "dist/index.esm.js",
@@ -47,5 +47,7 @@
4747
"rollup-plugin-typescript2": "^0.34.1",
4848
"typescript": "^4.9.5"
4949
},
50-
"dependencies": {}
50+
"dependencies": {
51+
"@remotion/preload": "^3.3.63"
52+
}
5153
}

packages/stories/src/index.tsx

+35-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default function Stories({
1818
onStoriesStart = () => {},
1919
classNames = {},
2020
pauseStoryWhenInActiveWindow = true,
21+
preloadNextStory = false,
2122
}: IStoryProps): JSX.Element | null {
2223
const storiesWithIndex: IStoryIndexedObject[] = useMemo(() => {
2324
return utilities.transformStories(stories, defaultDuration);
@@ -33,6 +34,25 @@ export default function Stories({
3334
const hasCalledEndedCb = useRef<any>(false);
3435
const hasCalledStartedCb = useRef<any>(false);
3536

37+
// Preload stories
38+
const preloadedStoriesUrlsRef = useRef<string[]>([]);
39+
const preloadStory = (index: number) => {
40+
if (!preloadNextStory) return;
41+
42+
if (
43+
index >= storiesWithIndex.length ||
44+
preloadedStoriesUrlsRef.current.indexOf(storiesWithIndex[index].url) >= 0
45+
)
46+
return;
47+
48+
if (utilities.preloadStory(
49+
storiesWithIndex[index].type,
50+
storiesWithIndex[index].url,
51+
)) {
52+
preloadedStoriesUrlsRef.current.push(storiesWithIndex[index].url);
53+
}
54+
};
55+
3656
useEffect(() => {
3757
if (!hasCalledStartedCb.current) {
3858
hasCalledStartedCb.current = true;
@@ -41,10 +61,14 @@ export default function Stories({
4161
}, [onStoriesStart]);
4262

4363
useEffect(() => {
64+
console.log('currentIndex change', currentIndex);
4465
const story = storiesWithIndex[currentIndex];
45-
if (story) {
46-
setSelectedStory(story);
47-
}
66+
67+
if (!story) return;
68+
69+
setSelectedStory(story);
70+
71+
preloadStory(currentIndex + 1);
4872
}, [currentIndex, stories]);
4973

5074
function handleNextClick() {
@@ -68,6 +92,7 @@ export default function Stories({
6892
return;
6993
}
7094
setSelectedStory((prev) => {
95+
7196
if (!prev) {
7297
return storiesWithIndex[0];
7398
}
@@ -84,9 +109,12 @@ export default function Stories({
84109
}
85110

86111
useEffect(() => {
87-
if (selectedStory) {
88-
onStoryChange(selectedStory.index);
89-
}
112+
console.log('selectedStory change', { sel: selectedStory ?? "", index: currentIndex });
113+
if (!selectedStory) return;
114+
115+
onStoryChange(selectedStory.index);
116+
117+
preloadStory(selectedStory.index + 1);
90118
}, [selectedStory]);
91119

92120
hooks.usePausableTimeout(
@@ -110,6 +138,7 @@ export default function Stories({
110138
defaultDuration,
111139
isPaused,
112140
classNames,
141+
preloadNextStory,
113142
};
114143

115144
if (!selectedStory) {

packages/stories/src/types.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface IStoryProps {
3333
onAllStoriesEnd?: () => void;
3434
classNames?: IStoryClassNames;
3535
pauseStoryWhenInActiveWindow?: boolean;
36+
preloadNextStory?:boolean;
3637
}
3738

3839
export interface IStoryContext {
@@ -42,6 +43,7 @@ export interface IStoryContext {
4243
defaultDuration: number;
4344
isPaused: boolean;
4445
classNames?: IStoryClassNames;
46+
preloadNextStory?: boolean;
4547
}
4648

4749
export interface IStoryComponentProps {

packages/stories/src/utilities.ts

+19
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { IStoryObject, IStoryIndexedObject } from './types';
2+
import { preloadImage, preloadVideo } from '@remotion/preload';
23

34
function getTimeDelta(precesion = 4): number {
45
return Number(Math.random().toFixed(precesion));
@@ -38,3 +39,21 @@ export function transformStories(
3839
};
3940
});
4041
}
42+
43+
/**
44+
* Preloads stories of type "image" and "video"
45+
* @param type
46+
* @param url
47+
*/
48+
export function preloadStory(type: string, url:string): boolean {
49+
if (type === 'component') {
50+
return false;
51+
}
52+
if (type === 'image') {
53+
preloadImage(url);
54+
}
55+
else {
56+
preloadVideo(url);
57+
}
58+
return true;
59+
}

readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export default function ImagesStories() {
9494
| `defaultDuration` | `number` | `10000` | default duration in ms of stories if duration is not provided in the `IStoryObject` |
9595
| `classNames` | `IStoryClassNames` | `{}` | classnames to overide different sections of a story renderer |
9696
| `pauseStoryWhenInActiveWindow` | `boolean` | `true` | pauses story when window goes out of focus (user changes tab/minimizes browser etc |
97+
| `preloadNextStory` | `boolean` | `false` | Preloads next story using `@remotion/preload` **(Applies only for image and video stories)** |
9798

9899
## IStoryObject
99100

0 commit comments

Comments
 (0)