diff --git a/README.md b/README.md
index 122df66..0769243 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,10 @@
> A decent story component for React-Native
+| Preview |
+| ----------------------------------------------------------------- |
+|
|
+
# Table of Contents
- [Getting started](#getting-started)
@@ -25,21 +29,25 @@ npm i react-native-story-component
## Props
-| Name | Description | Type | Default Value |
-| :--------------------- | :-------------------------------------------------- | :------------------------------------------------------------------------------------------- | :-----------: |
-| data | Array of IUserStory. You can check from interfaces. | object | |
-| unPressedBorderColor | Unpressed border color of profile circle | color | red |
-| pressedBorderColor | Pressed border color of profile circle | color | grey |
-| onClose | Todo when close | function | null |
-| onStart | Todo when start | function | null |
-| duration | Per story duration seconds | number | 10 |
-| swipeText | Text of swipe component | string | Swipe Up |
-| customSwipeUpComponent | For use custom component for swipe area | () => component | |
-| customCloseComponent | For use custom component for close button | () => component | |
-| customStoryList | For use custom component for story list | ({data: IUserStory[], onStoryPress: (item: IUserStory, index: number) => void}) => component | |
-| avatarSize | Size of avatar circle | number | 60 |
-| showAvatarText | For show or hide avatar text. | bool | true |
-| textStyle | For avatar text style | TextStyle | |
+| Name | Description | Type | Default Value |
+| :------------------- | :--------------------------------------- | :----------------------------------------------- | :-----------: |
+| data | Array of stories. | IUserStory[] | |
+| unPressedBorderColor | Unpressed border color of profile circle | color | red |
+| pressedBorderColor | Pressed border color of profile circle | color | grey |
+| onClose | Todo when close | (item: IUserStory) => void | null |
+| onStart | Todo when start | (item: IUserStory) => 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: ICustomStoryList) => React.ReactNode | |
+| customStoryView | Custom component for story view | (props: ICustomStoryView) => React.ReactNode | |
+| customProfileBanner | Custom component for profile banner | (props: ICustomProfileBanner) => 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 | |
## Usage
@@ -88,13 +96,13 @@ const App = () => {
},
]}
duration={10}
- onStart={(item) => {
- console.log(item);
+ onStart={(openedStory) => {
+ console.log(openedStory);
}}
- onClose={(item) => {
- console.log('close: ', item);
+ onClose={(closedStory) => {
+ console.log(closedStory);
}}
- customSwipeUpComponent={() => (
+ customSwipeUpButton={() => (
Swipe
diff --git a/docs/preview.gif b/docs/preview.gif
new file mode 100644
index 0000000..33b2951
Binary files /dev/null and b/docs/preview.gif differ
diff --git a/example/App.tsx b/example/App.tsx
new file mode 100644
index 0000000..1dd0f29
--- /dev/null
+++ b/example/App.tsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+import { StatusBar } from 'expo-status-bar';
+import { faker } from '@faker-js/faker';
+
+import Story from 'react-native-story-component';
+
+const createStories = () => {
+ const USER_COUNT = 5;
+ const USER_STORY_COUNT = 3;
+
+ return [...Array(USER_COUNT).keys()].map((i) => ({
+ id: `user-${i}`,
+ avatar: faker.image.avatar(),
+ name: faker.name.findName(),
+ // seen: Math.random() < 0.5,
+ stories: [...Array(USER_STORY_COUNT).keys()].map((y) => ({
+ id: `story-${i}-${y}`,
+ image: faker.image.imageUrl(1080, 1920, undefined, true),
+ swipeText: faker.lorem.text(),
+ onPress: () => console.log(`Story ${i}-${y} swiped!`),
+ })),
+ }));
+};
+
+const App = () => {
+ return (
+ <>
+
+
+ {
+ // return (
+ //
+ // Custom View
+ //
+ // );
+ // }}
+ customSwipeUpButton={() => (
+
+ Swipe
+
+ )}
+ storyListStyle={styles.story}
+ />
+
+ >
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#fff',
+ },
+ story: {
+ marginTop: 70,
+ },
+});
+
+export default App;
diff --git a/example/App.js b/example/index.js
similarity index 90%
rename from example/App.js
rename to example/index.js
index be6bdb8..9d5d25a 100644
--- a/example/App.js
+++ b/example/index.js
@@ -1,6 +1,6 @@
import { registerRootComponent } from 'expo';
-import App from './src/App';
+import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in the Expo client or in a native build,
diff --git a/example/package.json b/example/package.json
index e15c58e..6b52aa1 100644
--- a/example/package.json
+++ b/example/package.json
@@ -3,7 +3,7 @@
"description": "Example app for react-native-story-component",
"version": "0.0.1",
"private": true,
- "main": "App",
+ "main": "index",
"scripts": {
"android": "expo start --android",
"ios": "expo start --ios",
diff --git a/example/src/App.tsx b/example/src/App.tsx
deleted file mode 100644
index 61d757a..0000000
--- a/example/src/App.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import React from 'react';
-import { StyleSheet, Text, View } from 'react-native';
-import { StatusBar } from 'expo-status-bar';
-import { faker } from '@faker-js/faker';
-
-import Story from 'react-native-story-component';
-
-const createData = () => {
- const array = [];
-
- const USER_COUNT = 10;
- const USER_STORY_COUNT = 15;
-
- for (let i = 1; i <= USER_COUNT; i++) {
- const storyArray = [];
- for (let k = 1; k <= USER_STORY_COUNT; k++) {
- storyArray.push({
- id: i,
- image: faker.image.imageUrl(1080, 1920, undefined, true),
- swipeText: faker.lorem.text(),
- onPress: () => console.log(`Story ${i} swiped!`),
- });
- }
-
- array.push({
- // seen: Math.random() < 0.5,
- id: i,
- avatar: faker.image.avatar(),
- name: faker.name.findName(),
- stories: storyArray,
- });
- }
-
- return array;
-};
-
-const App = () => {
- return (
- <>
-
-
- (
-
- Swipe
-
- )}
- style={styles.story}
- />
-
- >
- );
-};
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#fff',
- },
- story: {
- marginTop: 70,
- },
-});
-
-export default App;
diff --git a/src/components/AndroidCubeEffect.js b/src/animations/AndroidCubeEffect.js
similarity index 100%
rename from src/components/AndroidCubeEffect.js
rename to src/animations/AndroidCubeEffect.js
diff --git a/src/components/CubeNavigationHorizontal.js b/src/animations/CubeNavigationHorizontal.js
similarity index 100%
rename from src/components/CubeNavigationHorizontal.js
rename to src/animations/CubeNavigationHorizontal.js
diff --git a/src/Story.tsx b/src/components/Story.tsx
similarity index 63%
rename from src/Story.tsx
rename to src/components/Story.tsx
index e276f3e..9ee579b 100644
--- a/src/Story.tsx
+++ b/src/components/Story.tsx
@@ -4,36 +4,42 @@ import Modal from 'react-native-modalbox';
import StoryListItem from './StoryListItem';
import StoryCircleListView from './StoryCircleListView';
-import AndroidCubeEffect from './components/AndroidCubeEffect';
-import CubeNavigationHorizontal from './components/CubeNavigationHorizontal';
-import { isNullOrWhitespace } from './helpers/ValidationHelpers';
+import AndroidCubeEffect from '../animations/AndroidCubeEffect';
+import CubeNavigationHorizontal from '../animations/CubeNavigationHorizontal';
-import { CloseStates } from './StoryListItem';
+import { isNullOrWhitespace, isUrl } from '../helpers/ValidationHelpers';
+
+import { ActionStates } from '../index';
+import type {
+ IUserStory,
+ ICustomStoryView,
+ ICustomStoryList,
+ ICustomProfileBanner,
+} from '../index';
import type { TextStyle, ViewStyle } from 'react-native';
-import type { IUserStory } from './interfaces/IUserStory';
-declare module './components/CubeNavigationHorizontal' {}
+declare module '../animations/CubeNavigationHorizontal' {}
-declare module './components/AndroidCubeEffect' {}
+declare module '../animations/AndroidCubeEffect' {}
type Props = {
data: IUserStory[];
- style?: ViewStyle;
+ storyListStyle?: ViewStyle;
unPressedBorderColor?: string;
pressedBorderColor?: string;
onClose?: (item: IUserStory) => void;
onStart?: (item: IUserStory) => void;
duration?: number;
swipeText?: string;
- customSwipeUpComponent?: () => React.ReactNode;
- customCloseComponent?: () => React.ReactNode;
- customStoryList?: (props: {
- data: IUserStory[];
- onStoryPress: (item: IUserStory, index: number) => void;
- }) => React.ReactNode;
+ customSwipeUpButton?: () => React.ReactNode;
+ customCloseButton?: () => React.ReactNode;
+ customStoryList?: (props: ICustomStoryList) => React.ReactNode;
+ customStoryView?: (props: ICustomStoryView) => React.ReactNode;
+ customProfileBanner?: (props: ICustomProfileBanner) => React.ReactNode;
avatarSize?: number;
showAvatarText?: boolean;
+ showProfileBanner?: boolean;
avatarTextStyle?: TextStyle;
};
@@ -42,16 +48,18 @@ const Story = (props: Props) => {
data,
unPressedBorderColor,
pressedBorderColor,
- style,
+ storyListStyle,
onStart,
onClose,
duration,
swipeText,
- customSwipeUpComponent,
- customCloseComponent,
+ customSwipeUpButton,
+ customCloseButton,
customStoryList,
+ customProfileBanner,
avatarSize,
showAvatarText,
+ showProfileBanner,
avatarTextStyle,
} = props;
@@ -59,7 +67,7 @@ const Story = (props: Props) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [currentPage, setCurrentPage] = useState(0);
const [selectedData, setSelectedData] = useState([]);
- const cube = useRef();
+ const cubeRef = useRef();
const _handleStoryItemPress = (item: IUserStory, index: number) => {
const newData = dataState.slice(index);
@@ -90,14 +98,14 @@ const Story = (props: Props) => {
handleSeen();
}, [currentPage, handleSeen]);
- const onStoryFinish = (state: CloseStates) => {
+ const onStoryFinish = (state: ActionStates) => {
if (!isNullOrWhitespace(state)) {
- if (state === CloseStates.NEXT) {
+ if (state === ActionStates.NEXT) {
const newPage = currentPage + 1;
if (newPage < selectedData.length) {
setCurrentPage(newPage);
//@ts-ignore
- cube?.current?.scrollTo(newPage);
+ cubeRef?.current?.scrollTo(newPage);
} else {
setIsModalOpen(false);
setCurrentPage(0);
@@ -105,7 +113,7 @@ const Story = (props: Props) => {
onClose(selectedData[selectedData.length - 1]);
}
}
- } else if (state === CloseStates.PREVIOUS) {
+ } else if (state === ActionStates.PREVIOUS) {
const newPage = currentPage - 1;
if (newPage < 0) {
setIsModalOpen(false);
@@ -113,67 +121,49 @@ const Story = (props: Props) => {
} else {
setCurrentPage(newPage);
//@ts-ignore
- cube?.current?.scrollTo(newPage);
+ cubeRef?.current?.scrollTo(newPage);
}
}
}
};
- const renderStoryList = () =>
- selectedData.map((story, i) => {
+ const onClosePress = (story: IUserStory) => {
+ setIsModalOpen(false);
+ if (onClose) onClose(story);
+ };
+
+ const renderStoryList = () => {
+ return selectedData.map((story, i) => {
+ if (props.customStoryView)
+ return props.customStoryView({
+ index: i,
+ data: story,
+ currentPage,
+ changeStory: onStoryFinish,
+ close: () => onClosePress(story),
+ });
+
return (
{
- setIsModalOpen(false);
- if (onClose) {
- onClose(story);
- }
- }}
- index={i}
+ customSwipeUpButton={customSwipeUpButton}
+ customCloseButton={customCloseButton}
+ customProfileBanner={customProfileBanner}
+ showProfileBanner={showProfileBanner}
+ onClosePress={() => onClosePress(story)}
/>
);
});
-
- const renderCube = () => {
- if (Platform.OS === 'ios') {
- return (
- {
- if (parseInt(`${x}`, 10) !== currentPage) {
- setCurrentPage(parseInt(`${x}`, 10));
- }
- }}
- >
- {renderStoryList()}
-
- );
- } else {
- return (
- {
- if (parseInt(`${x}`, 10) !== currentPage) {
- setCurrentPage(parseInt(`${x}`, 10));
- }
- }}
- >
- {renderStoryList()}
-
- );
- }
};
const renderStoryCircleList = () => {
@@ -197,9 +187,41 @@ const Story = (props: Props) => {
);
};
+ const renderCube = () => {
+ if (Platform.OS === 'ios') {
+ return (
+ {
+ if (parseInt(`${x}`, 10) !== currentPage) {
+ setCurrentPage(parseInt(`${x}`, 10));
+ }
+ }}
+ >
+ {renderStoryList()}
+
+ );
+ }
+
+ return (
+ {
+ if (parseInt(`${x}`, 10) !== currentPage) {
+ setCurrentPage(parseInt(`${x}`, 10));
+ }
+ }}
+ >
+ {renderStoryList()}
+
+ );
+ };
+
return (
<>
- {renderStoryCircleList()}
+ {renderStoryCircleList()}
{
Story.defaultProps = {
showAvatarText: true,
+ showProfileBanner: true,
};
const styles = StyleSheet.create({
diff --git a/src/StoryCircleListItem.tsx b/src/components/StoryCircleListItem.tsx
similarity index 90%
rename from src/StoryCircleListItem.tsx
rename to src/components/StoryCircleListItem.tsx
index f6c431f..8b1114c 100644
--- a/src/StoryCircleListItem.tsx
+++ b/src/components/StoryCircleListItem.tsx
@@ -8,10 +8,12 @@ import {
StyleSheet,
Platform,
} from 'react-native';
-import { usePrevious } from './helpers/StateHelpers';
+
+import { isUrl } from '../helpers/ValidationHelpers';
+import { usePrevious } from '../helpers/StateHelpers';
import type { TextStyle } from 'react-native';
-import type { IUserStory } from './interfaces/IUserStory';
+import type { IUserStory } from '../index';
interface Props {
item: IUserStory;
@@ -81,10 +83,10 @@ const StoryCircleListItem = (props: Props) => {
width: size,
borderRadius: size / 2,
}}
- source={{ uri: item.avatar }}
+ source={isUrl(item.avatar) ? { uri: item.avatar } : item.avatar}
defaultSource={
Platform.OS === 'ios'
- ? require('./assets/images/no_avatar.png')
+ ? require('../assets/images/no_avatar.png')
: null
}
/>
diff --git a/src/StoryCircleListView.tsx b/src/components/StoryCircleListView.tsx
similarity index 92%
rename from src/StoryCircleListView.tsx
rename to src/components/StoryCircleListView.tsx
index 6e925fa..47aaca4 100644
--- a/src/StoryCircleListView.tsx
+++ b/src/components/StoryCircleListView.tsx
@@ -3,7 +3,7 @@ import { View, FlatList, StyleSheet } from 'react-native';
import StoryCircleListItem from './StoryCircleListItem';
import type { TextStyle } from 'react-native';
-import type { IUserStory } from './interfaces/IUserStory';
+import type { IUserStory } from '../index';
interface Props {
data: IUserStory[];
@@ -29,7 +29,7 @@ const StoryCircleListView = (props: Props) => {
return (
`story-item-${index}`}
+ keyExtractor={(item) => `story-item-${item.id}`}
data={data}
horizontal
style={styles.container}
diff --git a/src/StoryListItem.tsx b/src/components/StoryListItem.tsx
similarity index 60%
rename from src/StoryListItem.tsx
rename to src/components/StoryListItem.tsx
index c373af2..c182653 100644
--- a/src/StoryListItem.tsx
+++ b/src/components/StoryListItem.tsx
@@ -15,121 +15,119 @@ import {
} from 'react-native';
import GestureRecognizer from 'react-native-swipe-gestures';
-import { usePrevious } from './helpers/StateHelpers';
-import { isNullOrWhitespace } from './helpers/ValidationHelpers';
+import { usePrevious } from '../helpers/StateHelpers';
+import { isNullOrWhitespace } from '../helpers/ValidationHelpers';
-import type { IUserStoryItem } from './interfaces/IUserStory';
+import { ActionStates } from '../index';
+import type { IUserStoryItem, ICustomProfileBanner } from '../index';
const { width, height } = Dimensions.get('window');
-export enum CloseStates {
- PREVIOUS = 'previous',
- NEXT = 'next',
-}
-
type Props = {
index: number;
profileName: string;
- profileImage: string;
+ profileImage: any; // @todo
duration?: number;
- onFinish?: (state: CloseStates) => void;
+ onFinish?: (state: ActionStates) => void;
onClosePress: () => void;
- key: number;
swipeText?: string;
- customSwipeUpComponent?: () => React.ReactNode;
- customCloseComponent?: () => React.ReactNode;
+ customSwipeUpButton?: () => React.ReactNode;
+ customCloseButton?: () => React.ReactNode;
+ customProfileBanner?: (props: ICustomProfileBanner) => React.ReactNode;
stories: IUserStoryItem[];
+ showProfileBanner?: boolean;
currentPage: number;
};
const StoryListItem = (props: Props) => {
- const stories = props.stories;
-
- const [load, setLoad] = useState(true);
+ const [loading, setLoading] = useState(true);
const [pressed, setPressed] = useState(false);
- const [content, setContent] = useState(
- stories.map((story) => {
- return {
- image: story.image,
- onPress: story.onPress,
- swipeText: story.swipeText,
- finish: 0,
- };
- })
+ const [currStoryIndex, setCurrentStoryIndex] = useState(0);
+ const [content, setContent] = useState(props.stories);
+
+ const currStory = useMemo(
+ () => content[currStoryIndex],
+ [content, currStoryIndex]
);
- const [current, setCurrent] = useState(0);
+ const currPageIndex = useMemo(() => props.currentPage, [props.currentPage]);
+
+ const swipeText = useMemo(
+ () => content?.[currStoryIndex]?.swipeText || props.swipeText || 'Swipe Up',
+ [content, currStoryIndex, props.swipeText]
+ );
const progress = useRef(new Animated.Value(0)).current;
- const prevCurrentPage = usePrevious(props.currentPage);
+ const prevPageIndex = usePrevious(currPageIndex);
+ const prevStoryIndex = usePrevious(currStoryIndex);
+ // call every page changes
useEffect(() => {
- let isPrevious = !!prevCurrentPage && prevCurrentPage > props.currentPage;
+ const isPrevious = !!prevPageIndex && prevPageIndex > currPageIndex;
+
if (isPrevious) {
- setCurrent(content.length - 1);
+ setCurrentStoryIndex(content.length - 1);
} else {
- setCurrent(0);
+ setCurrentStoryIndex(0);
}
let data = [...content];
data.map((x, i) => {
if (isPrevious) {
- x.finish = 1;
+ x.finished = true;
if (i === content.length - 1) {
- x.finish = 0;
+ x.finished = false;
}
} else {
- x.finish = 0;
+ x.finished = false;
}
});
+
setContent(data);
- start();
+ startStory();
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [props.currentPage]);
-
- const prevCurrent = usePrevious(current);
+ }, [currPageIndex]);
+ // call every story change requests
+ // ... and decide next or prev
useEffect(() => {
- if (!isNullOrWhitespace(prevCurrent)) {
- const isCurrent = !!prevCurrent && current > prevCurrent;
- if (isCurrent && content[current - 1].image === content[current].image) {
- start();
- } else if (
- isCurrent &&
- content[current + 1].image === content[current].image
- ) {
- start();
+ if (!isNullOrWhitespace(prevStoryIndex)) {
+ const isNextStory = !!prevStoryIndex && currStoryIndex > prevStoryIndex;
+ const isPrevStory = !isNextStory;
+
+ const nextStory = content[currStoryIndex + 1];
+ const prevStory = content[currStoryIndex - 1];
+
+ if (isNextStory && prevStory.id === currStory.id) {
+ startStory();
+ } else if (isPrevStory && nextStory?.id === currStory.id) {
+ startStory();
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [current]);
+ }, [currStoryIndex]);
- const start = () => {
- setLoad(false);
- progress.setValue(0);
- startAnimation();
- };
-
- const startAnimation = () => {
+ const startProgressAnimation = () => {
Animated.timing(progress, {
toValue: 1,
duration: props.duration,
useNativeDriver: false,
}).start(({ finished }) => {
- if (finished) {
- next();
- }
+ if (finished) next();
});
};
+ const startStory = () => {
+ setLoading(false);
+ progress.setValue(0);
+ startProgressAnimation();
+ };
+
const onSwipeUp = () => {
- if (props.onClosePress) {
- props.onClosePress();
- }
- if (content[current].onPress) {
- content[current].onPress();
- }
+ if (props.onClosePress) props.onClosePress();
+
+ if (currStory.onPress) currStory.onPress();
};
const onSwipeDown = () => {
@@ -138,72 +136,79 @@ const StoryListItem = (props: Props) => {
const next = () => {
// check if the next content is not empty
- setLoad(true);
- if (current !== content.length - 1) {
+ setLoading(true);
+ if (currStoryIndex !== content.length - 1) {
let data = [...content];
- data[current].finish = 1;
+ data[currStoryIndex].finished = true;
setContent(data);
- setCurrent(current + 1);
+ setCurrentStoryIndex(currStoryIndex + 1);
progress.setValue(0);
} else {
// the next content is empty
- close(CloseStates.NEXT);
+ close(ActionStates.NEXT);
}
};
const previous = () => {
// checking if the previous content is not empty
- setLoad(true);
- if (current - 1 >= 0) {
+ setLoading(true);
+ if (currStoryIndex - 1 >= 0) {
let data = [...content];
- data[current].finish = 0;
+ data[currStoryIndex].finished = false;
setContent(data);
- setCurrent(current - 1);
+ setCurrentStoryIndex(currStoryIndex - 1);
progress.setValue(0);
} else {
// the previous content is empty
- close(CloseStates.PREVIOUS);
+ close(ActionStates.PREVIOUS);
}
};
- const close = (state: CloseStates) => {
+ const close = (state: ActionStates) => {
let data = [...content];
- data.map((x) => (x.finish = 0));
+ data.map((x) => (x.finished = false));
setContent(data);
progress.setValue(0);
- if (props.currentPage === props.index) {
+ if (currPageIndex === props.index) {
if (props.onFinish) {
props.onFinish(state);
}
}
};
- const swipeText = useMemo(
- () => content?.[current]?.swipeText || props.swipeText || 'Swipe Up',
- [content, current, props.swipeText]
- );
-
const renderSwipeButton = () => {
- if (props.customSwipeUpComponent) {
- return props.customSwipeUpComponent();
+ if (props.customSwipeUpButton) {
+ return props.customSwipeUpButton();
}
- return (
- <>
-
- {swipeText}
- >
- );
+ return {swipeText};
};
const renderCloseButton = () => {
- if (props.customCloseComponent) {
- return props.customCloseComponent();
+ if (props.customCloseButton) {
+ return props.customCloseButton();
}
return X;
};
+ const renderProfileBanner = () => {
+ if (!props.showProfileBanner) return;
+
+ if (props.customProfileBanner)
+ return props.customProfileBanner({
+ image: props.profileImage,
+ name: props.profileName,
+ });
+
+ return (
+ <>
+
+ {props.profileName}
+ >
+ );
+ };
+
return (
{
>
start()}
- source={{ uri: content[current].image }}
+ onLoadEnd={() => startStory()}
+ source={{ uri: currStory.image }}
style={styles.image}
/>
- {load && (
+ {loading && (
@@ -233,7 +238,12 @@ const StoryListItem = (props: Props) => {
{
})}
-
-
- {props.profileName}
-
+ {renderProfileBanner()}
{
if (props.onClosePress) {
@@ -266,34 +270,30 @@ const StoryListItem = (props: Props) => {
onLongPress={() => setPressed(true)}
onPressOut={() => {
setPressed(false);
- startAnimation();
+ startProgressAnimation();
}}
onPress={() => {
- if (!pressed && !load) {
- previous();
- }
+ if (!pressed && !loading) previous();
}}
>
-
+
progress.stopAnimation()}
onLongPress={() => setPressed(true)}
onPressOut={() => {
setPressed(false);
- startAnimation();
+ startProgressAnimation();
}}
onPress={() => {
- if (!pressed && !load) {
- next();
- }
+ if (!pressed && !loading) next();
}}
>
-
+
- {content[current].onPress && (
+ {currStory.onPress && (
{
return input.toString().replace(/\s/g, '').length < 1;
};
+
+export const isUrl = (string: string) => {
+ try {
+ return Boolean(new URL(string));
+ } catch (e) {
+ return false;
+ }
+};
diff --git a/src/index.tsx b/src/index.tsx
index 708d55e..88bdb37 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,3 +1,45 @@
-import Story from './Story';
+import { Story } from './components';
+
+export enum ActionStates {
+ PREVIOUS,
+ NEXT,
+}
+
+export interface IUserStory {
+ id: number | string;
+ avatar: any; // @todo
+ name: string;
+ stories: IUserStoryItem[];
+ seen?: Boolean;
+ extra?: {
+ [key: string]: any;
+ };
+}
+
+export interface IUserStoryItem {
+ id: number | string;
+ image: string;
+ onPress?: () => void;
+ swipeText?: string;
+ finished?: boolean;
+}
+
+export interface ICustomStoryView {
+ index: number;
+ data: IUserStory;
+ currentPage: number;
+ changeStory: (state: ActionStates) => void;
+ close: () => void;
+}
+
+export interface ICustomStoryList {
+ data: IUserStory[];
+ onStoryPress: (item: IUserStory, index: number) => void;
+}
+
+export interface ICustomProfileBanner {
+ image: any;
+ name: string;
+}
export default Story;
diff --git a/src/interfaces/IUserStory.ts b/src/interfaces/IUserStory.ts
deleted file mode 100644
index 34cb64e..0000000
--- a/src/interfaces/IUserStory.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-export interface IUserStory {
- id: number;
- avatar: string;
- name: string;
- stories: IUserStoryItem[];
- seen?: Boolean;
-}
-
-export interface IUserStoryItem {
- id: number;
- image: string;
- onPress?: any;
- swipeText?: string;
-}