├── .babelrc
├── .gitignore
├── README.md
├── assets
├── .DS_Store
├── fonts
│ └── SpaceMono-Regular.ttf
└── images
│ ├── exponent-icon@2x.png
│ ├── exponent-icon@3x.png
│ └── exponent-wordmark.png
├── components
├── StyledText.js
└── __tests__
│ ├── StyledText-test.js
│ └── __snapshots__
│ └── StyledText-test.js.snap
├── constants
└── Colors.js
├── demo.gif
├── demo2.gif
├── exp.json
├── main.js
├── navigation
├── RootNavigation.js
├── Router.js
└── fadeIn.js
├── package.json
├── screens
├── Landing.js
├── LinksScreen.js
├── SettingsScreen.js
├── common
│ ├── button-bar.js
│ ├── button.js
│ ├── chat-item.js
│ ├── chat-search-bar.js
│ ├── create-post.js
│ ├── drawer.js
│ ├── image-post.js
│ ├── newsfeed-item.js
│ ├── onYourMind.js
│ ├── search-bar.js
│ └── single-image.js
├── helpers
│ └── index.js
├── imageScreen.js
└── img
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ ├── 4.jpg
│ ├── 5.jpg
│ ├── bob.png
│ ├── cookiemonster.jpeg
│ ├── elmo.jpg
│ └── me.png
└── utilities
├── __tests__
└── cacheAssetsAsync-test.js
├── cacheAssetsAsync.js
└── cacheHelpers.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-exponent"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .exponent/**/*
3 | .idea
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-facebook-ui
2 | Pure javascript prototype of iOS Facebook UI for React Native framework. This only includes the newsfeed page--more to be updated in the future!
3 | This application may not run correctly on Android. Feel free to submit a pull reqeust for the fixed styling for Android version.
4 |
5 | ## Sponsored by Spurwing
6 |
7 | 
8 |
9 | This repo is sponsored by Spurwing, where their API Makes Adding Scheduling Quick, Reliable and Scalable.
10 |
11 | - Scheduling API
12 | - Enterprise Scheduling API
13 | - Scheduling API for Business
14 | - Scheduling and Calendar Management API
15 | - Booking API
16 |
17 | Check them out [here](https://github.com/Spurwing/Appointment-Scheduling-API)!
18 |
19 |
20 | ## Inspiration
21 | I was inspired by an app I use everyday--Facebook! :-)
22 |
23 | ## #Demo
24 | 
25 |
26 | Working on images: (1/5/2017)
27 |
28 | 
29 |
30 | ## Try it out
31 |
32 | Try it with Exponent: https://getexponent.com/@sungwoopark95/react-native-facebook-ui
33 |
34 | ## Run it locally
35 |
36 | To install, there are two steps:
37 |
38 | 1. Install Exponent XDE [following this
39 | guide](https://docs.getexponent.com/versions/latest/introduction/installation.html).
40 | Also install the Exponent app on your phone if you want to test it on
41 | your device, otherwise you don't need to do anything for the simulator.
42 | 2. Clone this repo and run `npm install`
43 | ```bash
44 | git clone https://github.com/ggomaeng/react-native-facebook-ui.git facebook
45 |
46 | cd facebook
47 | npm install
48 | ```
49 | 3. Open the project with Exponent XDE and run it.
50 |
51 | The MIT License (MIT)
52 | =====================
53 |
54 | Copyright © 2016 Sung Woo Park
55 |
56 | Permission is hereby granted, free of charge, to any person
57 | obtaining a copy of this software and associated documentation
58 | files (the “Software”), to deal in the Software without
59 | restriction, including without limitation the rights to use,
60 | copy, modify, merge, publish, distribute, sublicense, and/or sell
61 | copies of the Software, and to permit persons to whom the
62 | Software is furnished to do so, subject to the following
63 | conditions:
64 |
65 | The above copyright notice and this permission notice shall be
66 | included in all copies or substantial portions of the Software.
67 |
68 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
69 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
70 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
71 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
72 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
73 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
74 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
75 | OTHER DEALINGS IN THE SOFTWARE.
76 |
--------------------------------------------------------------------------------
/assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/assets/.DS_Store
--------------------------------------------------------------------------------
/assets/fonts/SpaceMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/assets/fonts/SpaceMono-Regular.ttf
--------------------------------------------------------------------------------
/assets/images/exponent-icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/assets/images/exponent-icon@2x.png
--------------------------------------------------------------------------------
/assets/images/exponent-icon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/assets/images/exponent-icon@3x.png
--------------------------------------------------------------------------------
/assets/images/exponent-wordmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/assets/images/exponent-wordmark.png
--------------------------------------------------------------------------------
/components/StyledText.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Text } from 'react-native';
3 |
4 | export class MonoText extends React.Component {
5 | render() {
6 | return (
7 |
11 | );
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/components/__tests__/StyledText-test.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import { MonoText } from '../StyledText';
4 | import renderer from 'react-test-renderer';
5 |
6 | it('renders correctly', () => {
7 | const tree = renderer.create(
8 | Snapshot test!
9 | ).toJSON();
10 |
11 | expect(tree).toMatchSnapshot();
12 | });
13 |
--------------------------------------------------------------------------------
/components/__tests__/__snapshots__/StyledText-test.js.snap:
--------------------------------------------------------------------------------
1 | exports[`test renders correctly 1`] = `
2 |
14 | Snapshot test!
15 |
16 | `;
17 |
--------------------------------------------------------------------------------
/constants/Colors.js:
--------------------------------------------------------------------------------
1 | const tintColor = '#4F8AFF';
2 |
3 | export default {
4 | tintColor,
5 | tabIconDefault: '#888',
6 | main: "#3B5998",
7 | searchBar: '#384778',
8 | black: '#212121',
9 | grayText: '#898F9C',
10 | gray: '#D3D6DB',
11 | chat_bg: '#3F4045',
12 | chat_line: '#72757A',
13 | like: '#AFB4BD',
14 | liked: '#5890FF',
15 | lightGray: '#FAFAFA',
16 | tabIconSelected: tintColor,
17 | tabBar: '#fefefe',
18 | errorBackground: 'red',
19 | errorText: '#fff',
20 | warningBackground: '#EAEB5E',
21 | warningText: '#666804',
22 | noticeBackground: tintColor,
23 | noticeText: '#fff',
24 | };
25 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/demo.gif
--------------------------------------------------------------------------------
/demo2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/demo2.gif
--------------------------------------------------------------------------------
/exp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-facebook-ui",
3 | "description": "No description",
4 | "slug": "react-native-facebook-ui",
5 | "sdkVersion": "12.0.0",
6 | "version": "1.0.0",
7 | "orientation": "portrait",
8 | "primaryColor": "#cccccc",
9 | "iconUrl": "https://s3.amazonaws.com/exp-brand-assets/ExponentEmptyManifest_192.png",
10 | "notification": {
11 | "iconUrl": "https://s3.amazonaws.com/exp-us-standard/placeholder-push-icon-blue-circle.png",
12 | "color": "#000000"
13 | },
14 | "loading": {
15 | "iconUrl": "https://s3.amazonaws.com/exp-brand-assets/ExponentEmptyManifest_192.png",
16 | "hideExponentText": false
17 | },
18 | "packagerOpts": {
19 | "assetExts": ["ttf"]
20 | },
21 | "ios": {
22 | "supportsTablet": true
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import Exponent from 'exponent';
2 | import React from 'react';
3 | import {
4 | AppRegistry,
5 | Platform,
6 | StatusBar,
7 | StyleSheet,
8 | View,
9 | } from 'react-native';
10 | import {
11 | NavigationProvider,
12 | StackNavigation,
13 | } from '@exponent/ex-navigation';
14 | import {
15 | Ionicons,
16 | } from '@exponent/vector-icons';
17 |
18 | import Router from './navigation/Router';
19 | import cacheAssetsAsync from './utilities/cacheAssetsAsync';
20 |
21 | class AppContainer extends React.Component {
22 | state = {
23 | appIsReady: false,
24 | }
25 |
26 | componentWillMount() {
27 | this._loadAssetsAsync();
28 | }
29 |
30 | async _loadAssetsAsync() {
31 | try {
32 | await cacheAssetsAsync({
33 | images: [
34 | require('./screens/img/bob.png'),
35 | require('./screens/img/cookiemonster.jpeg'),
36 | require('./screens/img/elmo.jpg'),
37 | require('./screens/img/me.png'),
38 | require('./screens/img/1.jpg'),
39 | require('./screens/img/2.jpg'),
40 | require('./screens/img/3.jpg'),
41 | require('./screens/img/4.jpg'),
42 | require('./screens/img/5.jpg'),
43 | ],
44 | fonts: [
45 | Ionicons.font,
46 | ]
47 | });
48 | } catch(e) {
49 | console.warn(
50 | 'There was an error caching assets (see: main.js), perhaps due to a ' +
51 | 'network timeout, so we skipped caching. Reload the app to try again.'
52 | );
53 | console.log(e.message);
54 | } finally {
55 | this.setState({appIsReady: true});
56 | }
57 | }
58 |
59 | render() {
60 | if (this.state.appIsReady) {
61 | return (
62 |
63 |
64 |
65 |
66 |
67 | {Platform.OS === 'ios' && }
68 | {Platform.OS === 'android' && }
69 |
70 | );
71 | } else {
72 | return (
73 |
74 | );
75 | }
76 | }
77 | }
78 |
79 | const styles = StyleSheet.create({
80 | container: {
81 | flex: 1,
82 | backgroundColor: '#fff',
83 | },
84 | statusBarUnderlay: {
85 | height: 24,
86 | backgroundColor: 'rgba(0,0,0,0.2)',
87 | },
88 | });
89 |
90 | Exponent.registerRootComponent(AppContainer);
91 |
--------------------------------------------------------------------------------
/navigation/RootNavigation.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | View,
5 | } from 'react-native';
6 | import {
7 | StackNavigation,
8 | TabNavigation,
9 | TabNavigationItem,
10 | } from '@exponent/ex-navigation';
11 | import {
12 | Ionicons,
13 | } from '@exponent/vector-icons';
14 |
15 | import Colors from '../constants/Colors';
16 |
17 | export default class RootNavigation extends React.Component {
18 |
19 | render() {
20 | return (
21 |
24 | this._renderIcon('ios-paper', isSelected)}>
27 |
28 |
29 |
30 | this._renderIcon('ios-basket', isSelected)}>
33 |
34 |
35 |
36 | this._renderIcon('md-globe', isSelected)}>
39 |
40 |
41 | this._renderIcon('ios-menu', isSelected)}>
44 |
45 |
46 |
47 | );
48 | }
49 |
50 | _renderIcon(name, isSelected) {
51 | return (
52 |
57 | );
58 | }
59 |
60 |
61 | }
62 |
63 | const styles = StyleSheet.create({
64 | container: {
65 | flex: 1,
66 | backgroundColor: '#fff',
67 | },
68 | selectedTab: {
69 | color: Colors.tabIconSelected,
70 | },
71 | });
72 |
--------------------------------------------------------------------------------
/navigation/Router.js:
--------------------------------------------------------------------------------
1 | import {
2 | createRouter,
3 | } from '@exponent/ex-navigation';
4 |
5 | import Landing from '../screens/Landing';
6 | import LinksScreen from '../screens/LinksScreen';
7 | import ImageScreen from '../screens/imageScreen';
8 | import SettingsScreen from '../screens/SettingsScreen';
9 | import RootNavigation from './RootNavigation';
10 |
11 |
12 | export default createRouter(() => ({
13 | landing: () => Landing,
14 | images: () => ImageScreen,
15 | links: () => LinksScreen,
16 | settings: () => SettingsScreen,
17 | rootNavigation: () => RootNavigation,
18 | }));
19 |
--------------------------------------------------------------------------------
/navigation/fadeIn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 1/2/17.
3 | */
4 | import {
5 | Animated,
6 | Easing
7 | } from 'react-native';
8 |
9 | const configureTimingTransition = (transitionProps, previousTransitionProps) => ({
10 | timing: Animated.timing,
11 | easing: Easing.inOut(Easing.linear),
12 | duration: 150,
13 | });
14 |
15 | export default FadedZoom = {
16 | configureTransition: configureTimingTransition,
17 | sceneAnimations: (props) => {
18 | const {
19 | position,
20 | scene,
21 | } = props;
22 |
23 | const index = scene.index;
24 | const inputRange = [index - 1, index, index + 1];
25 |
26 | const opacity = position.interpolate({
27 | inputRange,
28 | outputRange: [0, 1, 0],
29 | });
30 |
31 | const scale = position.interpolate({
32 | inputRange,
33 | outputRange: [1.2, 1, 1.2],
34 | });
35 |
36 | return {
37 | opacity,
38 | transform: [
39 | { translateX: 0 },
40 | { translateY: 0 },
41 | { scale: scale },
42 | ],
43 | };
44 | },
45 | gestures: null,
46 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-facebook-ui",
3 | "version": "0.0.0",
4 | "description": "Hello Exponent!",
5 | "author": null,
6 | "main": "main.js",
7 | "scripts": {
8 | "test": "jest"
9 | },
10 | "jest": {
11 | "preset": "jest-exponent"
12 | },
13 | "dependencies": {
14 | "@exponent/ex-navigation": "~2.1.0",
15 | "@exponent/samples": "~1.0.2",
16 | "@exponent/vector-icons": "~2.0.3",
17 | "exponent": "~12.0.3",
18 | "lodash": "^4.17.2",
19 | "moment": "^2.17.1",
20 | "react": "~15.3.2",
21 | "react-native": "git+https://github.com/exponentjs/react-native#sdk-12.0.0"
22 | },
23 | "devDependencies": {
24 | "jest-exponent": "~0.1.3"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/screens/Landing.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/17/16.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | Animated,
7 | View,
8 | Text,
9 | Dimensions,
10 | RefreshControl,
11 | Modal,
12 | ScrollView,
13 | ListView,
14 | StyleSheet
15 | } from 'react-native';
16 |
17 | const {width, height} = Dimensions.get('window');
18 |
19 | import Colors from '../constants/Colors';
20 | import SearchBar from './common/search-bar';
21 | import ButtonBar from './common/button-bar';
22 | import OnYourMind from './common/onYourMind';
23 | import NewsFeedItem from './common/newsfeed-item';
24 | import CreatePost from './common/create-post';
25 |
26 |
27 | import Drawer from './common/drawer';
28 | import _ from 'lodash';
29 |
30 | //1 is regular post, 2 is image
31 | const data = ['0',
32 | {type: 'image', images: ['1']},
33 | {type: 'image', images: ['1', '2']},
34 | {type: 'image', images: ['1', '2', '3']},
35 | {type: 'image', images: ['1', '2', '3', '4']},
36 | {type: 'image', images: ['1', '2', '3', '4', '5']},
37 | {type: 'image', images: ['1', '2', '3', '4', '5', '6']},
38 | {type: 'post'},
39 | {type: 'post'},
40 | {type: 'post'}
41 | ];
42 | const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
43 |
44 | export default class Landing extends Component {
45 | constructor(props) {
46 | super(props);
47 |
48 | this.state = {
49 | modal: false,
50 | refreshing: false,
51 | loading: false,
52 | opacity: new Animated.Value(1),
53 | header_height: new Animated.Value(96),
54 |
55 | dataSource: ds.cloneWithRows(data)
56 | };
57 |
58 | this.offsetY = 0;
59 | this.offsetX = new Animated.Value(0);
60 | this.content_height = 0;
61 | this._onScroll = this._onScroll.bind(this);
62 | this.loadMore = _.debounce(this.loadMore, 300);
63 | this._onDrawerOpen = this._onDrawerOpen.bind(this);
64 | }
65 |
66 | componentDidMount() {
67 | setTimeout(() => {this.measureView()}, 0);
68 | }
69 |
70 | measureView() {
71 | this.refs.view.measure((a, b, w, h, px, py) => {
72 | this.content_height = h;
73 | });
74 | }
75 |
76 |
77 | _onRefresh() {
78 | this.setState({refreshing: true});
79 | setTimeout(() => {
80 | this.setState({refreshing: false});
81 | }, 1500)
82 | }
83 |
84 | _renderRow(data) {
85 |
86 | if (data == '0') {
87 | return this.setState({modal: true})}/>
88 | }
89 |
90 | return
91 | }
92 |
93 | renderModal() {
94 | return (
95 | this.setState({modal: false})}
100 | >
101 | this.setState({modal: false})} />
102 |
103 | )
104 |
105 | }
106 |
107 | loadMore() {
108 | console.log('should load more');
109 | this.setState({loading: true});
110 | //add two more child views
111 | data.push('1');
112 | data.push('1');
113 | this.setState({dataSource: ds.cloneWithRows(data)});
114 |
115 | }
116 |
117 | _onScroll(event) {
118 | const e = event.nativeEvent;
119 | const l_height = e.contentSize.height;
120 | const offset = e.contentOffset.y;
121 |
122 | if(offset > this.offsetY) {
123 | console.log('scrolling down');
124 | if(!(offset < 32)) {
125 | this.refs.buttonBar.hide();
126 | }
127 |
128 | if(!(offset < 56)) {
129 | this.refs.searchBar.hide();
130 | }
131 |
132 | //if
133 | } else {
134 | console.log('scrolling up');
135 |
136 | this.refs.buttonBar.show();
137 | setTimeout(() => {this.refs.searchBar.show();}, 150);
138 |
139 | }
140 |
141 | this.offsetY = offset;
142 |
143 |
144 | if(offset + this.content_height >= l_height) {
145 | console.log('end');
146 | this.loadMore();
147 | }
148 |
149 | // console.log(e);
150 | }
151 |
152 | getStyle() {
153 | return {
154 | opacity: this.offsetX.interpolate({
155 | inputRange: [0, width * 4/5],
156 | outputRange: [1, 0],
157 | }),
158 | }
159 | }
160 |
161 | renderFade() {
162 | return (
163 |
164 |
165 | )
166 | }
167 |
168 | renderDrawer() {
169 | return (
170 |
171 | )
172 | }
173 |
174 | _onDrawerOpen(event) {
175 | const e = event.nativeEvent;
176 | const offset = e.contentOffset.x;
177 | this.offsetX.setValue(offset);
178 | }
179 |
180 | openChat() {
181 | this.refs.scrollview.scrollTo({x: width * 4/5, y: 0, animated: true});
182 | }
183 |
184 |
185 | render() {
186 |
187 | return (
188 |
189 | {this.renderDrawer()}
190 |
199 |
200 |
201 |
202 |
203 |
209 | }
210 |
211 | onScroll={this._onScroll}
212 | dataSource={this.state.dataSource}
213 | renderRow={(data) => this._renderRow(data)}
214 | />
215 |
216 | {this.renderFade()}
217 |
218 | {this.renderModal()}
219 |
220 | )
221 | }
222 | }
223 |
224 | const styles= StyleSheet.create({
225 | container: {
226 | flex: 1,
227 | width,
228 | height,
229 | backgroundColor: Colors.gray
230 | },
231 | fade: {
232 | height,
233 | backgroundColor: 'black',
234 | width: width * 4/5,
235 | },
236 | drawer: {
237 | height,
238 | padding: 8,
239 | paddingTop: 20,
240 | width: width * 4/5,
241 | position: 'absolute',
242 | backgroundColor: Colors.chat_bg,
243 | right: 0
244 |
245 | }
246 | })
--------------------------------------------------------------------------------
/screens/LinksScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | ScrollView,
4 | StyleSheet,
5 | } from 'react-native';
6 | import {
7 | ExponentLinksView,
8 | } from '@exponent/samples';
9 |
10 | export default class LinksScreen extends React.Component {
11 | static route = {
12 | navigationBar: {
13 | title: 'Links',
14 | },
15 | }
16 |
17 | render() {
18 | return (
19 |
22 |
23 | { /* Go ahead and delete ExponentLinksView and replace it with your
24 | * content, we just wanted to provide you with some helpful links */ }
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | }
32 |
33 | const styles = StyleSheet.create({
34 | container: {
35 | flex: 1,
36 | paddingTop: 15,
37 | },
38 | });
39 |
--------------------------------------------------------------------------------
/screens/SettingsScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | ScrollView,
4 | StyleSheet,
5 | } from 'react-native';
6 | import {
7 | ExponentConfigView,
8 | } from '@exponent/samples';
9 |
10 | export default class SettingsScreen extends React.Component {
11 | static route = {
12 | navigationBar: {
13 | title: 'exp.json'
14 | },
15 | }
16 |
17 | render() {
18 | return (
19 |
22 |
23 | { /* Go ahead and delete ExponentConfigView and replace it with your
24 | * content, we just wanted to give you a quick view of your config */ }
25 |
26 |
27 |
28 | );
29 | }
30 | }
31 |
32 | const styles = StyleSheet.create({
33 | container: {
34 | flex: 1,
35 | },
36 | });
37 |
--------------------------------------------------------------------------------
/screens/common/button-bar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/17/16.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | Animated,
7 | View,
8 | Text,
9 | StyleSheet
10 | } from 'react-native';
11 |
12 | import {Ionicons} from '@exponent/vector-icons'
13 | import Colors from '../../constants/Colors';
14 |
15 | export default class ButtonBar extends Component {
16 | constructor() {
17 | super();
18 | this.state = {
19 | height: new Animated.Value(36),
20 | buttons: ['Live', 'Photo', 'Check In'],
21 | icons: ['ios-videocam', 'ios-camera', 'ios-pin']
22 | };
23 | }
24 |
25 | componentDidMount() {
26 | setTimeout(() => {this.measureView()}, 0)
27 | }
28 |
29 | measureView() {
30 | console.log('measuring view');
31 | this.refs.container.measure((a, b, w, h, x, y) => {
32 | this.setState({height: new Animated.Value(h), original: h});
33 | });
34 | }
35 |
36 | hide() {
37 |
38 | if(this.state.animating) {
39 | return;
40 | }
41 | console.log('animating');
42 |
43 | this.setState({animating: true});
44 | Animated.timing(
45 | this.state.height,
46 | {toValue: 0}
47 | ).start();
48 | }
49 |
50 | show() {
51 | if(!this.state.animating) {
52 | return;
53 | }
54 | console.log('animating');
55 | this.setState({animating: false});
56 | Animated.timing(
57 | this.state.height,
58 | {toValue: this.state.original}
59 | ).start();
60 | }
61 |
62 | renderButtons() {
63 | const {buttons, icons} = this.state;
64 | return buttons.map((button, i) => {
65 | return (
66 |
67 |
68 | {button}
69 |
70 | )
71 | })
72 |
73 | }
74 |
75 | getStyle() {
76 | const {height} = this.state;
77 |
78 |
79 | return {height, opacity: height.interpolate({
80 | inputRange: [0, 36],
81 | outputRange: [0, 1],
82 | })}
83 | }
84 |
85 | render() {
86 |
87 | return (
88 |
89 |
90 | {this.renderButtons()}
91 |
92 |
93 | )
94 | }
95 | }
96 |
97 | const styles = StyleSheet.create({
98 | container: {
99 | flexDirection: 'row',
100 | height: 36,
101 | backgroundColor: Colors.lightGray,
102 | borderBottomWidth: StyleSheet.hairlineWidth
103 | },
104 |
105 | buttonItem: {
106 | flex: 1,
107 | backgroundColor: 'transparent',
108 | flexDirection: 'row',
109 | justifyContent: 'center',
110 | alignItems: 'center',
111 | borderLeftWidth: StyleSheet.hairlineWidth,
112 | borderColor: '#ddd'
113 | },
114 |
115 | text: {
116 | fontSize: 14,
117 | backgroundColor: 'transparent',
118 | fontWeight: '700',
119 | marginLeft: 8,
120 | color: Colors.black
121 | }
122 | });
--------------------------------------------------------------------------------
/screens/common/button.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/17/16.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | View,
7 | Text,
8 | TouchableOpacity,
9 | StyleSheet
10 | } from 'react-native';
11 |
12 | import Colors from '../../constants/Colors';
13 | import {Ionicons} from '@exponent/vector-icons';
14 |
15 | export default class Button extends Component {
16 | constructor(props) {
17 | super(props);
18 | this.state = {
19 | pressed: false,
20 | name: props.name,
21 | icon: props.icon
22 | }
23 | }
24 |
25 | pressed(name) {
26 | if(name == 'Like') {
27 | this.setState({pressed: !this.state.pressed});
28 | if(!this.state.pressed) {
29 | this.props.onPress('Like');
30 | } else {
31 | this.props.onPress('Dislike');
32 | }
33 |
34 |
35 | }
36 |
37 |
38 |
39 |
40 |
41 |
42 | }
43 |
44 | render() {
45 | const {pressed, name, icon} = this.state;
46 |
47 | return (
48 | this.pressed(name)} style={styles.buttonItem}>
49 |
50 | {name}
51 |
52 | )
53 | }
54 | }
55 |
56 | const styles = StyleSheet.create({
57 | buttonContainer: {
58 | flexDirection: 'row',
59 | height: 36,
60 | borderBottomWidth: StyleSheet.hairlineWidth
61 | },
62 |
63 |
64 | buttonItem: {
65 | flex: 1,
66 | backgroundColor: 'transparent',
67 | flexDirection: 'row',
68 | justifyContent: 'center',
69 | alignItems: 'center',
70 | },
71 |
72 | text: {
73 | backgroundColor: 'transparent',
74 | fontSize: 14,
75 | fontWeight: '700',
76 | marginLeft: 8,
77 | }
78 | })
--------------------------------------------------------------------------------
/screens/common/chat-item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/21/16.
3 | */
4 | import React from 'react';
5 | import {
6 | View,
7 | Image,
8 | Text,
9 | StyleSheet
10 | } from 'react-native';
11 |
12 | import Colors from '../../constants/Colors';
13 | import {randomProfile} from '../helpers';
14 | import {Ionicons} from '@exponent/vector-icons';
15 |
16 | export default () => {
17 |
18 | const avatar = randomProfile();
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
26 | {avatar.name}
27 | {avatar.online ?
28 | ·
29 | :
30 |
31 | 30m
32 |
33 |
34 | }
35 |
36 |
37 |
38 | )
39 | }
40 |
41 | const styles = StyleSheet.create({
42 | container: {
43 | paddingLeft: 12,
44 | flexDirection: 'row',
45 | alignItems: 'center',
46 | height: 48,
47 | borderBottomColor: Colors.chat_line,
48 | borderBottomWidth: StyleSheet.hairlineWidth
49 | },
50 |
51 | desc: {
52 | flex: 1,
53 | flexDirection: 'row',
54 | justifyContent: 'space-between',
55 | alignItems: 'center',
56 | },
57 |
58 | descText: {
59 | color: Colors.chat_line,
60 | fontSize: 12,
61 | marginRight: 8,
62 |
63 | },
64 |
65 | phone: {
66 | flexDirection: 'row',
67 | alignItems: 'center',
68 | marginRight: 12,
69 | },
70 |
71 | text: {
72 | color: 'white',
73 | paddingLeft: 10,
74 | fontSize: 14,
75 | fontWeight: '500'
76 | },
77 |
78 | img: {
79 | height: 40,
80 | width: 40
81 | }
82 | });
--------------------------------------------------------------------------------
/screens/common/chat-search-bar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/21/16.
3 | */
4 | import React from 'react';
5 | import {
6 | View,
7 | Text,
8 | TextInput,
9 | StyleSheet
10 | } from 'react-native';
11 |
12 | import {Ionicons} from '@exponent/vector-icons';
13 | import Colors from '../../constants/Colors';
14 |
15 | export default () => {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | )
32 | }
33 |
34 | const styles = StyleSheet.create({
35 | container: {
36 | marginTop: 8,
37 | padding: 12,
38 |
39 | },
40 |
41 | icon: {
42 | alignItems: 'center',
43 | justifyContent: 'center',
44 | marginLeft: 8,
45 | },
46 |
47 |
48 | searchContainer: {
49 | height: 28,
50 | flexDirection: 'row'
51 | },
52 |
53 | search: {
54 | flex: 1,
55 | backgroundColor: '#292C34',
56 | justifyContent: 'center',
57 | alignItems: 'center',
58 | padding: 4,
59 | fontSize: 12,
60 | color: 'white',
61 | borderRadius: 5,
62 | },
63 |
64 | searchBarContainer: {
65 | flex: 1,
66 | flexDirection: 'row',
67 | alignItems: 'center',
68 | height: 28,
69 | backgroundColor: '#292C34',
70 | borderRadius: 5,
71 | padding: 8,
72 | },
73 |
74 | searchIcon: {
75 | justifyContent: 'center',
76 | alignItems: 'center',
77 | },
78 |
79 | })
--------------------------------------------------------------------------------
/screens/common/create-post.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/21/16.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | View,
7 | Image,
8 | Dimensions,
9 | Keyboard,
10 | Text,
11 | TextInput,
12 | TouchableOpacity,
13 | StatusBar,
14 | StyleSheet
15 | } from 'react-native';
16 |
17 | const {width, height} = Dimensions.get('window');
18 |
19 |
20 |
21 | import Colors from '../../constants/Colors';
22 | import {Ionicons} from '@exponent/vector-icons';
23 |
24 | export default class CreatePost extends Component{
25 | constructor() {
26 | super();
27 | this.state = {
28 | visibleHeight: Dimensions.get('window').height,
29 | k_visible: false,
30 | }
31 | }
32 |
33 | componentDidMount () {
34 | Keyboard.addListener('keyboardWillShow', this.keyboardWillShow.bind(this));
35 | Keyboard.addListener('keyboardWillHide', this.keyboardWillHide.bind(this));
36 | }
37 |
38 | componentWillUnmount() {
39 | Keyboard.removeListener('keyboardWillShow');
40 | Keyboard.removeListener('keyboardWillHide');
41 | }
42 |
43 | keyboardWillShow (e) {
44 | let newSize = Dimensions.get('window').height - e.endCoordinates.height
45 | this.setState({visibleHeight: newSize, k_visible: true})
46 | }
47 |
48 | keyboardWillHide (e) {
49 | if(this.componentDidMount) {
50 | this.setState({visibleHeight: Dimensions.get('window').height, k_visible: false})
51 | }
52 |
53 | }
54 |
55 |
56 | renderHeader() {
57 | return (
58 |
61 |
62 | Cancel
63 |
64 | Update Status
65 | Post
66 |
67 | )
68 | }
69 |
70 | renderAvatar() {
71 | return (
72 |
73 |
74 |
75 | Sung Woo Park
76 |
77 |
79 |
80 | Public
81 |
82 |
83 |
85 |
86 | Seoul
87 |
88 |
89 |
90 |
91 |
92 | )
93 | }
94 |
95 | renderText() {
96 | return (
97 |
98 |
99 |
100 | )
101 | }
102 |
103 | renderMenu() {
104 | const {k_visible} = this.state;
105 | if(k_visible) {
106 | return (
107 | {Keyboard.dismiss()}}
109 | style={{height: 56, borderTopWidth: StyleSheet.hairlineWidth, borderColor: Colors.chat_line,
110 | flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingLeft: 16}}>
111 | Add to your post
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | )
121 | }
122 |
123 | return (
124 | this.renderList()
125 | )
126 | }
127 |
128 | renderList() {
129 | const objs =
130 | [
131 | {
132 | icon: 'md-camera',
133 | color: '#93B75F',
134 | name: 'Photo/Video'
135 | },
136 | {
137 | icon: 'md-videocam',
138 | color: '#E7404E',
139 | name: 'Live Video'
140 | },
141 | {
142 | icon: 'md-pin',
143 | color: '#D8396F',
144 | name: 'Check In'
145 | },
146 | {
147 | icon: 'ios-happy',
148 | color: '#EDC370',
149 | name: 'Feeling/Activity'
150 | },
151 | {
152 | icon: 'ios-person-add',
153 | color: '#628FF6',
154 | name: 'Tag Friends'
155 | }
156 | ];
157 |
158 | return objs.map((o, i) => {
159 | return (
160 |
162 |
163 | {o.name}
164 |
165 | )
166 | })
167 | }
168 |
169 | render() {
170 | return (
171 |
172 |
173 | {this.renderHeader()}
174 | {this.renderAvatar()}
175 | {this.renderText()}
176 | {this.renderMenu()}
177 |
178 | )
179 | }
180 | }
181 |
182 | const styles = StyleSheet.create({
183 | container: {
184 | flex: 1,
185 | },
186 |
187 | img: {
188 | width: 40,
189 | height: 40
190 | },
191 |
192 | icon: {
193 | marginLeft: 10
194 | }
195 | });
--------------------------------------------------------------------------------
/screens/common/drawer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/21/16.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | View,
7 | Dimensions,
8 | ListView,
9 | Text,
10 | StyleSheet
11 | } from 'react-native';
12 |
13 | const {width, height} = Dimensions.get('window');
14 | import Colors from '../../constants/Colors';
15 | import ChatItem from './chat-item';
16 | import ChatSearchBar from './chat-search-bar';
17 |
18 | const data = ['1', '1', '1', '1', '1', '1', '1', '1', '1'];
19 | const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
20 |
21 | export default class Drawer extends Component {
22 | constructor() {
23 | super();
24 | this.state = {
25 | dataSource: ds.cloneWithRows(data)
26 | }
27 | }
28 |
29 | _renderRow(data) {
30 | return (
31 |
32 | )
33 | }
34 |
35 |
36 | render() {
37 | return (
38 |
39 |
40 | this._renderRow(data)}
44 | />
45 |
46 |
47 | )
48 | }
49 | }
50 |
51 | const styles = StyleSheet.create({
52 | drawer: {
53 | height,
54 | paddingTop: 20,
55 | width: width * 4/5,
56 | position: 'absolute',
57 | backgroundColor: Colors.chat_bg,
58 | right: 0
59 |
60 | }
61 | });
--------------------------------------------------------------------------------
/screens/common/image-post.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 1/1/17.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | View,
7 | Dimensions,
8 | Text,
9 | StyleSheet,
10 | TouchableWithoutFeedback,
11 | Image,
12 | } from 'react-native';
13 |
14 | import {getImage} from '../helpers';
15 | const {width, height} = Dimensions.get('window');
16 | import { withNavigation } from '@exponent/ex-navigation';
17 |
18 | import SingleImage from './single-image';
19 |
20 |
21 | @withNavigation
22 | export default class ImagePost extends Component {
23 |
24 |
25 | renderImages() {
26 | const {imageCount, images} = this.props;
27 |
28 | switch(imageCount) {
29 | //1 image
30 | case 1:
31 | return (
32 |
33 |
34 |
35 | );
36 | break;
37 |
38 | case 2:
39 | return(
40 |
41 |
42 |
43 |
44 |
45 |
46 | );
47 | break;
48 | case 3:
49 | return(
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | );
60 | break;
61 | case 4:
62 | return(
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 | break;
77 | case 5:
78 | return(
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | );
93 | break;
94 | default:
95 | //for cases with 5+ pictures
96 | //@TODO render images with more than 6
97 | return(
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | + {imageCount - 5}
111 |
112 |
113 |
114 |
115 |
116 |
117 | );
118 |
119 |
120 | }
121 | }
122 |
123 | openImages() {
124 | const {images} = this.props;
125 |
126 | this.props.navigator.push('images', {images});
127 | }
128 |
129 | render() {
130 | const {imageCount} = this.props;
131 | return (
132 |
133 |
134 | This is an image post with {imageCount} image(s).
135 |
136 | {this.renderImages()}
137 |
138 | )
139 | }
140 | }
141 |
142 | const styles = StyleSheet.create({
143 | imageContainer: {
144 | height: height/2.5,
145 | },
146 |
147 | img: {
148 | flex: 1,
149 | width: null,
150 | height: null
151 | },
152 | textContainer: {
153 | padding: 16,
154 | paddingTop: 0,
155 | paddingBottom: 8
156 | }
157 | });
--------------------------------------------------------------------------------
/screens/common/newsfeed-item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/17/16.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | View,
7 | Text,
8 | Image,
9 | StyleSheet
10 | } from 'react-native';
11 |
12 | import Colors from '../../constants/Colors';
13 | import {Ionicons} from '@exponent/vector-icons';
14 | import {randomProfile} from '../helpers';
15 | import moment from 'moment';
16 | import ImagePost from './image-post';
17 |
18 | import Button from './button';
19 |
20 | export default class NewsFeedItem extends Component {
21 | constructor() {
22 | super();
23 | this.state = {
24 | profile: randomProfile(),
25 | time: moment().format('hh:mm A MMM Do'),
26 | buttons: ['Like', 'Comment', 'Share'],
27 | icons: ['md-thumbs-up', 'md-chatbubbles', 'ios-share-alt'],
28 | likes: 0,
29 | comments: 0
30 | };
31 | }
32 |
33 | buttonOnPress(name) {
34 | console.log(name);
35 | switch(name) {
36 | case 'Like':
37 | this.setState({likes: this.state.likes + 1});
38 | break;
39 | case 'Dislike':
40 | this.setState({likes: this.state.likes - 1});
41 | break;
42 | case 'Comment':
43 | this.setState({comments: this.state.comments + 1});
44 | break;
45 | default:
46 | return
47 | }
48 | }
49 |
50 | renderAvatar() {
51 | const {profile, time} = this.state;
52 | return (
53 |
54 |
55 |
56 | {profile.name}
57 | {time}
58 |
59 |
60 | )
61 | }
62 |
63 | renderLikesAndComments() {
64 | const {likes, comments} = this.state;
65 |
66 | if(likes == 0 && comments == 0) {
67 | return
68 | }
69 |
70 | return (
71 |
72 | {likes > 0 ? : ''}{likes == 0 ? '' : ' ' + likes}
73 | {comments == 0 ? '' : comments + ' Comments'}
74 |
75 | )
76 | }
77 |
78 | renderLikeBar() {
79 | const {buttons, icons} = this.state;
80 | return buttons.map((button, i) => {
81 | return (
82 |
83 | )
84 | })
85 | }
86 |
87 | renderContent() {
88 | const {data} = this.props;
89 | if(data.type == 'image') {
90 | return (
91 |
92 | )
93 | }
94 |
95 | return (
96 |
97 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer dapibus tincidunt massa, sit amet volutpat nisi imperdiet at. Morbi maximus, neque vitae posuere molestie, enim est posuere eros, at rutrum sem felis id nisl. Ut nec mi augue. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec tempor lectus facilisis, rutrum metus sed, volutpat felis. Fusce vitae dictum sapien, non facilisis nisl. Aenean elementum ante sed lectus sodales consequat in a mauris. Duis fermentum condimentum elit, vel suscipit purus lobortis in. Maecenas lorem quam, gravida in hendrerit pharetra, malesuada in elit.
98 |
99 | )
100 | }
101 |
102 | render() {
103 | return (
104 |
105 |
106 | {this.renderAvatar()}
107 | {this.renderContent()}
108 | {this.renderLikesAndComments()}
109 |
110 |
111 |
112 | {this.renderLikeBar()}
113 |
114 |
115 | )
116 | }
117 | }
118 |
119 | const styles = StyleSheet.create({
120 | container: {
121 | flex: 1,
122 | backgroundColor: 'white',
123 | marginBottom: 10,
124 | },
125 |
126 | content: {
127 | padding: 16,
128 | paddingTop: 0,
129 | paddingBottom: 0
130 | },
131 |
132 | line: {
133 | margin: 16,
134 | marginBottom: 0,
135 | borderColor: '#ddd',
136 | borderBottomWidth: StyleSheet.hairlineWidth
137 | },
138 |
139 | avatarContainer: {
140 | padding: 16,
141 | paddingBottom: 0,
142 | flexDirection: 'row',
143 | marginBottom: 10,
144 | },
145 |
146 | nameContainer: {
147 | marginLeft: 10,
148 | justifyContent: 'space-around'
149 | },
150 |
151 | name: {
152 | fontSize: 14,
153 | color: 'black',
154 | fontWeight: '600'
155 | },
156 |
157 | time: {
158 | color: 'gray',
159 | fontSize: 12,
160 | },
161 |
162 | profile: {
163 | backgroundColor: 'black',
164 | height: 40,
165 | width: 40,
166 | },
167 |
168 | buttonContainer: {
169 | flexDirection: 'row',
170 |
171 | height: 36,
172 | borderBottomWidth: StyleSheet.hairlineWidth
173 | },
174 |
175 |
176 | buttonItem: {
177 | flex: 1,
178 | flexDirection: 'row',
179 | justifyContent: 'center',
180 | alignItems: 'center',
181 | },
182 |
183 | text: {
184 | fontSize: 14,
185 | fontWeight: '700',
186 | marginLeft: 8,
187 | color: Colors.like
188 | },
189 |
190 | likeText: {
191 | fontSize: 12,
192 | color: Colors.grayText
193 | },
194 |
195 | likesComments: {
196 | padding: 16,
197 | paddingBottom: 0,
198 | flexDirection: 'row',
199 | justifyContent: 'space-between'
200 | }
201 | })
202 |
--------------------------------------------------------------------------------
/screens/common/onYourMind.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/17/16.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | View,
7 | Text,
8 | TextInput,
9 | Image,
10 | StyleSheet
11 | } from 'react-native';
12 |
13 | export default class onYourMind extends Component {
14 | render() {
15 | return (
16 |
17 |
18 |
19 |
20 | )
21 | }
22 | }
23 |
24 | const styles = StyleSheet.create({
25 | container: {
26 | flexDirection: 'row',
27 | alignItems: 'center',
28 | height: 64,
29 | padding: 16,
30 | backgroundColor: 'white',
31 | marginTop: 10,
32 | marginBottom: 10,
33 | },
34 |
35 | input: {
36 | flex: 1,
37 | fontSize: 14,
38 | marginLeft: 10
39 | },
40 |
41 | profile: {
42 | backgroundColor: 'black',
43 | height: 40,
44 | width: 40,
45 | }
46 | })
--------------------------------------------------------------------------------
/screens/common/search-bar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/17/16.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | Animated,
7 | View,
8 | Text,
9 | TextInput,
10 | TouchableOpacity,
11 | StyleSheet
12 | } from 'react-native';
13 |
14 | import Colors from '../../constants/Colors';
15 |
16 | import {Ionicons} from '@exponent/vector-icons';
17 |
18 | export default class SearchBar extends Component {
19 | constructor() {
20 | super();
21 | this.state = {
22 | height: null,
23 | animating: false,
24 | };
25 |
26 | this.measureView = this.measureView.bind(this);
27 | }
28 |
29 | componentDidMount() {
30 | setTimeout(() => {this.measureView()}, 0)
31 | }
32 |
33 | measureView() {
34 | console.log('measuring view');
35 | this.refs.container.measure((a, b, w, h, x, y) => {
36 | this.setState({height: new Animated.Value(h), original: h});
37 | });
38 | }
39 |
40 | hide() {
41 |
42 | if(this.state.animating) {
43 | return;
44 | }
45 | console.log('animating');
46 |
47 | this.setState({animating: true});
48 | Animated.timing(
49 | this.state.height,
50 | {toValue: 0}
51 | ).start();
52 | }
53 |
54 | show() {
55 | if(!this.state.animating) {
56 | return;
57 | }
58 | console.log('animating');
59 | this.setState({animating: false});
60 | Animated.timing(
61 | this.state.height,
62 | {toValue: this.state.original}
63 | ).start();
64 | }
65 |
66 | render() {
67 | const {height} = this.state;
68 | return (
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | )
90 |
91 | }
92 | }
93 |
94 | const styles = StyleSheet.create({
95 | container: {
96 | flexDirection: 'row',
97 | justifyContent: 'space-between',
98 | alignItems: 'center',
99 | height: 60,
100 | padding: 16,
101 | paddingTop: 24,
102 | paddingBottom: 10,
103 | backgroundColor: Colors.main,
104 | },
105 |
106 | icon: {
107 | alignItems: 'center',
108 | justifyContent: 'center'
109 | },
110 |
111 | searchBarContainer: {
112 | flex: 1,
113 | flexDirection: 'row',
114 | alignItems: 'center',
115 | height: 28,
116 | backgroundColor: Colors.searchBar,
117 | borderRadius: 5,
118 | padding: 8,
119 | marginLeft: 8,
120 | marginRight: 8,
121 | },
122 |
123 | searchIcon: {
124 | justifyContent: 'center',
125 | alignItems: 'center',
126 | },
127 |
128 | searchBar: {
129 | flex: 1,
130 | color: 'white',
131 | fontSize: 14,
132 | marginLeft: 8
133 | }
134 | })
--------------------------------------------------------------------------------
/screens/common/single-image.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 1/2/17.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | Animated,
7 | PanResponder,
8 | View,
9 | Dimensions,
10 | Image,
11 | Text,
12 | Modal,
13 | TouchableWithoutFeedback,
14 | StyleSheet
15 | } from 'react-native';
16 |
17 | import {getImage} from '../helpers';
18 | import Main from '../../main';
19 | const {width, height} = Dimensions.get('window');
20 |
21 | export default class SingleImage extends Component {
22 | state = {
23 | pan: new Animated.ValueXY(),
24 | open: false,
25 |
26 | };
27 |
28 | py = 0;
29 |
30 |
31 | componentWillMount() {
32 | let panMove = Animated.event([
33 | null, {dx: this.state.pan.x, dy: this.state.pan.y},
34 | ]);
35 |
36 | this._panResponder = PanResponder.create({
37 | onMoveShouldSetResponderCapture: () => true,
38 | onMoveShouldSetPanResponderCapture: () => true,
39 | onPanResponderGrant: (e, gestureState) => {
40 |
41 | },
42 | onPanResponderMove: (e, g) => {
43 | if(!this.open) return;
44 | return panMove(e, g);
45 | },
46 | onPanResponderRelease: () => {
47 | // this.state.pan.setOffset({x: 0, y: 0});
48 | }
49 | });
50 | }
51 |
52 | openImage() {
53 | //calculate center of image
54 |
55 | this.refs.view.measure((a, b, w, h, px, py) => {
56 | console.log('offset:', py, 'height:', height);
57 | const toY = height/2 - (py + 100);
58 | this.py = py;
59 |
60 | this.setState({open: true});
61 |
62 | });
63 |
64 |
65 | }
66 |
67 | closeImage() {
68 |
69 | }
70 |
71 | toggleLike() {
72 |
73 | }
74 |
75 | getStyle() {
76 | return [
77 | {
78 | flex: 1,
79 | transform: [
80 | {
81 | translateX: this.state.pan.x
82 | },
83 | {
84 | translateY: this.state.pan.y
85 | },
86 | ]
87 | },
88 | ];
89 | }
90 |
91 | renderModal() {
92 | const {image} = this.props;
93 | const {open} = this.state;
94 | return (
95 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | )
106 |
107 | }
108 |
109 | render() {
110 | const {image} = this.props;
111 | const {open} = this.state;
112 | if(!open) {
113 | return (
114 |
115 |
116 |
117 |
118 |
119 | )
120 | }
121 | return this.renderModal();
122 | }
123 | }
--------------------------------------------------------------------------------
/screens/helpers/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/17/16.
3 | */
4 | const profile = [
5 | {
6 | source: require('../img/bob.png'),
7 | name: 'Bob the Builder',
8 | online: false,
9 | },
10 | {
11 | source: require('../img/cookiemonster.jpeg'),
12 | name: 'Cookie Monster',
13 | online: true,
14 | },
15 | {
16 | source: require('../img/elmo.jpg'),
17 | name: 'Elmo',
18 | online: false,
19 | }
20 | ];
21 |
22 | const images = {
23 | '1': require('../img/1.jpg'),
24 | '2': require('../img/2.jpg'),
25 | '3': require('../img/3.jpg'),
26 | '4': require('../img/4.jpg'),
27 | '5': require('../img/5.jpg')
28 | };
29 |
30 | export function randomProfile() {
31 | var random = Math.floor((Math.random() * profile.length));
32 |
33 | return profile[random];
34 | }
35 |
36 | export function getImage(index) {
37 | return images[index];
38 | }
--------------------------------------------------------------------------------
/screens/imageScreen.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 1/1/17.
3 | */
4 | import React, {Component} from 'react';
5 | import {
6 | View,
7 | Text,
8 | StyleSheet
9 | } from 'react-native';
10 |
11 |
12 | export default class ImageScreen extends Component {
13 |
14 | render() {
15 | console.log(this.props);
16 | return (
17 |
18 | Helloooo
19 |
20 | )
21 | }
22 | }
--------------------------------------------------------------------------------
/screens/img/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/screens/img/1.jpg
--------------------------------------------------------------------------------
/screens/img/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/screens/img/2.jpg
--------------------------------------------------------------------------------
/screens/img/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/screens/img/3.jpg
--------------------------------------------------------------------------------
/screens/img/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/screens/img/4.jpg
--------------------------------------------------------------------------------
/screens/img/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/screens/img/5.jpg
--------------------------------------------------------------------------------
/screens/img/bob.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/screens/img/bob.png
--------------------------------------------------------------------------------
/screens/img/cookiemonster.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/screens/img/cookiemonster.jpeg
--------------------------------------------------------------------------------
/screens/img/elmo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/screens/img/elmo.jpg
--------------------------------------------------------------------------------
/screens/img/me.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ggomaeng/react-native-facebook-ui/0971331b329be32fe1e8ea8c0913a595db8784b6/screens/img/me.png
--------------------------------------------------------------------------------
/utilities/__tests__/cacheAssetsAsync-test.js:
--------------------------------------------------------------------------------
1 | describe('example test', () => {
2 | it('works', () => {
3 | expect(1).toBe(1);
4 | });
5 | });
6 |
--------------------------------------------------------------------------------
/utilities/cacheAssetsAsync.js:
--------------------------------------------------------------------------------
1 | import {
2 | Image,
3 | } from 'react-native';
4 | import {
5 | Asset,
6 | Font,
7 | } from 'exponent';
8 |
9 | export default function cacheAssetsAsync({images = [], fonts = []}) {
10 | return Promise.all([
11 | ...cacheImages(images),
12 | ...cacheFonts(fonts),
13 | ]);
14 | }
15 |
16 | function cacheImages(images) {
17 | return images.map(image => {
18 | if (typeof image === 'string') {
19 | return Image.prefetch(image);
20 | } else {
21 | return Asset.fromModule(image).downloadAsync();
22 | }
23 | });
24 | }
25 |
26 | function cacheFonts(fonts) {
27 | return fonts.map(font => Font.loadAsync(font));
28 | }
29 |
--------------------------------------------------------------------------------
/utilities/cacheHelpers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ggoma on 12/17/16.
3 | */
4 | import {
5 | Image,
6 | } from 'react-native';
7 | import {
8 | Asset,
9 | Font,
10 | } from 'exponent';
11 |
12 | export default function cacheAssetsAsync({images = [], fonts = []}) {
13 | return Promise.all([
14 | ...cacheImages(images),
15 | ...cacheFonts(fonts),
16 | ]);
17 | }
18 |
19 | function cacheImages(images) {
20 | return images.map(image => {
21 | if (typeof image === 'string') {
22 | return Image.prefetch(image);
23 | } else {
24 | return Asset.fromModule(image).downloadAsync();
25 | }
26 | });
27 | }
28 |
29 | function cacheFonts(fonts) {
30 | return fonts.map(font => Font.loadAsync(font));
31 | }
--------------------------------------------------------------------------------