├── .expo-shared └── assets.json ├── .gitignore ├── App.js ├── README.md ├── app.json ├── assets ├── background.jpg ├── background.png ├── category │ ├── dangdut.jpg │ ├── edm.jpg │ ├── indie.png │ ├── jazz.png │ └── pop.png ├── favicon.png ├── forgetpassword.png ├── icon.png ├── login.png ├── onboarding1.png ├── onboarding2.png ├── onboardingBackground.png ├── register.png └── splash.png ├── babel.config.js ├── components ├── card │ ├── CategoryCard.js │ ├── ExploreCard.js │ ├── FestivalCard.js │ ├── MyTicketsCard.js │ ├── PerformerCard.js │ ├── PreferenceCard.js │ └── TicketCard.js ├── global │ └── Background.js ├── navigation │ ├── SearchTopNav.js │ ├── SearchWIthBackTopNav.js │ ├── TopNav.js │ ├── TopNavWithBack.js │ └── TopNavWithDelete.js └── utils │ ├── StyledText.js │ └── TabBarIcon.js ├── constants └── Colors.js ├── debug.log ├── navigation └── AppNavigator.js ├── package-lock.json ├── package.json ├── rm ├── design.png ├── preview.gif ├── teams.png └── thumbnail.png └── screens ├── auth ├── ForgetPassword.js ├── Login.js ├── OnBoarding.js └── Register.js ├── main ├── Category.js ├── Detail.js ├── Explore.js ├── Home.js ├── MyTickets.js ├── Payment.js ├── PaymentGateway.js ├── Profile.js ├── Search.js └── TicketDetail.js └── onBoarding ├── Loading.js ├── PhoneNumber.js └── Preference.js /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { AppLoading } from 'expo'; 3 | import { Asset } from 'expo-asset'; 4 | import * as Font from 'expo-font'; 5 | import { StyleSheet } from 'react-native'; 6 | import { 7 | Ubuntu_300Light, 8 | Ubuntu_300Light_Italic, 9 | Ubuntu_400Regular, 10 | Ubuntu_400Regular_Italic, 11 | Ubuntu_500Medium, 12 | Ubuntu_500Medium_Italic, 13 | Ubuntu_700Bold, 14 | Ubuntu_700Bold_Italic, 15 | } from '@expo-google-fonts/ubuntu'; 16 | import AppNavigator from './navigation/AppNavigator'; 17 | import Colors from './constants/Colors'; 18 | import { 19 | ApplicationProvider, 20 | IconRegistry, 21 | Layout, 22 | Text, 23 | } from '@ui-kitten/components'; 24 | import { SafeAreaView } from 'react-native-safe-area-context'; 25 | import { EvaIconsPack } from '@ui-kitten/eva-icons'; 26 | import { mapping, light as lightTheme } from '@eva-design/eva'; 27 | import { StatusBar } from 'expo-status-bar'; 28 | import moment from 'moment'; 29 | import 'moment/locale/id'; 30 | export default function App(props) { 31 | const [isLoadingComplete, setLoadingComplete] = useState(false); 32 | 33 | if (!isLoadingComplete && !props.skipLoadingScreen) { 34 | return ( 35 | handleFinishLoading(setLoadingComplete)} 39 | /> 40 | ); 41 | } else { 42 | return ( 43 | <> 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ); 54 | } 55 | } 56 | 57 | async function loadResourcesAsync() { 58 | await Promise.all([ 59 | Asset.loadAsync([ 60 | require('./assets/icon.png'), 61 | require('./assets/splash.png'), 62 | require('./assets/background.png'), 63 | require('./assets/onboardingBackground.png'), 64 | require('./assets/background.png'), 65 | require('./assets/category/indie.png'), 66 | require('./assets/category/jazz.png'), 67 | require('./assets/category/pop.png'), 68 | require('./assets/category/dangdut.jpg'), 69 | require('./assets/category/edm.jpg'), 70 | ]), 71 | Font.loadAsync({ 72 | Ubuntu_300Light, 73 | Ubuntu_300Light_Italic, 74 | Ubuntu_400Regular, 75 | Ubuntu_400Regular_Italic, 76 | Ubuntu_500Medium, 77 | Ubuntu_500Medium_Italic, 78 | Ubuntu_700Bold, 79 | Ubuntu_700Bold_Italic, 80 | }), 81 | ]); 82 | } 83 | 84 | function handleLoadingError(error) { 85 | // In this case, you might want to report the error to your error reporting 86 | // service, for example Sentry 87 | console.warn(error); 88 | } 89 | 90 | function handleFinishLoading(setLoadingComplete) { 91 | setLoadingComplete(true); 92 | } 93 | const styles = StyleSheet.create({ 94 | container: { 95 | flex: 1, 96 | }, 97 | }); 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tikteng 2 | 3 | ![rm/thumbnail.png](rm/thumbnail.png) 4 | 5 | BNCC Technoscape x tiket.com Hackathon submission for Maradota Team (TikTeng) 6 | 7 | 🥈 2nd place 🥳 8 | 9 | ## Preview 10 | 11 | ![rm/preview.gif](rm/preview.gif) 12 | 13 | ## Team 14 | 15 | ![rm/teams.png](rm/teams.png) 16 | 17 | ## Ideas 18 | 19 | Sebuah Platform penjualan tiket konser online yang bisa dibeli secara eceran (ketengan) 20 | 21 | ## Features 22 | 23 | - Login with facebook 24 | - Rekomendasi show yang sesuai dengan selera user 25 | - Beli tiket show online eceran (ngeteng) 26 | - Metode pembayaran yang mudah (midtrans) 27 | 28 | ## Design System 29 | 30 | ![rm/design.png](rm/design.png) 31 | 32 | ## Technology used 33 | 34 | - React native 35 | - Expo 36 | - Firebase 37 | - Midtrans 38 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "TikTeng", 4 | "slug": "tikteng", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "cover", 11 | "backgroundColor": "#16191F" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "web": { 23 | "favicon": "./assets/favicon.png" 24 | }, 25 | "description": "" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/background.jpg -------------------------------------------------------------------------------- /assets/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/background.png -------------------------------------------------------------------------------- /assets/category/dangdut.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/category/dangdut.jpg -------------------------------------------------------------------------------- /assets/category/edm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/category/edm.jpg -------------------------------------------------------------------------------- /assets/category/indie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/category/indie.png -------------------------------------------------------------------------------- /assets/category/jazz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/category/jazz.png -------------------------------------------------------------------------------- /assets/category/pop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/category/pop.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/favicon.png -------------------------------------------------------------------------------- /assets/forgetpassword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/forgetpassword.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/icon.png -------------------------------------------------------------------------------- /assets/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/login.png -------------------------------------------------------------------------------- /assets/onboarding1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/onboarding1.png -------------------------------------------------------------------------------- /assets/onboarding2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/onboarding2.png -------------------------------------------------------------------------------- /assets/onboardingBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/onboardingBackground.png -------------------------------------------------------------------------------- /assets/register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/register.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/assets/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /components/card/CategoryCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, View, Image, Dimensions } from 'react-native'; 3 | import { LinearGradient } from 'expo-linear-gradient'; 4 | import Text from '../utils/StyledText'; 5 | import Colors from '../../constants/Colors'; 6 | const { width, height } = Dimensions.get('window'); 7 | const w = width - 60; 8 | const fxw = w / 3; 9 | export default function (props) { 10 | return ( 11 | 16 | 21 | 22 | 33 | 44 | {props.title} 45 | 46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /components/card/ExploreCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | StyleSheet, 4 | View, 5 | Image, 6 | Dimensions, 7 | TouchableOpacity, 8 | } from 'react-native'; 9 | import { LinearGradient } from 'expo-linear-gradient'; 10 | import Text from '../utils/StyledText'; 11 | import Colors from '../../constants/Colors'; 12 | import moment from 'moment'; 13 | const { width, height } = Dimensions.get('window'); 14 | const w = width - 60; 15 | const fxw = w / 3; 16 | export default function (props) { 17 | const item = props.item; 18 | return ( 19 | { 25 | props.navigation.navigate('Detail', { 26 | item: item, 27 | }); 28 | }} 29 | > 30 | 37 | 46 | 53 | {item.title} 54 | 55 | 56 | {moment(item.date).format('D MMMM YYYY')} 57 | 58 | {item.ketengan && ( 59 | 71 | 72 | Bisa Ketengan 73 | 74 | 75 | )} 76 | {item.bundling && ( 77 | 88 | 89 | Gratis kuota streaming 90 | 91 | 92 | )} 93 | 94 | 95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /components/card/FestivalCard.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { 3 | StatusBar, 4 | Image, 5 | FlatList, 6 | Dimensions, 7 | Animated, 8 | View, 9 | StyleSheet, 10 | SafeAreaView, 11 | } from 'react-native'; 12 | const { width } = Dimensions.get('screen'); 13 | import { 14 | FlingGestureHandler, 15 | Directions, 16 | State, 17 | TouchableWithoutFeedback, 18 | } from 'react-native-gesture-handler'; 19 | import Text from '../../components/utils/StyledText'; 20 | import Colors from '../../constants/Colors'; 21 | // https://www.creative-flyers.com 22 | import moment from 'moment'; 23 | 24 | const OVERFLOW_HEIGHT = 95; 25 | const SPACING = 10; 26 | const ITEM_WIDTH = width - 100; 27 | const ITEM_HEIGHT = ITEM_WIDTH * 1.5; 28 | const VISIBLE_ITEMS = 3; 29 | 30 | const OverflowItems = ({ data, scrollXAnimated }) => { 31 | const inputRange = [-1, 0, 1]; 32 | const translateY = scrollXAnimated.interpolate({ 33 | inputRange, 34 | outputRange: [OVERFLOW_HEIGHT, 0, -OVERFLOW_HEIGHT], 35 | }); 36 | return ( 37 | 48 | 49 | {data.map((item, index) => { 50 | return ( 51 | 59 | {item.bundling && ( 60 | 78 | Gratis kuota streaming 79 | 80 | )} 81 | 82 | 83 | {item.title} 84 | 85 | 86 | 93 | 94 | {moment(item.date).format('D MMMM YYYY')} 95 | 96 | 97 | {item.ketengan && ( 98 | 109 | 110 | Bisa Ketengan 111 | 112 | 113 | )} 114 | 115 | 116 | ); 117 | })} 118 | 119 | 120 | ); 121 | }; 122 | 123 | export default function App(props) { 124 | const [data, setData] = React.useState(props.data); 125 | const scrollXIndex = React.useRef(new Animated.Value(0)).current; 126 | const scrollXAnimated = React.useRef(new Animated.Value(0)).current; 127 | const [index, setIndex] = React.useState(0); 128 | const setActiveIndex = React.useCallback((activeIndex) => { 129 | scrollXIndex.setValue(activeIndex); 130 | setIndex(activeIndex); 131 | }); 132 | 133 | React.useEffect(() => { 134 | if (index === data.length - VISIBLE_ITEMS - 1) { 135 | // get new data 136 | // fetch more data 137 | const newData = [...data, ...data]; 138 | setData(newData); 139 | } 140 | }); 141 | 142 | React.useEffect(() => { 143 | Animated.spring(scrollXAnimated, { 144 | toValue: scrollXIndex, 145 | duration: 100, 146 | useNativeDriver: true, 147 | }).start(); 148 | }); 149 | 150 | return ( 151 | <> 152 | { 156 | if (ev.nativeEvent.state === State.END) { 157 | if (index === data.length - 1) { 158 | return; 159 | } 160 | setActiveIndex(index + 1); 161 | } 162 | }} 163 | > 164 | { 168 | if (ev.nativeEvent.state === State.END) { 169 | if (index === 0) { 170 | return; 171 | } 172 | setActiveIndex(index - 1); 173 | } 174 | }} 175 | > 176 | 177 | 178 | 179 | String(index)} 182 | horizontal 183 | inverted 184 | contentContainerStyle={{ 185 | flex: 1, 186 | justifyContent: 'center', 187 | padding: 20, 188 | }} 189 | scrollEnabled={false} 190 | removeClippedSubviews={false} 191 | CellRendererComponent={({ 192 | item, 193 | index, 194 | children, 195 | style, 196 | ...props 197 | }) => { 198 | const newStyle = [style, { zIndex: data.length - index }]; 199 | return ( 200 | 201 | {children} 202 | 203 | ); 204 | }} 205 | renderItem={({ item, index }) => { 206 | const inputRange = [index - 1, index, index + 1]; 207 | const translateX = scrollXAnimated.interpolate({ 208 | inputRange, 209 | outputRange: [50, 0, -100], 210 | }); 211 | const scale = scrollXAnimated.interpolate({ 212 | inputRange, 213 | outputRange: [0.8, 1, 1.3], 214 | }); 215 | const opacity = scrollXAnimated.interpolate({ 216 | inputRange, 217 | outputRange: [1 - 1 / VISIBLE_ITEMS, 1, 0], 218 | }); 219 | 220 | return ( 221 | 234 | { 236 | props.navigation.navigate('Detail', { 237 | item: item, 238 | }); 239 | }} 240 | > 241 | 249 | 250 | 251 | ); 252 | }} 253 | /> 254 | 255 | 256 | 257 | 258 | ); 259 | } 260 | 261 | const styles = StyleSheet.create({ 262 | container: { 263 | flex: 1, 264 | justifyContent: 'center', 265 | }, 266 | title: { 267 | fontSize: 28, 268 | fontWeight: '900', 269 | textTransform: 'uppercase', 270 | letterSpacing: -1, 271 | }, 272 | location: { 273 | fontSize: 16, 274 | }, 275 | date: { 276 | fontSize: 12, 277 | }, 278 | itemContainer: { 279 | height: OVERFLOW_HEIGHT, 280 | padding: SPACING * 2, 281 | }, 282 | itemContainerRow: { 283 | flexDirection: 'row', 284 | justifyContent: 'space-between', 285 | alignItems: 'center', 286 | }, 287 | overflowContainer: { 288 | height: OVERFLOW_HEIGHT, 289 | overflow: 'hidden', 290 | }, 291 | }); 292 | -------------------------------------------------------------------------------- /components/card/MyTicketsCard.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { View, Dimensions, TouchableOpacity } from 'react-native'; 3 | import Text from '../utils/StyledText'; 4 | import moment from 'moment'; 5 | import * as firebase from 'firebase'; 6 | var numeral = require('numeral'); 7 | export default function (props) { 8 | const [show, setShow] = useState(''); 9 | const item = props.item; 10 | const ticket = item.ticket; 11 | useEffect(() => { 12 | getShowDetail(); 13 | }, []); 14 | function getShowDetail() { 15 | const ref = firebase.database().ref('shows/' + item.ticket.showUid); 16 | ref.once('value', function (snapshot) { 17 | const data = snapshot.val(); 18 | if (data !== null) { 19 | setShow(data); 20 | } else { 21 | setShow(null); 22 | } 23 | }); 24 | } 25 | return ( 26 | { 28 | props.navigation.navigate(item.paid ? 'TicketDetail' : 'Payment', { 29 | item: item, 30 | }); 31 | }} 32 | disabled={props.disabled} 33 | > 34 | 43 | 57 | 58 | {show.title} 59 | 60 | 61 | {moment(show.date).format('D MMM YYYY')} 62 | 63 | 64 | 73 | 78 | 79 | {ticket.performer} 80 | 81 | 82 | {moment(ticket.date + ' ' + ticket.timeStart).format('HH:mm')} -{' '} 83 | {moment(ticket.date + ' ' + ticket.timeEnd).format('HH:mm')} 84 | 85 | 86 | 87 | {numeral(ticket.price).format('0,0')} 88 | 89 | 90 | 91 | 92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /components/card/PerformerCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Dimensions } from 'react-native'; 3 | import Text from '../utils/StyledText'; 4 | import moment from 'moment'; 5 | export default function (props) { 6 | const item = props.item; 7 | return ( 8 | 20 | 27 | {item.performer} 28 | 29 | 36 | {moment(item.date + ' ' + item.timeStart).format('HH:mm')} -{' '} 37 | {moment(item.date + ' ' + item.timeEnd).format('HH:mm')} 38 | 39 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /components/card/PreferenceCard.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { StyleSheet, View, TouchableWithoutFeedback } from 'react-native'; 3 | import Text from '../../components/utils/StyledText'; 4 | import Colors from '../../constants/Colors'; 5 | 6 | export default function App(props) { 7 | const [choosen, setChoosen] = useState(false); 8 | const handleCategory = (name, x) => { 9 | props.press(name, x); 10 | }; 11 | return ( 12 | { 14 | const x = !choosen; 15 | setChoosen(x); 16 | handleCategory(props.name, x); 17 | }} 18 | > 19 | 28 | 35 | {props.title} 36 | 37 | 38 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /components/card/TicketCard.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View, Dimensions, TouchableOpacity } from 'react-native'; 3 | import Text from '../utils/StyledText'; 4 | import moment from 'moment'; 5 | var numeral = require('numeral'); 6 | import * as firebase from 'firebase'; 7 | import { StackActions } from '@react-navigation/native'; 8 | export default function (props) { 9 | const [loading, setLoading] = useState(false); 10 | const item = props.item; 11 | const clickTicket = (dataTicket) => { 12 | const ref = firebase.database().ref('invoice'); 13 | const u = firebase.auth().currentUser; 14 | const userRef = firebase.database().ref('users/' + u.uid); 15 | userRef.once('value', function (snapshot) { 16 | var key = ref.push().key; 17 | const ticket = { 18 | ticket: dataTicket, 19 | uid: key, 20 | timeStamp: moment().format(), 21 | paid: false, 22 | user: { 23 | uid: snapshot.val().uid, 24 | email: snapshot.val().email, 25 | name: snapshot.val().name, 26 | phoneNumber: snapshot.val().phoneNumber, 27 | }, 28 | userUid: snapshot.val().uid, 29 | }; 30 | ref 31 | .child(key) 32 | .update(ticket) 33 | .then(function () { 34 | props.navigation.dispatch( 35 | StackActions.replace('Payment', { 36 | item: ticket, 37 | }) 38 | ); 39 | }) 40 | .catch(function (error) { 41 | alert(error); 42 | }); 43 | }); 44 | }; 45 | return ( 46 | { 55 | clickTicket(item); 56 | }} 57 | disabled={props.disabled} 58 | > 59 | 70 | 78 | 85 | 86 | {item.performer} 87 | 88 | 89 | {moment(item.date + ' ' + item.timeStart).format('HH:mm')} -{' '} 90 | {moment(item.date + ' ' + item.timeEnd).format('HH:mm')} 91 | 92 | 93 | 94 | 95 | {numeral(item.price).format('0,0')} 96 | 97 | 98 | 99 | ); 100 | } 101 | -------------------------------------------------------------------------------- /components/global/Background.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, View, ImageBackground } from 'react-native'; 3 | import { StatusBar } from 'expo-status-bar'; 4 | export default function (props) { 5 | return ( 6 | 7 | 8 | 12 | {props.children} 13 | 14 | 15 | ); 16 | } 17 | 18 | const styles = StyleSheet.create({ 19 | container: { 20 | flex: 1, 21 | flexDirection: 'column', 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /components/navigation/SearchTopNav.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { 3 | View, 4 | StyleSheet, 5 | ScrollView, 6 | Image, 7 | Dimensions, 8 | TouchableOpacity, 9 | TextInput, 10 | } from 'react-native'; 11 | import { Ionicons } from '@expo/vector-icons'; 12 | import Text from '../utils/StyledText'; 13 | 14 | const { width, height } = Dimensions.get('window'); 15 | const category = 0.034 * width; 16 | export default function TopNav(props) { 17 | const [search, setSearch] = useState(''); 18 | return ( 19 | 20 | 30 | 31 | 40 | { 51 | setSearch(text); 52 | }} 53 | onSubmitEditing={() => { 54 | props.navigation.navigate('Search', { 55 | search: search, 56 | }); 57 | }} 58 | /> 59 | 60 | 67 | 68 | 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /components/navigation/SearchWIthBackTopNav.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { 3 | View, 4 | StyleSheet, 5 | ScrollView, 6 | Image, 7 | Dimensions, 8 | TouchableOpacity, 9 | TextInput, 10 | } from 'react-native'; 11 | import { Ionicons } from '@expo/vector-icons'; 12 | import Text from '../utils/StyledText'; 13 | import { Icon } from '@ui-kitten/components'; 14 | 15 | const { width, height } = Dimensions.get('window'); 16 | const category = 0.034 * width; 17 | export default function TopNav(props) { 18 | const [search, setSearch] = useState(''); 19 | return ( 20 | 21 | 31 | { 33 | props.navigation.goBack(); 34 | }} 35 | style={{ flex: 1, alignItems: 'flex-start' }} 36 | > 37 | 38 | 39 | 48 | { 59 | setSearch(text); 60 | }} 61 | onSubmitEditing={() => { 62 | props.navigation.navigate('Search', { 63 | search: search, 64 | }); 65 | }} 66 | /> 67 | 68 | 75 | 76 | 77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /components/navigation/TopNav.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { 3 | View, 4 | StyleSheet, 5 | ScrollView, 6 | Image, 7 | Dimensions, 8 | TouchableOpacity, 9 | TextInput, 10 | } from 'react-native'; 11 | import { Ionicons } from '@expo/vector-icons'; 12 | import Text from '../utils/StyledText'; 13 | 14 | const { width, height } = Dimensions.get('window'); 15 | const category = 0.034 * width; 16 | export default function TopNav(props) { 17 | return ( 18 | 19 | 29 | 30 | 39 | 18 ? 18 : 0.0437 * width, 44 | }} 45 | > 46 | {props.title} 47 | 48 | 49 | 56 | 57 | 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /components/navigation/TopNavWithBack.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { 3 | View, 4 | StyleSheet, 5 | ScrollView, 6 | Image, 7 | Dimensions, 8 | TouchableOpacity, 9 | TextInput, 10 | } from 'react-native'; 11 | import { Ionicons } from '@expo/vector-icons'; 12 | import Text from '../utils/StyledText'; 13 | import { Icon } from '@ui-kitten/components'; 14 | 15 | const { width, height } = Dimensions.get('window'); 16 | const category = 0.034 * width; 17 | export default function TopNav(props) { 18 | return ( 19 | 20 | 30 | { 32 | props.navigation.goBack(); 33 | }} 34 | style={{ flex: 1, alignItems: 'flex-start' }} 35 | > 36 | 37 | 38 | 39 | 48 | 18 ? 18 : 0.0437 * width, 53 | }} 54 | > 55 | {props.title} 56 | 57 | 58 | 65 | 66 | 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /components/navigation/TopNavWithDelete.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { 3 | View, 4 | StyleSheet, 5 | ScrollView, 6 | Image, 7 | Dimensions, 8 | TouchableOpacity, 9 | TextInput, 10 | } from 'react-native'; 11 | import { Ionicons } from '@expo/vector-icons'; 12 | import Text from '../utils/StyledText'; 13 | import { Icon } from '@ui-kitten/components'; 14 | 15 | const { width, height } = Dimensions.get('window'); 16 | const category = 0.034 * width; 17 | export default function TopNav(props) { 18 | return ( 19 | 20 | 30 | { 32 | props.navigation.goBack(); 33 | }} 34 | style={{ flex: 1, alignItems: 'flex-start' }} 35 | > 36 | 37 | 38 | 39 | 48 | 18 ? 18 : 0.0437 * width, 53 | }} 54 | > 55 | {props.title} 56 | 57 | 58 | { 60 | props.onPress; 61 | }} 62 | style={{ flex: 1, alignItems: 'flex-end', justifyContent: 'center' }} 63 | > 64 | 70 | 71 | 72 | 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /components/utils/StyledText.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Text } from 'react-native'; 3 | 4 | export default function (props) { 5 | return ( 6 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /components/utils/TabBarIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Icon } from '@ui-kitten/components'; 3 | export default (props) => { 4 | return ( 5 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /constants/Colors.js: -------------------------------------------------------------------------------- 1 | const background = '#F7F7F7'; 2 | const primary = '#E94560'; 3 | const black = '#2F2E41'; 4 | const black2 = '#46455B'; 5 | const gray = '#C0C0C0'; 6 | 7 | export default { 8 | background, 9 | primary, 10 | black, 11 | black2, 12 | gray, 13 | }; 14 | -------------------------------------------------------------------------------- /debug.log: -------------------------------------------------------------------------------- 1 | [1012/014819.133:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) 2 | -------------------------------------------------------------------------------- /navigation/AppNavigator.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { View } from 'react-native'; 3 | import { NavigationContainer } from '@react-navigation/native'; 4 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; 5 | import { createStackNavigator } from '@react-navigation/stack'; 6 | import * as firebase from 'firebase'; 7 | import { MaterialCommunityIcons } from '@expo/vector-icons'; 8 | 9 | import TabBarIcon from '../components/utils/TabBarIcon'; 10 | import Text from '../components/utils/StyledText'; 11 | 12 | import OnBoarding from '../screens/auth/OnBoarding'; 13 | import Login from '../screens/auth/Login'; 14 | import Register from '../screens/auth/Register'; 15 | import ForgetPassword from '../screens/auth/ForgetPassword'; 16 | 17 | import Home from '../screens/main/Home'; 18 | import Profile from '../screens/main/Profile'; 19 | import Explore from '../screens/main/Explore'; 20 | import MyTickets from '../screens/main/MyTickets'; 21 | 22 | import Detail from '../screens/main/Detail'; 23 | import Payment from '../screens/main/Payment'; 24 | import TicketDetail from '../screens/main/TicketDetail'; 25 | import Search from '../screens/main/Search'; 26 | import Category from '../screens/main/Category'; 27 | import PaymentGateway from '../screens/main/PaymentGateway'; 28 | 29 | import Preference from '../screens/onBoarding/Preference'; 30 | import PhoneNumber from '../screens/onBoarding/PhoneNumber'; 31 | 32 | import Loading from '../screens/onBoarding/Loading'; 33 | 34 | const firebaseConfig = { 35 | apiKey: 'AIzaSyCtz7aCg-oRtRPinPIJZ0fjH2Wi8BP77Xs', 36 | authDomain: 'tikteng-d095d.firebaseapp.com', 37 | databaseURL: 'https://tikteng-d095d.firebaseio.com', 38 | projectId: 'tikteng-d095d', 39 | storageBucket: 'tikteng-d095d.appspot.com', 40 | messagingSenderId: '25093089371', 41 | appId: '1:25093089371:web:79256347f2beb2bf1cf77e', 42 | }; 43 | if (firebase.apps.length === 0) { 44 | firebase.initializeApp(firebaseConfig); 45 | } 46 | console.disableYellowBox = true; 47 | const WelcomeStack = createStackNavigator(); 48 | 49 | const Welcome = () => { 50 | return ( 51 | 57 | 58 | 59 | 60 | 61 | 62 | ); 63 | }; 64 | 65 | const Tab = createBottomTabNavigator(); 66 | 67 | const MainTabs = () => { 68 | return ( 69 | 78 | ( 83 | 92 | Home 93 | 94 | ), 95 | tabBarIcon: ({ focused }) => ( 96 | 97 | ), 98 | }} 99 | /> 100 | ( 105 | 114 | Explore 115 | 116 | ), 117 | tabBarIcon: ({ focused }) => ( 118 | 119 | ), 120 | }} 121 | /> 122 | ( 127 | 136 | My Tickets 137 | 138 | ), 139 | tabBarIcon: ({ focused }) => ( 140 | 146 | ), 147 | }} 148 | /> 149 | ( 154 | 163 | Profile 164 | 165 | ), 166 | tabBarIcon: ({ focused }) => ( 167 | 168 | ), 169 | }} 170 | /> 171 | 172 | ); 173 | }; 174 | 175 | const MainStack = createStackNavigator(); 176 | const Main = () => { 177 | return ( 178 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | ); 193 | }; 194 | 195 | const onBoardingStack = createStackNavigator(); 196 | const OB = () => { 197 | return ( 198 | 204 | 205 | 206 | 207 | ); 208 | }; 209 | export default () => { 210 | const [user, setUser] = useState(null); 211 | const [phone, setPhone] = useState(null); 212 | 213 | firebase.auth().onAuthStateChanged(function (u) { 214 | if (u) { 215 | setUser(true); 216 | firebase 217 | .database() 218 | .ref('users/' + u.uid + '/phoneNumber') 219 | .on('value', function (snapshot) { 220 | setPhone(snapshot.val() ? true : false); 221 | }); 222 | } else { 223 | setUser(false); 224 | setPhone(false); 225 | } 226 | }); 227 | 228 | return ( 229 | 230 | {user == null ? ( 231 | 232 | ) : phone == null ? ( 233 | 234 | ) : user ? ( 235 | phone ? ( 236 |
237 | ) : ( 238 | 239 | ) 240 | ) : ( 241 | 242 | )} 243 | 244 | ); 245 | }; 246 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@eva-design/eva": "^2.0.0", 12 | "@expo-google-fonts/ubuntu": "^0.1.0", 13 | "@react-native-community/masked-view": "0.1.10", 14 | "@react-navigation/bottom-tabs": "^5.9.2", 15 | "@react-navigation/native": "^5.7.6", 16 | "@react-navigation/stack": "^5.9.3", 17 | "@ui-kitten/components": "^5.0.0", 18 | "@ui-kitten/eva-icons": "^5.0.0", 19 | "base-64": "^0.1.0", 20 | "expo": "~39.0.2", 21 | "expo-facebook": "~9.0.0", 22 | "expo-linear-gradient": "~8.3.0", 23 | "expo-status-bar": "~1.0.2", 24 | "firebase": "7.9.0", 25 | "lodash": "^4.17.20", 26 | "moment": "^2.29.1", 27 | "numeral": "^2.0.6", 28 | "react": "16.13.1", 29 | "react-dom": "16.13.1", 30 | "react-native": "https://github.com/expo/react-native/archive/sdk-39.0.3.tar.gz", 31 | "react-native-gesture-handler": "~1.7.0", 32 | "react-native-onboarding-swiper": "^1.1.4", 33 | "react-native-reanimated": "~1.13.0", 34 | "react-native-safe-area-context": "3.1.4", 35 | "react-native-screens": "~2.10.1", 36 | "react-native-svg": "^12.1.0", 37 | "react-native-web": "~0.13.12", 38 | "react-native-webview": "10.7.0", 39 | "expo-web-browser": "~8.5.0" 40 | }, 41 | "devDependencies": { 42 | "@babel/core": "~7.9.0" 43 | }, 44 | "private": true 45 | } 46 | -------------------------------------------------------------------------------- /rm/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/rm/design.png -------------------------------------------------------------------------------- /rm/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/rm/preview.gif -------------------------------------------------------------------------------- /rm/teams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/rm/teams.png -------------------------------------------------------------------------------- /rm/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingki/TikTeng/0972f5dd55c71fe8478a164610900a6c0d03727d/rm/thumbnail.png -------------------------------------------------------------------------------- /screens/auth/ForgetPassword.js: -------------------------------------------------------------------------------- 1 | import { StatusBar } from 'expo-status-bar'; 2 | import React, { useState } from 'react'; 3 | import { 4 | StyleSheet, 5 | View, 6 | Image, 7 | TextInput, 8 | TouchableOpacity, 9 | ScrollView, 10 | KeyboardAvoidingView, 11 | ActivityIndicator, 12 | } from 'react-native'; 13 | import Text from '../../components/utils/StyledText'; 14 | import Colors from '../../constants/Colors'; 15 | import * as firebase from 'firebase'; 16 | import Background from '../../components/global/Background'; 17 | export default function ({ navigation }) { 18 | const [email, setEmail] = useState(''); 19 | 20 | const [loading, setLoading] = useState(false); 21 | async function forgetPassword() { 22 | setLoading(true); 23 | await firebase 24 | .auth() 25 | .sendPasswordResetEmail(email) 26 | .then(function () { 27 | setLoading(false); 28 | navigation.navigate('Login'); 29 | alert('Password reset sudah dikirim ke email anda'); 30 | }) 31 | .catch(function (error) { 32 | setLoading(false); 33 | alert(error); 34 | }); 35 | } 36 | return ( 37 | 38 | 39 | 40 | 47 | 48 | 55 | 64 | Lupa Password 65 | 66 | 67 | Email 68 | 69 | 77 | setEmail(text)} 89 | /> 90 | 91 | 92 | { 94 | forgetPassword(); 95 | }} 96 | disabled={loading} 97 | > 98 | 103 | 114 | {loading ? ( 115 | 116 | ) : ( 117 | 118 | Ganti Password 119 | 120 | )} 121 | 122 | 123 | 124 | 132 | 138 | Sudah punya akun? 139 | 140 | { 142 | navigation.navigate('Login'); 143 | }} 144 | > 145 | 153 | Masuk disini 154 | 155 | 156 | 157 | 165 | { 167 | navigation.navigate('ForgetPassword'); 168 | }} 169 | > 170 | 178 | Lupa Password 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | ); 187 | } 188 | 189 | const styles = StyleSheet.create({ 190 | container: { 191 | flex: 1, 192 | backgroundColor: '#F7f7f7', 193 | }, 194 | }); 195 | -------------------------------------------------------------------------------- /screens/auth/Login.js: -------------------------------------------------------------------------------- 1 | import { StatusBar } from 'expo-status-bar'; 2 | import React, { useState } from 'react'; 3 | import { 4 | StyleSheet, 5 | View, 6 | Image, 7 | TextInput, 8 | TouchableOpacity, 9 | ScrollView, 10 | KeyboardAvoidingView, 11 | ActivityIndicator, 12 | } from 'react-native'; 13 | import * as firebase from 'firebase'; 14 | 15 | import Text from '../../components/utils/StyledText'; 16 | import Colors from '../../constants/Colors'; 17 | import Background from '../../components/global/Background'; 18 | import * as Facebook from 'expo-facebook'; 19 | 20 | export default function ({ navigation }) { 21 | const [email, setEmail] = useState(''); 22 | const [password, setPassword] = useState(''); 23 | const [loading, setLoading] = useState(false); 24 | 25 | async function login() { 26 | setLoading(true); 27 | await firebase 28 | .auth() 29 | .signInWithEmailAndPassword(email, password) 30 | .catch(function (error) { 31 | // Handle Errors here. 32 | var errorCode = error.code; 33 | var errorMessage = error.message; 34 | // ... 35 | setLoading(false); 36 | alert(errorMessage); 37 | }); 38 | } 39 | async function loginWithFacebook() { 40 | const appId = '697232937815789'; 41 | const permissions = ['public_profile', 'email']; 42 | await Facebook.initializeAsync(appId); 43 | const { type, token } = await Facebook.logInWithReadPermissionsAsync({ 44 | permissions, 45 | }); 46 | 47 | if (type == 'success') { 48 | await firebase 49 | .auth() 50 | .setPersistence(firebase.auth.Auth.Persistence.LOCAL); 51 | const credential = firebase.auth.FacebookAuthProvider.credential(token); 52 | const facebookProfileData = await firebase 53 | .auth() 54 | .signInWithCredential(credential); // Sign in with Facebook credential 55 | } 56 | } 57 | 58 | return ( 59 | 60 | 61 | 62 | 69 | 70 | 77 | 86 | Masuk 87 | 88 | 89 | Email 90 | 91 | 99 | setEmail(text)} 111 | /> 112 | 113 | 114 | Password 115 | 116 | 124 | setPassword(text)} 136 | /> 137 | 138 | { 140 | login(); 141 | }} 142 | disabled={loading} 143 | > 144 | 149 | 160 | {loading ? ( 161 | 162 | ) : ( 163 | 164 | Lanjutkan 165 | 166 | )} 167 | 168 | 169 | 170 | { 172 | loginWithFacebook(); 173 | }} 174 | disabled={loading} 175 | style={{ 176 | marginTop: 20, 177 | }} 178 | > 179 | 184 | 195 | {loading ? ( 196 | 197 | ) : ( 198 | 199 | Continue with Facebook 200 | 201 | )} 202 | 203 | 204 | 205 | 213 | 219 | Belum punya akun? 220 | 221 | { 223 | navigation.navigate('Register'); 224 | }} 225 | > 226 | 234 | Daftar disini 235 | 236 | 237 | 238 | 246 | { 248 | navigation.navigate('ForgetPassword'); 249 | }} 250 | > 251 | 259 | Lupa Password 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | ); 268 | } 269 | 270 | const styles = StyleSheet.create({ 271 | container: { 272 | flex: 1, 273 | backgroundColor: '#F7f7f7', 274 | }, 275 | }); 276 | -------------------------------------------------------------------------------- /screens/auth/OnBoarding.js: -------------------------------------------------------------------------------- 1 | import { StatusBar } from 'expo-status-bar'; 2 | import React from 'react'; 3 | import { StyleSheet, View, Image, ImageBackground } from 'react-native'; 4 | import Text from '../../components/utils/StyledText'; 5 | import Onboarding from 'react-native-onboarding-swiper'; 6 | import Colors from '../../constants/Colors'; 7 | 8 | export default function App({ navigation }) { 9 | const Next = ({ ...props }) => ( 10 | 11 | 12 | Selanjutnya 13 | 14 | 15 | ); 16 | const Done = ({ ...props }) => ( 17 | 18 | 19 | Masuk 20 | 21 | 22 | ); 23 | return ( 24 | 25 | 29 | 30 | { 33 | navigation.navigate('Login'); 34 | }} 35 | NextButtonComponent={Next} 36 | DoneButtonComponent={Done} 37 | pages={[ 38 | { 39 | image: , 40 | backgroundColor: 'rgba(0,0,0,0)', 41 | titleStyles: { 42 | fontFamily: 'Ubuntu_700Bold', 43 | color: 'white', 44 | }, 45 | subTitleStyles: { 46 | fontFamily: 'Ubuntu_400Regular', 47 | color: 'white', 48 | }, 49 | title: 'Welcome to TikTeng', 50 | subtitle: 51 | 'Platform penjualan tiket konser online yang bisa diketeng', 52 | }, 53 | { 54 | image: , 55 | backgroundColor: 'rgba(0,0,0,0)', 56 | titleStyles: { 57 | fontFamily: 'Ubuntu_700Bold', 58 | color: 'white', 59 | }, 60 | subTitleStyles: { 61 | fontFamily: 'Ubuntu_400Regular', 62 | color: 'white', 63 | }, 64 | title: 'Ketengan? Apatuh?', 65 | subtitle: 66 | 'Ketengan itu bisa memilih performer sesuai keinginanmu sendiri dengan harga yang lebih murah', 67 | }, 68 | { 69 | image: , 70 | backgroundColor: 'rgba(0,0,0,0)', 71 | titleStyles: { 72 | fontFamily: 'Ubuntu_700Bold', 73 | color: 'white', 74 | }, 75 | subTitleStyles: { 76 | fontFamily: 'Ubuntu_400Regular', 77 | color: 'white', 78 | }, 79 | title: 'Emang bisa?', 80 | subtitle: 81 | 'Bisa dong, kan udah ada TikTeng. Nonton performer favoritmu tanpa ribet', 82 | }, 83 | ]} 84 | /> 85 | 86 | 87 | ); 88 | } 89 | 90 | const styles = StyleSheet.create({ 91 | container: { 92 | flex: 1, 93 | }, 94 | }); 95 | -------------------------------------------------------------------------------- /screens/auth/Register.js: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from 'expo'; 2 | import { StatusBar } from 'expo-status-bar'; 3 | import React, { useState } from 'react'; 4 | import { 5 | StyleSheet, 6 | View, 7 | Image, 8 | TextInput, 9 | TouchableOpacity, 10 | ScrollView, 11 | KeyboardAvoidingView, 12 | ActivityIndicator, 13 | } from 'react-native'; 14 | import Text from '../../components/utils/StyledText'; 15 | import Colors from '../../constants/Colors'; 16 | import * as firebase from 'firebase'; 17 | import Background from '../../components/global/Background'; 18 | export default function ({ navigation }) { 19 | const [name, setName] = useState(''); 20 | const [email, setEmail] = useState(''); 21 | const [password, setPassword] = useState(''); 22 | const [loading, setLoading] = useState(false); 23 | 24 | async function register() { 25 | setLoading(true); 26 | await firebase 27 | .auth() 28 | .createUserWithEmailAndPassword(email, password) 29 | .then(async (auth) => { 30 | await auth.user.updateProfile({ 31 | displayName: name, 32 | }); 33 | }) 34 | .catch(function (error) { 35 | // Handle Errors here. 36 | var errorCode = error.code; 37 | var errorMessage = error.message; 38 | // ... 39 | setLoading(false); 40 | alert(errorMessage); 41 | }); 42 | } 43 | 44 | return ( 45 | 46 | 47 | 48 | 55 | 56 | 63 | 72 | Daftar 73 | 74 | 75 | Nama 76 | 77 | 85 | setName(text)} 96 | /> 97 | 98 | 99 | Email 100 | 101 | 109 | setEmail(text)} 121 | /> 122 | 123 | 124 | Password 125 | 126 | 134 | setPassword(text)} 146 | /> 147 | 148 | { 150 | register(); 151 | }} 152 | disabled={loading} 153 | > 154 | 159 | 170 | {loading ? ( 171 | 172 | ) : ( 173 | 174 | Lanjutkan 175 | 176 | )} 177 | 178 | 179 | 180 | { 182 | loginWithFacebook(); 183 | }} 184 | disabled={loading} 185 | style={{ 186 | marginTop: 20, 187 | }} 188 | > 189 | 194 | 205 | {loading ? ( 206 | 207 | ) : ( 208 | 209 | Continue with Facebook 210 | 211 | )} 212 | 213 | 214 | 215 | 223 | 229 | Sudah punya akun? 230 | 231 | { 233 | navigation.navigate('Login'); 234 | }} 235 | > 236 | 244 | Masuk disini 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | ); 253 | } 254 | 255 | const styles = StyleSheet.create({ 256 | container: { 257 | flex: 1, 258 | backgroundColor: '#F7f7f7', 259 | }, 260 | }); 261 | -------------------------------------------------------------------------------- /screens/main/Category.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { View, ScrollView, ActivityIndicator } from 'react-native'; 3 | import Text from '../../components/utils/StyledText'; 4 | import TopNav from '../../components/navigation/SearchWIthBackTopNav'; 5 | import Background from '../../components/global/Background'; 6 | import CategoryCard from '../../components/card/CategoryCard'; 7 | import ExploreCard from '../../components/card/ExploreCard'; 8 | import moment from 'moment'; 9 | import * as firebase from 'firebase'; 10 | var _ = require('lodash'); 11 | export default function App({ navigation, route }) { 12 | const { category, name } = route.params; 13 | const [data, setData] = useState(null); 14 | useEffect(() => { 15 | getData(); 16 | }, []); 17 | 18 | function getData() { 19 | const ref = firebase 20 | .database() 21 | .ref('shows') 22 | .orderByChild('category') 23 | .equalTo(name); 24 | ref.once('value', function (snapshot) { 25 | const data = snapshot.val(); 26 | if (data !== null) { 27 | setData(_.sortBy(Object.values(data), ['date'])); 28 | } else { 29 | setData(null); 30 | } 31 | }); 32 | } 33 | return ( 34 | 35 | 36 | 37 | 38 | 45 | 46 | {category} 47 | 48 | 49 | {data == null ? ( 50 | 58 | 59 | 60 | ) : ( 61 | data.map((item) => ( 62 | 63 | )) 64 | )} 65 | 66 | 67 | 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /screens/main/Detail.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { View, ScrollView, Image, ActivityIndicator } from 'react-native'; 3 | import Text from '../../components/utils/StyledText'; 4 | import TopNav from '../../components/navigation/TopNavWithBack'; 5 | import Background from '../../components/global/Background'; 6 | import Colors from '../../constants/Colors'; 7 | import PerformerCard from '../../components/card/PerformerCard'; 8 | import TicketCard from '../../components/card/TicketCard'; 9 | import moment from 'moment'; 10 | import * as firebase from 'firebase'; 11 | 12 | var _ = require('lodash'); 13 | 14 | export default function App({ navigation, route }) { 15 | const { item } = route.params; 16 | const [data, setData] = useState(null); 17 | useEffect(() => { 18 | getTickets(); 19 | }, []); 20 | 21 | function getTickets() { 22 | const ref = firebase 23 | .database() 24 | .ref('tickets') 25 | .orderByChild('showUid') 26 | .equalTo(item.uid); 27 | ref.once('value', function (snapshot) { 28 | const data = snapshot.val(); 29 | if (data !== null) { 30 | setData(Object.values(data)); 31 | } else { 32 | setData(null); 33 | } 34 | }); 35 | } 36 | 37 | function performer(data) { 38 | const ilangin = _.reject(data, ['full', true]); 39 | const sort = _.sortBy(ilangin, ['timeStart']); 40 | 41 | return sort; 42 | } 43 | 44 | function full(data) { 45 | const ilangin = _.reject(data, ['full', false]); 46 | 47 | return ilangin; 48 | } 49 | 50 | return ( 51 | 52 | 53 | 54 | 55 | 61 | 68 | 77 | 84 | {item.title} 85 | 86 | 87 | {moment(item.date).format('D MMMM YYYY')} 88 | 89 | {item.ketengan && ( 90 | 102 | 103 | Bisa Ketengan 104 | 105 | 106 | )} 107 | {item.bundling && ( 108 | 119 | 120 | Gratis kuota streaming 121 | 122 | 123 | )} 124 | 125 | 126 | 127 | 135 | 142 | {item.description} 143 | 144 | 152 | Perfomance by: 153 | 154 | {data == null ? ( 155 | 162 | 163 | 164 | ) : ( 165 | <> 166 | {performer(data).map((item) => ( 167 | 168 | ))} 169 | 175 | 176 | Full Show 177 | 178 | {full(data).map((item) => ( 179 | 184 | ))} 185 | 186 | 192 | 193 | Tiket Ketengan 194 | 195 | {performer(data).map((item) => ( 196 | 201 | ))} 202 | 203 | 204 | )} 205 | 206 | 207 | 208 | ); 209 | } 210 | -------------------------------------------------------------------------------- /screens/main/Explore.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { 3 | View, 4 | ScrollView, 5 | ActivityIndicator, 6 | TouchableOpacity, 7 | } from 'react-native'; 8 | import Text from '../../components/utils/StyledText'; 9 | import TopNav from '../../components/navigation/SearchTopNav'; 10 | import Background from '../../components/global/Background'; 11 | import CategoryCard from '../../components/card/CategoryCard'; 12 | import ExploreCard from '../../components/card/ExploreCard'; 13 | import * as firebase from 'firebase'; 14 | import moment from 'moment'; 15 | var _ = require('lodash'); 16 | export default function App({ navigation }) { 17 | const [data, setData] = useState(null); 18 | const [count, setCount] = useState(10); 19 | const [loading, setLoading] = useState(false); 20 | useEffect(() => { 21 | getData(count); 22 | }, []); 23 | 24 | function getData(count) { 25 | const ref = firebase 26 | .database() 27 | .ref('shows') 28 | .orderByChild('date') 29 | .startAt(moment().format('YYYY-MM-DD')) 30 | .limitToFirst(count); 31 | ref.once('value', function (snapshot) { 32 | const data = snapshot.val(); 33 | if (data !== null) { 34 | setData(_.sortBy(Object.values(data), ['date'])); 35 | setLoading(false); 36 | } else { 37 | setData(null); 38 | setLoading(false); 39 | } 40 | }); 41 | } 42 | return ( 43 | 44 | 45 | 46 | 47 | 48 | 49 | Oi, mau liat konser online apa nih? 50 | 51 | 52 | 53 | 62 | { 64 | navigation.navigate('Category', { 65 | category: 'Pop', 66 | name: 'pop', 67 | }); 68 | }} 69 | > 70 | 74 | 75 | { 77 | navigation.navigate('Category', { 78 | category: 'Jazz', 79 | name: 'jazz', 80 | }); 81 | }} 82 | > 83 | 87 | 88 | { 90 | navigation.navigate('Category', { 91 | category: 'Indie', 92 | name: 'indie', 93 | }); 94 | }} 95 | > 96 | 100 | 101 | { 103 | navigation.navigate('Category', { 104 | category: 'Electronic Dance Music', 105 | name: 'electronicMusic', 106 | }); 107 | }} 108 | > 109 | 113 | 114 | { 116 | navigation.navigate('Category', { 117 | category: 'Dangdut', 118 | name: 'dangdut', 119 | }); 120 | }} 121 | > 122 | 126 | 127 | 128 | 129 | 137 | 138 | Konser online yang akan tayang 139 | 140 | {data == null ? ( 141 | 148 | 149 | 150 | ) : ( 151 | data.map((item) => ( 152 | 153 | )) 154 | )} 155 | {data && ( 156 | { 158 | setLoading(true); 159 | setCount(count + 5); 160 | getData(count + 5); 161 | }} 162 | style={{ 163 | flex: 1, 164 | backgroundColor: 'rgba(255,255,255,0.2)', 165 | padding: 20, 166 | paddingVertical: 10, 167 | borderRadius: 12, 168 | marginTop: 15, 169 | flexDirection: 'row', 170 | justifyContent: 'center', 171 | }} 172 | disabled={loading} 173 | > 174 | {loading ? ( 175 | 176 | ) : ( 177 | 184 | More show 185 | 186 | )} 187 | 188 | )} 189 | 190 | 191 | 192 | ); 193 | } 194 | -------------------------------------------------------------------------------- /screens/main/Home.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { StyleSheet, View, ActivityIndicator } from 'react-native'; 3 | import Text from '../../components/utils/StyledText'; 4 | import TopNav from '../../components/navigation/TopNav'; 5 | import FestivalCard from '../../components/card/FestivalCard'; 6 | import Background from '../../components/global/Background'; 7 | import * as firebase from 'firebase'; 8 | import moment from 'moment'; 9 | 10 | var _ = require('lodash'); 11 | export default function App({ navigation }) { 12 | const [data, setData] = useState(null); 13 | const [preference, setPreference] = useState(null); 14 | const [filtered, setFiltered] = useState(null); 15 | 16 | useEffect(() => { 17 | getPreference(); 18 | getData(); 19 | }, []); 20 | 21 | useEffect(() => { 22 | load(); 23 | }, [data, preference]); 24 | 25 | function filterData() { 26 | let datanya = data; 27 | if ( 28 | preference.jazz == false && 29 | preference.pop == false && 30 | preference.indie == false && 31 | preference.dangdut == false && 32 | preference.electronicMusic == false 33 | ) { 34 | datanya = datanya; 35 | } else { 36 | if (preference.jazz == false) { 37 | datanya = _.reject(datanya, { category: 'jazz' }); 38 | } 39 | if (preference.pop == false) { 40 | datanya = _.reject(datanya, { category: 'pop' }); 41 | } 42 | if (preference.indie == false) { 43 | datanya = _.reject(datanya, { category: 'indie' }); 44 | } 45 | if (preference.dangdut == false) { 46 | datanya = _.reject(datanya, { category: 'dangdut' }); 47 | } 48 | if (preference.electronicMusic == false) { 49 | datanya = _.reject(datanya, { category: 'electronicMusic' }); 50 | } 51 | } 52 | 53 | return Object.values(datanya); 54 | } 55 | 56 | function getPreference() { 57 | const { uid } = firebase.auth().currentUser; 58 | const ref = firebase.database().ref('users/' + uid + '/preference'); 59 | ref.once('value', function (snapshot) { 60 | const data = snapshot.val(); 61 | if (data !== null) { 62 | setPreference(data); 63 | } else { 64 | setPreference(null); 65 | } 66 | }); 67 | } 68 | 69 | function load() { 70 | if (data !== null && preference !== null) { 71 | setFiltered(filterData()); 72 | } 73 | } 74 | 75 | function getData() { 76 | const ref = firebase 77 | .database() 78 | .ref('shows') 79 | .orderByChild('date') 80 | .startAt(moment().format('YYYY-MM-DD')); 81 | ref.once('value', function (snapshot) { 82 | const data = snapshot.val(); 83 | if (data !== null) { 84 | setData(Object.values(data)); 85 | } else { 86 | setData(null); 87 | } 88 | }); 89 | } 90 | 91 | return ( 92 | 93 | 94 | 95 | 96 | 97 | Kayanya kamu suka ini deh 98 | 99 | 100 | {filtered == null ? ( 101 | 108 | 109 | 110 | ) : ( 111 | 112 | )} 113 | 114 | ); 115 | } 116 | -------------------------------------------------------------------------------- /screens/main/MyTickets.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { View, ScrollView, ActivityIndicator, Dimensions } from 'react-native'; 3 | import Text from '../../components/utils/StyledText'; 4 | import TopNav from '../../components/navigation/TopNav'; 5 | import Background from '../../components/global/Background'; 6 | import TiketCard from '../../components/card/MyTicketsCard'; 7 | import * as firebase from 'firebase'; 8 | const { width, height } = Dimensions.get('window'); 9 | var _ = require('lodash'); 10 | export default function App({ navigation }) { 11 | const [loading, setLoading] = useState(true); 12 | const [paid, setPaid] = useState(null); 13 | const [notPaid, setNotPaid] = useState(null); 14 | useEffect(() => { 15 | getUsersTickets(); 16 | }, []); 17 | 18 | function getUsersTickets() { 19 | const user = firebase.auth().currentUser; 20 | const ref = firebase 21 | .database() 22 | .ref('invoice') 23 | .orderByChild('userUid') 24 | .equalTo(user.uid); 25 | ref.on('value', function (snapshot) { 26 | const data = snapshot.val(); 27 | if (data !== null) { 28 | const tix = Object.values(data); 29 | const paid = _.reject(tix, ['paid', false]); 30 | const notPaid = _.reject(tix, ['paid', true]); 31 | setPaid(paid); 32 | setNotPaid(notPaid); 33 | setLoading(false); 34 | } else { 35 | setLoading(null); 36 | } 37 | }); 38 | } 39 | return ( 40 | 41 | 42 | 43 | {loading ? ( 44 | 52 | 53 | 54 | ) : loading == null ? ( 55 | 63 | 64 | Belum ada tiket yang dibooking 65 | 66 | 67 | ) : ( 68 | <> 69 | {paid.length !== 0 && ( 70 | 77 | 78 | Yang sudah dibayar 79 | 80 | 81 | )} 82 | 83 | {paid.map((item) => ( 84 | 85 | ))} 86 | 87 | {notPaid.length !== 0 && ( 88 | 95 | 96 | Yang belum dibayar 97 | 98 | 99 | )} 100 | {notPaid.map((item) => ( 101 | 102 | ))} 103 | 104 | )} 105 | 106 | 107 | ); 108 | } 109 | -------------------------------------------------------------------------------- /screens/main/Payment.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { 3 | View, 4 | ScrollView, 5 | Image, 6 | TextInput, 7 | ActivityIndicator, 8 | TouchableOpacity, 9 | } from 'react-native'; 10 | import Text from '../../components/utils/StyledText'; 11 | import TopNav from '../../components/navigation/TopNavWithBack'; 12 | import Background from '../../components/global/Background'; 13 | import Colors from '../../constants/Colors'; 14 | import PerformerCard from '../../components/card/PerformerCard'; 15 | import TicketCard from '../../components/card/TicketCard'; 16 | import { Icon } from '@ui-kitten/components'; 17 | import moment from 'moment'; 18 | import * as firebase from 'firebase'; 19 | import numeral from 'numeral'; 20 | export default function App({ navigation, route }) { 21 | const { item } = route.params; 22 | const [show, setShow] = useState(null); 23 | 24 | useEffect(() => { 25 | getShowDetail(); 26 | }, []); 27 | function getShowDetail() { 28 | const ref = firebase.database().ref('shows/' + item.ticket.showUid); 29 | ref.once('value', function (snapshot) { 30 | const data = snapshot.val(); 31 | if (data !== null) { 32 | setShow(data); 33 | } else { 34 | setShow(null); 35 | } 36 | }); 37 | } 38 | return ( 39 | 40 | 41 | 42 | 48 | {show == null ? ( 49 | 56 | 57 | 58 | ) : ( 59 | <> 60 | 61 | {show.title} 62 | 63 | 70 | 77 | 84 | {moment(show.date).format('D MMMM YYYY')} 85 | 86 | 87 | 88 | )} 89 | 90 | 91 | 92 | 93 | 101 | 108 | Ini adalah acara online, link akan diberikan setelah proses 109 | pembayaran selesai. 110 | 111 | {item.ticket.bundling && ( 112 | 120 | Pembayaran sudah termasuk kuota bonus, kode voucher kuota bonus 121 | akan diberikan bersama link untuk streaming. 122 | 123 | )} 124 | 125 | 132 | 139 | Total 140 | 141 | 151 | 157 | 164 | Nonton Semuanya 165 | 166 | 173 | {numeral(item.ticket.price).format('0,0')} 174 | 175 | 176 | 183 | 190 | Biaya admin 191 | 192 | 199 | 5.000 200 | 201 | 202 | 209 | 216 | Total 217 | 218 | 225 | {numeral(item.ticket.price + 5000).format('0,0')} 226 | 227 | 228 | 229 | 230 | 231 | { 244 | navigation.navigate('PaymentGateway', { 245 | show: show, 246 | invoice: item, 247 | total: item.ticket.price + 5000, 248 | }); 249 | }} 250 | > 251 | 252 | Bayar Sekarang 253 | 254 | 255 | 256 | ); 257 | } 258 | -------------------------------------------------------------------------------- /screens/main/PaymentGateway.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { 3 | ScrollView, 4 | ActivityIndicator, 5 | View, 6 | TouchableOpacity, 7 | } from 'react-native'; 8 | import { WebView } from 'react-native-webview'; 9 | import base64 from 'base-64'; 10 | import Colors from '../../constants/Colors'; 11 | import Text from '../../components/utils/StyledText'; 12 | import TopNav from '../../components/navigation/TopNavWithBack'; 13 | import Background from '../../components/global/Background'; 14 | import { FontAwesome } from '@expo/vector-icons'; 15 | import * as firebase from 'firebase'; 16 | export default function App({ navigation, route }) { 17 | const [loading, setLoading] = useState(false); 18 | const { invoice, show, total } = route.params; 19 | const [user, setUser] = useState(false); 20 | const [mid, setMid] = useState(false); 21 | const u = firebase.auth().currentUser; 22 | useEffect(() => { 23 | firebase 24 | .database() 25 | .ref('users/' + u.uid) 26 | .once('value') 27 | .then(async function (snapshot) { 28 | const data = snapshot.val(); 29 | midtrans(data).then((data) => { 30 | setMid(data); 31 | if (data.error_messages) { 32 | alert('Tagihan ini sudah dibayar'); 33 | } 34 | }); 35 | }); 36 | }, []); 37 | async function midtrans(user) { 38 | const url = 'https://app.sandbox.midtrans.com/snap/v1/transactions'; 39 | const serverKey = 'SB-Mid-server-6zAlKGcTJi-6XExDUt79J31l:'; 40 | const base64Key = base64.encode(serverKey); 41 | 42 | const data = { 43 | transaction_details: { 44 | order_id: invoice.uid, 45 | gross_amount: total, 46 | }, 47 | item_details: [ 48 | { 49 | id: invoice.ticket.uid, 50 | price: invoice.ticket.price, 51 | quantity: 1, 52 | name: show.title + ' - ' + invoice.ticket.performer, 53 | category: 'Online Show', 54 | merchant_name: show.title, 55 | }, 56 | { 57 | id: 'Admin Fee', 58 | price: 5000, 59 | quantity: 1, 60 | name: 'Admin Fee', 61 | category: 'Cut Fee', 62 | merchant_name: 'TikTeng', 63 | }, 64 | ], 65 | customer_details: { 66 | first_name: user.name, 67 | email: user.email, 68 | phone: user.phoneNumber, 69 | }, 70 | }; 71 | 72 | const response = await fetch(url, { 73 | method: 'POST', 74 | headers: { 75 | Accept: 'application/json', 76 | 'Content-Type': 'application/json', 77 | Authorization: 'Basic ' + base64Key, 78 | }, 79 | body: JSON.stringify(data), 80 | }); 81 | return response.json(); 82 | } 83 | 84 | async function check() { 85 | const url = `https://api.sandbox.midtrans.com/v2/${invoice.uid}/status`; 86 | const serverKey = 'SB-Mid-server-6zAlKGcTJi-6XExDUt79J31l:'; 87 | const base64Key = base64.encode(serverKey); 88 | 89 | const response = await fetch(url, { 90 | method: 'GET', 91 | headers: { 92 | Accept: 'application/json', 93 | 'Content-Type': 'application/json', 94 | Authorization: 'Basic ' + base64Key, 95 | }, 96 | }); 97 | return response.json(); 98 | } 99 | 100 | return ( 101 | 102 | 103 | {!mid ? ( 104 | 111 | 112 | 113 | ) : ( 114 | <> 115 | 121 | 122 | { 124 | setLoading(true); 125 | check().then((data) => { 126 | if (data.status_code == 200) { 127 | firebase 128 | .database() 129 | .ref('invoice/' + invoice.uid) 130 | .update({ 131 | paid: true, 132 | }) 133 | .then(function () { 134 | setLoading(false); 135 | navigation.replace('TicketDetail', { 136 | item: invoice, 137 | }); 138 | }) 139 | .catch(function (error) { 140 | alert(error); 141 | }); 142 | } else { 143 | setLoading(false); 144 | alert('Hmmm, kamu belum bayar nih'); 145 | } 146 | }); 147 | }} 148 | style={{ 149 | backgroundColor: Colors.primary, 150 | padding: 20, 151 | paddingVertical: 15, 152 | 153 | flexDirection: 'row', 154 | justifyContent: 'space-between', 155 | }} 156 | disabled={loading} 157 | > 158 | 165 | Cek Pembayaran 166 | 167 | {loading ? ( 168 | 169 | ) : ( 170 | 171 | )} 172 | 173 | 174 | )} 175 | 176 | ); 177 | } 178 | -------------------------------------------------------------------------------- /screens/main/Profile.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { View, ScrollView, Image } from 'react-native'; 3 | import Text from '../../components/utils/StyledText'; 4 | import TopNav from '../../components/navigation/TopNav'; 5 | import Background from '../../components/global/Background'; 6 | import TiketCard from '../../components/card/MyTicketsCard'; 7 | import Colors from '../../constants/Colors'; 8 | import * as firebase from 'firebase'; 9 | import { TouchableOpacity } from 'react-native-gesture-handler'; 10 | 11 | export default function App({ navigation }) { 12 | const [user, setUser] = useState(false); 13 | const u = firebase.auth().currentUser; 14 | useEffect(() => { 15 | firebase 16 | .database() 17 | .ref('users/' + u.uid) 18 | .once('value') 19 | .then(function (snapshot) { 20 | setUser(snapshot.val()); 21 | }); 22 | }, []); 23 | 24 | return ( 25 | 26 | 27 | 34 | 46 | 47 | {user.name} 48 | 49 | 50 | 51 | 61 | 67 | 74 | Email 75 | 76 | 83 | {user.email} 84 | 85 | 86 | 93 | 100 | No. Telepon 101 | 102 | 109 | {user.phoneNumber ? user.phoneNumber : 'Kosong'} 110 | 111 | 112 | 113 | 114 | { 116 | firebase.auth().signOut(); 117 | }} 118 | style={{ 119 | marginTop: 10, 120 | marginHorizontal: 20, 121 | }} 122 | > 123 | 130 | Logout 131 | 132 | 133 | 134 | 135 | ); 136 | } 137 | -------------------------------------------------------------------------------- /screens/main/Search.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { View, ScrollView, ActivityIndicator } from 'react-native'; 3 | import Text from '../../components/utils/StyledText'; 4 | import TopNav from '../../components/navigation/SearchWIthBackTopNav'; 5 | import Background from '../../components/global/Background'; 6 | import CategoryCard from '../../components/card/CategoryCard'; 7 | import ExploreCard from '../../components/card/ExploreCard'; 8 | import moment from 'moment'; 9 | import * as firebase from 'firebase'; 10 | var _ = require('lodash'); 11 | export default function App({ navigation, route }) { 12 | const { search } = route.params; 13 | const [data, setData] = useState(null); 14 | useEffect(() => { 15 | getData(); 16 | }, []); 17 | 18 | function getData() { 19 | const ref = firebase 20 | .database() 21 | .ref('shows') 22 | .orderByChild('title') 23 | .startAt(search) 24 | .endAt(search + '\uf8ff'); 25 | ref.once('value', function (snapshot) { 26 | const data = snapshot.val(); 27 | if (data !== null) { 28 | setData(_.sortBy(Object.values(data), ['date'])); 29 | } else { 30 | setData(null); 31 | } 32 | }); 33 | } 34 | return ( 35 | 36 | 37 | 38 | 39 | 46 | 47 | {search} 48 | 49 | 50 | {data == null ? ( 51 | 59 | 60 | 61 | ) : ( 62 | data.map((item) => ( 63 | 64 | )) 65 | )} 66 | 67 | 68 | 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /screens/main/TicketDetail.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { 3 | View, 4 | ScrollView, 5 | Image, 6 | TextInput, 7 | ActivityIndicator, 8 | Clipboard, 9 | TouchableOpacity, 10 | } from 'react-native'; 11 | import Text from '../../components/utils/StyledText'; 12 | import TopNav from '../../components/navigation/TopNavWithBack'; 13 | import Background from '../../components/global/Background'; 14 | import Colors from '../../constants/Colors'; 15 | import PerformerCard from '../../components/card/PerformerCard'; 16 | import TicketCard from '../../components/card/MyTicketsCard'; 17 | import { Icon } from '@ui-kitten/components'; 18 | import * as firebase from 'firebase'; 19 | import * as WebBrowser from 'expo-web-browser'; 20 | export default function App({ navigation, route }) { 21 | const { item } = route.params; 22 | const [loading, setLoading] = useState(null); 23 | const [data, setData] = useState(null); 24 | 25 | useEffect(() => { 26 | getData(); 27 | }, []); 28 | 29 | function getData() { 30 | const ref = firebase.database().ref('tickets/' + item.ticket.uid); 31 | ref.on('value', function (snapshot) { 32 | const data = snapshot.val(); 33 | if (data !== null) { 34 | setData(data); 35 | 36 | setLoading(false); 37 | } else { 38 | setLoading(null); 39 | setData(false); 40 | } 41 | }); 42 | } 43 | async function openBrowser(link) { 44 | await WebBrowser.openBrowserAsync(link); 45 | } 46 | return ( 47 | 48 | 49 | 50 | 51 | 52 | {item.ticket.bundling && ( 53 | 62 | 69 | Reddem Kuota 70 | 71 | 79 | Silahkan masukan kode unik untuk kuota gratisnya 80 | 81 | 82 | 91 | {data !== null ? ( 92 | 95 | 8512-1245-5238-6923 96 | 97 | ) : ( 98 | 99 | )} 100 | 101 | 102 | )} 103 | 104 | 113 | 121 | Mohon simpan link sebaik-baiknya dan rahasiakan, karena satu link 122 | hanya bisa diakses satu device. Selamat Menonton! 123 | 124 | 133 | {data !== null ? ( 134 | 137 | {data.link} 138 | 139 | ) : ( 140 | 141 | )} 142 | 143 | {data !== null && ( 144 | 150 | { 159 | Clipboard.setString(data.link); 160 | }} 161 | disabled={data !== null ? false : true} 162 | > 163 | 170 | Copy link 171 | 172 | 173 | 178 | 179 | { 189 | openBrowser(data.link); 190 | }} 191 | > 192 | 199 | Buka link 200 | 201 | 202 | 203 | )} 204 | 205 | 206 | 207 | ); 208 | } 209 | -------------------------------------------------------------------------------- /screens/onBoarding/Loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ActivityIndicator } from 'react-native'; 3 | import Background from '../../components/global/Background'; 4 | export default function App({}) { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /screens/onBoarding/PhoneNumber.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | StyleSheet, 4 | View, 5 | TextInput, 6 | TouchableOpacity, 7 | ActivityIndicator, 8 | } from 'react-native'; 9 | import Text from '../../components/utils/StyledText'; 10 | import TopNav from '../../components/navigation/TopNavWithBack'; 11 | import Colors from '../../constants/Colors'; 12 | import Background from '../../components/global/Background'; 13 | import PreferenceCard from '../../components/card/PreferenceCard'; 14 | import * as firebase from 'firebase'; 15 | export default function App({ navigation }) { 16 | const [phone, setPhone] = React.useState(''); 17 | const [loading, setLoading] = React.useState(false); 18 | function updatePhone() { 19 | setLoading(true); 20 | const user = firebase.auth().currentUser; 21 | firebase 22 | .database() 23 | .ref('users/' + user.uid) 24 | .update({ 25 | name: user.displayName, 26 | email: user.email, 27 | phoneNumber: phone, 28 | uid: user.uid, 29 | }) 30 | .catch((e) => { 31 | setLoading(false); 32 | alert(e); 33 | }); 34 | } 35 | return ( 36 | 37 | 38 | 39 | 40 | 41 | 47 | 48 | Satu langkah lagi... 49 | 50 | 58 | setPhone(text)} 70 | /> 71 | 72 | 73 | { 75 | updatePhone(); 76 | }} 77 | style={{ 78 | margin: 20, 79 | padding: 15, 80 | backgroundColor: Colors.primary, 81 | alignItems: 'center', 82 | borderRadius: 12, 83 | position: 'absolute', 84 | left: 0, 85 | right: 0, 86 | bottom: 0, 87 | }} 88 | disabled={loading} 89 | > 90 | {loading ? ( 91 | 92 | ) : ( 93 | 94 | Lanjutkan 95 | 96 | )} 97 | 98 | 99 | ); 100 | } 101 | -------------------------------------------------------------------------------- /screens/onBoarding/Preference.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { 3 | StyleSheet, 4 | View, 5 | TouchableOpacity, 6 | TouchableWithoutFeedback, 7 | } from 'react-native'; 8 | import Text from '../../components/utils/StyledText'; 9 | import TopNav from '../../components/navigation/TopNav'; 10 | import Colors from '../../constants/Colors'; 11 | import Background from '../../components/global/Background'; 12 | import PreferenceCard from '../../components/card/PreferenceCard'; 13 | import * as firebase from 'firebase'; 14 | export default function App({ navigation }) { 15 | const [jazz, setJazz] = useState(false); 16 | const [pop, setPop] = useState(false); 17 | const [dangdung, setDangdut] = useState(false); 18 | const [indie, setIndie] = useState(false); 19 | const [edm, setEdm] = useState(false); 20 | const [preference, setPreference] = useState({ 21 | jazz: false, 22 | pop: false, 23 | dangdut: false, 24 | indie: false, 25 | electronicMusic: false, 26 | }); 27 | const handleCategory = (name, choosen) => { 28 | if (choosen) { 29 | setPreference({ 30 | ...preference, 31 | [name]: true, 32 | }); 33 | } else { 34 | setPreference({ 35 | ...preference, 36 | [name]: false, 37 | }); 38 | } 39 | }; 40 | function lanjutkan() { 41 | const user = firebase.auth().currentUser; 42 | firebase 43 | .database() 44 | .ref('users/' + user.uid) 45 | .update({ preference }) 46 | .then(function () { 47 | navigation.navigate('PhoneNumber'); 48 | }); 49 | } 50 | return ( 51 | 52 | 58 | 59 | Genre musik yang kamu suka apa sih? 60 | 61 | 62 | 63 | 64 | 65 | 66 | 71 | 72 | 73 | { 75 | lanjutkan(); 76 | }} 77 | style={{ 78 | margin: 20, 79 | padding: 15, 80 | backgroundColor: Colors.primary, 81 | alignItems: 'center', 82 | borderRadius: 12, 83 | position: 'absolute', 84 | left: 0, 85 | right: 0, 86 | bottom: 0, 87 | }} 88 | > 89 | 90 | Lanjutkan 91 | 92 | 93 | 94 | ); 95 | } 96 | --------------------------------------------------------------------------------