diff --git a/controllers/AttendeesListViewController.js b/controllers/AttendeesListViewController.js index ec1e4e6..a86b7e4 100644 --- a/controllers/AttendeesListViewController.js +++ b/controllers/AttendeesListViewController.js @@ -9,6 +9,7 @@ import moment from 'moment'; import React from 'react'; import {FlatList, View, ActivityIndicator} from 'react-native'; +import SegmentedControlTab from 'react-native-segmented-control-tab'; import {inject, observer} from 'mobx-react/native'; import {SearchBar} from 'react-native-elements'; @@ -20,6 +21,14 @@ import TournamentRow from './components/TournamentRow'; const keyExtractor = (item, index) => item.id; export default class AttendeesListViewController extends MemeleeViewController { + state = { + selectedIndex: 0 + }; + + swapIndex = (index) => this.setState({ + selectedIndex: index + }); + renderItem = ({item}) => () render() { @@ -28,7 +37,20 @@ export default class AttendeesListViewController extends MemeleeViewController { keyExtractor: keyExtractor, renderItem: this.renderItem }; + + const segmentedProps = { + values: ['Players', 'Teams'], + borderRadius: 0, + activeTabStyle: styles.tournamentInfoActiveTableStyle, + tabStyle: styles.tournamentInfoTabsStyle, + tabTextStyle: styles.tournamentInfoTabTextStyle, + selectedIndex: this.state.selectedIndex, + onTabPress: this.swapIndex + }; - return + return ( + + + ); } } diff --git a/controllers/BracketViewController.js b/controllers/BracketViewController.js index 9247047..4fabba9 100644 --- a/controllers/BracketViewController.js +++ b/controllers/BracketViewController.js @@ -11,13 +11,21 @@ import React from 'react'; import {ScrollView, Text, View} from 'react-native'; import {inject, observer} from 'mobx-react/native'; +import styles from '../styles'; + import MemeleeViewController from './MemeleeViewController'; const Match = ({set, ...rest}) => ( - - {set.entrant1.name ? set.entrant1.name : ''} {set.entrant1Score} - - {set.entrant2.name ? set.entrant2.name : ''} {set.entrant2Score} + + + {set.entrant1.name ? set.entrant1.name : ''} + {set.entrant1Score} + + + + {set.entrant2.name ? set.entrant2.name : ''} + {set.entrant2Score} + ); @@ -33,8 +41,11 @@ export default class BracketViewController extends MemeleeViewController { {this.props.Events.bracketData[key].map(bracket => ( - {bracket.title} - {bracket.sets.map(set => )} + {bracket.title} + {bracket.sets.map(set => { + if(set.isGF) console.log(JSON.stringify(set, null, 4)); + return ; + })} ))} diff --git a/controllers/ContactViewController.js b/controllers/ContactViewController.js index ec1e4e6..e54b43c 100644 --- a/controllers/ContactViewController.js +++ b/controllers/ContactViewController.js @@ -6,29 +6,54 @@ * @copyright Ryan McGrath 2018. */ -import moment from 'moment'; import React from 'react'; -import {FlatList, View, ActivityIndicator} from 'react-native'; -import {inject, observer} from 'mobx-react/native'; -import {SearchBar} from 'react-native-elements'; +import {ScrollView, View, Text, StyleSheet} from 'react-native'; +import Markdown from 'react-native-markdown-renderer'; +import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; +import SettingsList, {Header, Item} from 'react-native-settings-list'; import styles from '../styles'; -import Constants from '../utils/Constants'; +import {openURL} from '../utils'; import MemeleeViewController from './MemeleeViewController'; -import TournamentRow from './components/TournamentRow'; -const keyExtractor = (item, index) => item.id; - -export default class AttendeesListViewController extends MemeleeViewController { - renderItem = ({item}) => () +export default class ContactViewController extends MemeleeViewController { + email = () => { openURL('mailto:' + this.props.data.email); } + phone = () => { openURL('tel:' + this.props.data.phone); } + twitter = () => { openURL('https://twitter.com/' + this.props.data.twitter); } render() { - const props = { - data: this.props.data, - keyExtractor: keyExtractor, - renderItem: this.renderItem + const s = StyleSheet.flatten(styles.tournamentDetailsEventWrapper); + const ss = { + itemWidth: 50, + backgroundColor: s.backgroundColor, + style: styles.tournamentDetailsEventWrapper, + titleStyle: styles.tournamentDetailsEventItem }; - return + return ( + + {this.props.data.email ? + + + } /> : null} + {this.props.data.phone ? + + + } /> : null} + {this.props.data.twitter ? + + + } /> : null} + + + {this.props.data.info ? + + {this.props.data.info} + + : null} + ); } } diff --git a/controllers/LocationViewController.js b/controllers/LocationViewController.js index ec1e4e6..1c8f3e7 100644 --- a/controllers/LocationViewController.js +++ b/controllers/LocationViewController.js @@ -6,29 +6,38 @@ * @copyright Ryan McGrath 2018. */ -import moment from 'moment'; import React from 'react'; -import {FlatList, View, ActivityIndicator} from 'react-native'; -import {inject, observer} from 'mobx-react/native'; -import {SearchBar} from 'react-native-elements'; +import {ScrollView, StyleSheet, Image, Text, View, TouchableOpacity, Dimensions} from 'react-native'; +import Markdown, {PluginContainer} from 'react-native-markdown-renderer'; +import SettingsList, {Header, Item} from 'react-native-settings-list'; +import openMap from 'react-native-open-maps'; import styles from '../styles'; -import Constants from '../utils/Constants'; import MemeleeViewController from './MemeleeViewController'; -import TournamentRow from './components/TournamentRow'; - -const keyExtractor = (item, index) => item.id; - -export default class AttendeesListViewController extends MemeleeViewController { - renderItem = ({item}) => () +export default class LocationViewController extends MemeleeViewController { render() { - const props = { - data: this.props.data, - keyExtractor: keyExtractor, - renderItem: this.renderItem + const addy = '\n' + this.props.data.venue.name + '\n' + this.props.data.venue.address + ' ' + this.props.data.venue.city + + ' ' + this.props.data.venue.state + ' ' + this.props.data.venue.postalCode + '\n' + this.props.data.venue.country + '\n'; + + const s = StyleSheet.flatten(styles.tournamentDetailsEventWrapper); + const ss = { + itemWidth: 50, + backgroundColor: s.backgroundColor, + style: styles.tournamentDetailsEventWrapper, + titleStyle: styles.tournamentDetailsEventItem }; - return + return ( + + openMap({latitude: this.props.data.map.lat, longitude: this.props.data.map.lng})} /> + + + {this.props.data.gettingThere ? + + {this.props.data.gettingThere} + + : null} + ); } } diff --git a/controllers/TournamentInfoViewController.js b/controllers/TournamentInfoViewController.js index 85538bd..c032118 100644 --- a/controllers/TournamentInfoViewController.js +++ b/controllers/TournamentInfoViewController.js @@ -9,6 +9,7 @@ import moment from 'moment'; import React from 'react'; import {ScrollView, StyleSheet, Image, Text, View, TouchableOpacity, Dimensions} from 'react-native'; +import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import Markdown, {PluginContainer} from 'react-native-markdown-renderer'; import SegmentedControlTab from 'react-native-segmented-control-tab'; import SettingsList, {Header, Item} from 'react-native-settings-list'; @@ -21,16 +22,116 @@ import EventsStore from '../stores/TournamentEventStore'; import MemeleeViewController from './MemeleeViewController'; const w = Dimensions.get('screen').width; -/* - - Register - - - Bookmark - - -*/ +const determineTabs = (tournament) => { + const slugs = parseSlugs(tournament, null); + const tabs = [ + // Actually can't do this without using what's pretty clearly a private API. :( + // {slug: 'attendees', name: 'Attendees', screen: Constants.Screens.Attendees, adminOnly: false, data: []} + {slug: 'attendees', icon: 'link', name: 'Attendees', url: 'https://smash.gg/tournament/' + slugs.tournament + '/attendees/players', adminOnly: false} + ]; + + if( + tournament.gettingThere || tournament.lat || tournament.lng || tournament.mapsPlaceId || tournament.venueName || + tournament.venueAddress || tournament.city || tournament.addrState || tournament.countryCode || tournament.postalCode + ) tabs.push({ + slug: 'location', + name: 'Location', + icon: 'map-marker', + screen: Constants.Screens.Location, + adminOnly: false, + data: { + gettingThere: tournament.gettingThere, + map: { + lat: tournament.lat, + lng: tournament.lng, + mapsPlaceId: tournament.mapsPlaceId, + }, + venue: { + name: tournament.venueName, + address: tournament.venueAddress, + city: tournament.city, + state: tournament.addrState, + country: tournament.countryCode, + postalCode: tournament.postalCode + } + } + }); + + tabs.push({ + slug: 'contact', + name: 'Contact', + icon: 'email', + screen: Constants.Screens.Contact, + adminOnly: false, + data: { + email: tournament.contactEmail, + info: tournament.contactInfo, + phone: tournament.contactPhone, + twitter: tournament.contactTwitter + } + }); + + if(tournament.rules && tournament.rules !== '') { + if(tournament.rules.startsWith('http://') || tournament.rules.startsWith('https://')) { + tabs.push({slug: 'rules', icon: 'link', name: 'Rules', url: tournament.rules, adminOnly: false}); + } else { + tabs.push({slug: 'rules', name: 'Rules', icon: 'file-document-box', info: tournament.rules, adminOnly: false}); + } + } + + if(tournament.prizes && tournament.prizes !== '') { + if(tournament.prizes.startsWith('http://') || tournament.prizes.startsWith('https://')) { + tabs.push({slug: 'prizes', name: 'Prizes', icon: 'trophy', url: tournament.prizes, adminOnly: false}); + } else { + tabs.push({slug: 'prizes', name: 'Prizes', icon: 'trophy', info: tournament.prizes, adminOnly: false}); + } + } + + if(tournament.publishing && tournament.publishing.fantasy) + tabs.push({ + slug: 'fantasy', + name: 'Fantasy', + icon: 'chart-timeline', + url: 'https://smash.gg/tournament/' + slugs.tournament + '/fantasy/', + adminOnly: false + }); + + const generatedTabs = tournament.generatedTabs; + if(generatedTabs) { + const objs = Object.keys(generatedTabs).map(key => generatedTabs[key]); + + objs.forEach(tab => { + Object.keys(tab).map(key => ({ + slug: key, + name: tab[key].name, + adminOnly: tab[key].adminOnly, + icon: 'link', + url: 'https://smash.gg/tournament/' + slugs.tournament + '/' + key + })).filter(tab => !tab.adminOnly || tab.adminOnly === false).forEach(tab => tabs.push(tab)); + }); + } + + const links = tournament.links; + if(links) { + Object.keys(tournament.links).forEach(key => { + tabs.push({ + slug: key, + icon: 'link', + name: key[0].toUpperCase() + key.substring(1), + url: links[key] + }); + }); + } + + return tabs; +} + +const SIcon = (icon) => ( + + + +); export default class TournamentInfoViewController extends MemeleeViewController { state = { @@ -39,50 +140,9 @@ export default class TournamentInfoViewController extends MemeleeViewController }; componentWillMount = () => { - const tabs = [ - {slug: 'attendees', name: 'Attendees', screen: Constants.Screens.Attendees, adminOnly: false}, - {slug: 'location', name: 'Location', screen: Constants.Screens.Location, adminOnly: false}, - {slug: 'contact', name: 'Contact', screen: Constants.Screens.Contact, adminOnly: false} - ]; - - if(this.props.tournament.rules && this.props.tournament.rules !== '') { - if(this.props.tournament.rules.startsWith('http://') || this.props.tournament.rules.startsWith('https://')) { - tabs.push({slug: 'rules', name: 'Rules', url: this.props.tournament.rules, adminOnly: false}); - } else { - tabs.push({slug: 'rules', name: 'Rules', info: this.props.tournament.rules, adminOnly: false}); - } - } - - if(this.props.tournament.prizes && this.props.tournament.prizes !== '') { - if(this.props.tournament.prizes.startsWith('http://') || this.props.tournament.prizes.startsWith('https://')) { - tabs.push({slug: 'prizes', name: 'Prizes', url: this.props.tournament.prizes, adminOnly: false}); - } else { - tabs.push({slug: 'prizes', name: 'Prizes', info: this.props.tournament.prizes, adminOnly: false}); - } - } - - if(this.props.tournament.publishing && this.props.tournament.publishing.fantasy) - tabs.push({ - slug: 'fantasy', - name: 'Fantasy', - url: 'https://smash.gg/tournament/' + parseSlugs(this.props.tournament, null).tournament + '/fantasy/', - adminOnly: false - }); - - const generatedTabs = this.props.tournament.generatedTabs; - if(generatedTabs) { - const objs = Object.keys(generatedTabs).map(key => generatedTabs[key]); - - objs.forEach(tab => { - Object.keys(tab).map(key => ({ - slug: key, - name: tab[key].name, - adminOnly: tab[key].adminOnly - })).filter(tab => !tab.adminOnly || tab.adminOnly === false).forEach(tab => tabs.push(tab)); - }); - } - - this.setState({tabs: tabs}); + this.setState({ + tabs: determineTabs(this.props.tournament) + }); } onEventTapped = (evt) => { @@ -100,7 +160,7 @@ export default class TournamentInfoViewController extends MemeleeViewController return this.props.navigator.push({ screen: tab.screen, title: tab.name, - passProps: {data: []}, + passProps: {data: tab.data}, navigatorStyle: {tabBarHidden: true} }); @@ -146,7 +206,7 @@ export default class TournamentInfoViewController extends MemeleeViewController - {this.state.tabs.map(tab => this.handleTab(tab)} />)} + {this.state.tabs.map(tab => this.handleTab(tab)} />)} ) : null} diff --git a/controllers/components/TournamentRow.js b/controllers/components/TournamentRow.js index a6c6f70..86af2e1 100644 --- a/controllers/components/TournamentRow.js +++ b/controllers/components/TournamentRow.js @@ -34,7 +34,7 @@ export default class TournamentRow extends React.Component { - {this.props.tournament.hasOnlineEvents && (!this.props.tournament.city || this.props.tournament.city === '') ? 'Online' : (this.props.tournament.city ? this.props.tournament.city + ', ' : '') + this.props.tournament.addrState} + {this.props.tournament.hasOnlineEvents && (!this.props.tournament.city || this.props.tournament.city === '') ? 'Online' : (this.props.tournament.city ? this.props.tournament.city + ', ' : '') + (this.props.tournament.addrState ? this.props.tournament.addrState : '')} {this.props.tournament.memeleeEventsCount} Events diff --git a/ios/memelee.xcodeproj/project.xcworkspace/xcuserdata/laika.xcuserdatad/UserInterfaceState.xcuserstate b/ios/memelee.xcodeproj/project.xcworkspace/xcuserdata/laika.xcuserdatad/UserInterfaceState.xcuserstate index 04176fb..cb36ff7 100644 Binary files a/ios/memelee.xcodeproj/project.xcworkspace/xcuserdata/laika.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/memelee.xcodeproj/project.xcworkspace/xcuserdata/laika.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/package.json b/package.json index 1714b5b..dae0fa1 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,10 @@ "react-native": "0.52.0", "react-native-elements": "^0.19.0", "react-native-hyperlink": "0.0.12", + "react-native-maps": "^0.21.0", "react-native-markdown-renderer": "^3.1.0", "react-native-navigation": "^1.1.426", + "react-native-open-maps": "^0.1.4", "react-native-segmented-control-tab": "^3.2.2", "react-native-settings-list": "^1.8.0", "react-native-svg": "^6.3.1", diff --git a/stores/TournamentEventStore.js b/stores/TournamentEventStore.js index e44618b..d52dfd9 100644 --- a/stores/TournamentEventStore.js +++ b/stores/TournamentEventStore.js @@ -139,6 +139,10 @@ class Store { * * @arg bracketID {String or Number} ID for the bracket - SmashGG calls this a phase. Go fig. */ + +//https://smash.gg/api/-/gg_api./tournament/fighter-s-spirit-2018-1;eventSlug=street-fighter-v-arcade-edition-singles;expand=["event","phase","wave","station","stream","entrantCounts","tagsByEntity","ruleset"];mutations=["playerData","profileTaskNotifs"];phaseId=246410;reset=false;slug=fighter-s-spirit-2018-1?returnMeta=true + + fetchBracketData = (bracketID) => { this.bracketData = {winners: [], losers: []}; this.bracketID = bracketID; @@ -157,10 +161,15 @@ class Store { }; const url = api + Object.keys(args).map(key => `${key}=${args[key]}`).join(';') + '?returnMeta=true' - console.log(url); + //console.log(url); fetch( url ).then(response => response.json()).then(data => { + if(data.items.resultEntity && data.items.resultEntity === 'groups' && typeof data.items.entities.sets === 'undefined') { + this.parseAndStoreGroups(data); + return; + } + const grands = []; const brackets = { winners: [], @@ -176,13 +185,14 @@ class Store { result.entrant1 = {}; result.entrant2 = {}; - data.items.entities.entrants.forEach(function(entrant) { - if(entrant.id === result.entrant1Id) - result.entrant1 = entrant; + if(data.items.entities.entrants) + data.items.entities.entrants.forEach(function(entrant) { + if(entrant.id === result.entrant1Id) + result.entrant1 = entrant; - if(entrant.id === result.entrant2Id) - result.entrant2 = entrant; - }); + if(entrant.id === result.entrant2Id) + result.entrant2 = entrant; + }); if(result.isGF) { grands.push(result); @@ -208,13 +218,11 @@ class Store { // GFs are technically in the winners bracket, but for presentation purposes they're shoved // in after to be like how smash.gg presents them. if(grands.length > 0) { - grands.forEach(grandFinal => { - brackets.winners.push({ - title: 'Grand Finals', - sets: [grandFinal], - key: v4() - }); - }); + grands.forEach(grandFinal => brackets.winners.push({ + title: 'Grand Finals', + sets: [grandFinal], + key: v4() + })); } runInAction('Set Bracket Data', () => { @@ -222,6 +230,13 @@ class Store { }); }); } + + parseAndStoreGroups = (data) => { + console.warn('Groups!'); + runInAction('Set Groups Data', () => { + + }); + } }; export default new Store(); diff --git a/styles/index.js b/styles/index.js index 461503c..7a7c294 100644 --- a/styles/index.js +++ b/styles/index.js @@ -121,6 +121,37 @@ const stylesheet = StyleSheet.create({ textAlign: 'center', marginTop: 5, color: textColor + }, + + bracketTitle: { + fontWeight: 'bold', + padding: 5, + textAlign: 'center', + color: textColor + }, + + matchWrapper: { + backgroundColor: '#3b3b48', + borderColor: '#0e0e12', + borderWidth: 0.5, + borderRadius: 4, + marginBottom: 20, + overflow: 'hidden' + }, + + matchEntrant: { + flexDirection: 'row', + justifyContent: 'space-between' + }, + + matchEntrantText: { + color: '#dbdbde', + padding: 5 + }, + + matchSeparator: { + borderTopWidth: 0.5, + borderTopColor: '#2a2a33' } });