Skip to content

Commit

Permalink
feat: ability to prefetch story images
Browse files Browse the repository at this point in the history
  • Loading branch information
ridvanaltun committed Aug 9, 2023
1 parent 6145a50 commit 2412027
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 23 deletions.
41 changes: 21 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,27 @@ npm i react-native-story-component

## Props

| Name | Description | Type | Default Value |
| :------------------- | :--------------------------------------- | :---------------------------------------------- | :-----------: |
| data | Array of stories. | UserStory[] | |
| unPressedBorderColor | Unpressed border color of profile circle | color | red |
| pressedBorderColor | Pressed border color of profile circle | color | grey |
| onClose | Todo when close | (item: UserStory) => void | null |
| onStart | Todo when start | (item: UserStory) => void | null |
| duration | Per story duration in seconds | number | 10 |
| swipeText | Text of swipe component | string | Swipe Up |
| customSwipeUpButton | Custom component for swipe area | () => ReactNode | |
| customCloseButton | Custom component for close button | () => ReactNode | |
| customStoryList | Custom component for story list | (props: CustomStoryList) => React.ReactNode | |
| customStoryView | Custom component for story view | (props: CustomStoryView) => React.ReactNode | |
| customProfileBanner | Custom component for profile banner | (props: CustomProfileBanner) => React.ReactNode | |
| customStoryImage | Custom component for story image | (props: CustomStoryImage) => React.ReactNode | |
| avatarSize | Size of avatar circle | number | 60 |
| showAvatarText | Show or hide avatar text | bool | true |
| showProfileBanner | Show or hide profile banner | bool | true |
| textStyle | Avatar text style | TextStyle | |
| storyListStyle | Story list view style | ViewStyle | |
| Name | Description | Type | Default Value |
| :------------------- | :---------------------------------------- | :---------------------------------------------- | :-----------: |
| data | Array of stories. | UserStory[] | |
| unPressedBorderColor | Unpressed border color of profile circle | color | red |
| pressedBorderColor | Pressed border color of profile circle | color | grey |
| onClose | Todo when close | (item: UserStory) => void | null |
| onStart | Todo when start | (item: UserStory) => void | null |
| duration | Per story duration in seconds | number | 10 |
| swipeText | Text of swipe component | string | Swipe Up |
| customSwipeUpButton | Custom component for swipe area | () => ReactNode | |
| customCloseButton | Custom component for close button | () => ReactNode | |
| customStoryList | Custom component for story list | (props: CustomStoryList) => React.ReactNode | |
| customStoryView | Custom component for story view | (props: CustomStoryView) => React.ReactNode | |
| customProfileBanner | Custom component for profile banner | (props: CustomProfileBanner) => React.ReactNode | |
| customStoryImage | Custom component for story image | (props: CustomStoryImage) => React.ReactNode | |
| avatarSize | Size of avatar circle | number | 60 |
| showAvatarText | Show or hide avatar text | bool | true |
| showProfileBanner | Show or hide profile banner | bool | true |
| textStyle | Avatar text style | TextStyle | |
| prefetchImages | Prefetch story images | bool | true |
| onImagesPrefetched | Callback function for prefetching process | (allImagesPrefetched: bool) => void | |

## Usage

Expand Down
3 changes: 3 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ const App = () => {
// </View>
// );
// }}
onImagesPrefetched={(status) => {
console.log('is all images prefetched ->', status);
}}
customSwipeUpButton={CustomSwipeButton}
/>
</SafeAreaView>
Expand Down
39 changes: 39 additions & 0 deletions src/components/Story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Platform,
StatusBar,
StyleSheet,
Image,
} from 'react-native';
import Modal from 'react-native-modalbox';

Expand All @@ -15,6 +16,7 @@ import AndroidCubeEffect from '../animations/AndroidCubeEffect';
import CubeNavigationHorizontal from '../animations/CubeNavigationHorizontal';

import { isNullOrWhitespace, isUrl } from '../helpers/ValidationHelpers';
import useMountEffect from '../helpers/useMountEffect';

import { ActionStates } from '../index';
import type {
Expand Down Expand Up @@ -46,6 +48,8 @@ interface StoryProps {
showAvatarText?: boolean;
showProfileBanner?: boolean;
avatarTextStyle?: TextStyle;
prefetchImages?: boolean;
onImagesPrefetched?: (allImagesPrefethed: boolean) => void;
}

const Story = (props: StoryProps) => {
Expand All @@ -67,6 +71,8 @@ const Story = (props: StoryProps) => {
showAvatarText,
showProfileBanner,
avatarTextStyle,
prefetchImages,
onImagesPrefetched,
} = props;

const cubeRef = useRef<CubeAnimationHandle>(null);
Expand Down Expand Up @@ -101,6 +107,38 @@ const Story = (props: StoryProps) => {
}
}, [currentPage, dataState, selectedData]);

useMountEffect(() => {
if (prefetchImages) {
let preFetchTasks: Promise<boolean>[] = [];
const images = data.flatMap((story) => {
const storyImages = story.stories.map((storyItem) => {
return storyItem.image;
});

return storyImages;
});

images.forEach((image) => {
preFetchTasks.push(Image.prefetch(image));
});

Promise.all(preFetchTasks).then((results) => {
let downloadedAll = true;

results.forEach((result) => {
if (!result) {
//error occurred downloading a pic
downloadedAll = false;
}
});

if (onImagesPrefetched) {
onImagesPrefetched(downloadedAll);
}
});
}
});

useEffect(() => {
handleSeen();
}, [currentPage, handleSeen]);
Expand Down Expand Up @@ -249,6 +287,7 @@ const Story = (props: StoryProps) => {
Story.defaultProps = {
showAvatarText: true,
showProfileBanner: true,
prefetchImages: true,
};

const styles = StyleSheet.create({
Expand Down
2 changes: 1 addition & 1 deletion src/components/StoryCircleListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from 'react-native';

import { isUrl } from '../helpers/ValidationHelpers';
import { usePrevious } from '../helpers/StateHelpers';
import usePrevious from '../helpers/usePrevious';

import type { TextStyle } from 'react-native';
import type { UserStory } from '../index';
Expand Down
2 changes: 1 addition & 1 deletion src/components/StoryListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from 'react-native';
import GestureRecognizer from 'react-native-swipe-gestures';

import { usePrevious } from '../helpers/StateHelpers';
import usePrevious from '../helpers/usePrevious';
import { isNullOrWhitespace } from '../helpers/ValidationHelpers';

import { ActionStates } from '../index';
Expand Down
15 changes: 15 additions & 0 deletions src/helpers/useMountEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useEffect } from 'react';

const useMountEffect = (effect: () => void) => {
if (typeof effect !== 'function') {
console.error('Effect must be a function');
}

useEffect(() => {
effect?.();

// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};

export default useMountEffect;
4 changes: 3 additions & 1 deletion src/helpers/StateHelpers.ts → src/helpers/usePrevious.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { useEffect, useRef } from 'react';

// @see: https://usehooks.com/usePrevious/

export const usePrevious = (value: any) => {
const usePrevious = (value: any) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
};

export default usePrevious;

0 comments on commit 2412027

Please sign in to comment.