diff options
-rw-r--r-- | src/Actions/entity.ts | 79 | ||||
-rw-r--r-- | src/Api/api.ts | 16 | ||||
-rw-r--r-- | src/Api/interfaces.ts | 4 | ||||
-rw-r--r-- | src/Components/Login.tsx | 20 | ||||
-rw-r--r-- | src/Components/Map.tsx | 121 | ||||
-rw-r--r-- | src/Components/MapObjects.tsx | 114 | ||||
-rw-r--r-- | src/Components/MapOverlay.tsx | 130 | ||||
-rw-r--r-- | src/Components/PortalPanel.tsx | 50 | ||||
-rw-r--r-- | src/Components/Settings.tsx | 46 | ||||
-rw-r--r-- | src/Main.js | 30 | ||||
-rw-r--r-- | src/Store/store.ts | 14 | ||||
-rw-r--r-- | src/colors.ts | 1 |
12 files changed, 325 insertions, 300 deletions
diff --git a/src/Actions/entity.ts b/src/Actions/entity.ts index 632b505..f2161fc 100644 --- a/src/Actions/entity.ts +++ b/src/Actions/entity.ts @@ -9,55 +9,40 @@ import { decodePortal } from '../Api/entityDecoder'; const entity = { 'update': (region: Region, width: number, refresh: boolean) => async (dispatch, getStore) => { const store = getStore() - let tiles = getTilesToLoad(region, width) - if (!refresh) { - const loadedAlready = Object.keys(store.entities.portals) - tiles = tiles.filter(t => loadedAlready.indexOf(t) == -1) - } - const display = store.entities.display - dispatch(entity.loadTiles(tiles, display)) + const queue = store.entities.loadQueue + const loadedAlready = refresh ? + [ + ...Object.keys(store.entities.portals), + ...queue, + ] : [] + const tiles = getTilesToLoad(region, width).filter(t => loadedAlready.indexOf(t) == -1) + dispatch(entity.setLoadQueue([...queue, ...tiles])) + setImmediate(() => dispatch(entity.loadRutine())) }, - 'loadTiles': (tiles: string[], display: any) => async (dispatch, getStore) => { + 'loadRutine': () => async (dispatch, getStore) => { const store = getStore() + const queue = store.entities.loadQueue + console.log('1', queue.length) + const chunk = queue.splice(0, 25) + console.log('2', chunk.length, queue.length) const { v, csrf } = store.auth const params = { v, csrf } - dispatch(entity.entitiesLoad(tiles.length)) - setImmediate(() => loadTiles(tiles, params) - .then((loadedData: LoadedResult) => dispatch(entity.receiveTiles(loadedData, display)))) - }, - 'receiveTiles': (loadedData: LoadedResult, display: any) => async (dispatch) => { - dispatch(entity.portalsSet(loadedData.portals)) - dispatch(entity.linksSet(loadedData.links)) - dispatch(entity.fieldsSet(loadedData.fields)) - Object.keys(loadedData.portalsByTile).forEach(tile => { - loadedData.portalsByTile[tile].forEach(p => { - if (display.portals.indexOf(p) == -1) { - display.portals.push(p) + loadTiles(chunk, params) + .then(({ result, failed }) => { + const queue = store.entities.loadQueue + dispatch(entity.portalsSet(result.portals)) + dispatch(entity.linksSet(result.links)) + dispatch(entity.fieldsSet(result.fields)) + let newQueue = queue + if (failed.length > 0) { + newQueue = [...queue, ...failed] } - }) - }) - Object.keys(loadedData.linksByTile).forEach(tile => { - loadedData.linksByTile[tile].forEach(p => { - if (display.links.indexOf(p) == -1) { - display.links.push(p) - } - }) - }) - Object.keys(loadedData.fieldsByTile).forEach(tile => { - loadedData.fieldsByTile[tile].forEach(p => { - if (display.fields.indexOf(p) == -1) { - display.fields.push(p) + dispatch(entity.setLoadQueue([...newQueue])) + if (newQueue.length > 0) { + setImmediate(() => dispatch(entity.loadRutine())) } }) - }) - dispatch(entity.entitiesDisplay(display)) - if (loadedData.failedTiles.length > 0) { - setImmediate(() => { - dispatch(entity.loadTiles(loadedData.failedTiles, display)) - }) - } else { - dispatch(entity.entitiesLoad(0)) - } + }, 'getPortalDetails': (guid: string) => async (dispatch, getStore) => { const store = getStore() @@ -69,13 +54,9 @@ const entity = { dispatch(entity.portalSet(guid, portal)) }) }, - 'entitiesDisplay': (display: any) => ({ - type: 'entitiesDisplay', - display - }), - 'entitiesLoad': (loading: number) => ({ - type: 'entitiesLoad', - loading + 'setLoadQueue': (queue: string[][]) => ({ + type: 'setLoad', + queue }), 'portalsSet': (portals: { [guid: string]: Portal }) => ({ type: 'portalsSet', diff --git a/src/Api/api.ts b/src/Api/api.ts index 2da4594..4c83252 100644 --- a/src/Api/api.ts +++ b/src/Api/api.ts @@ -59,8 +59,7 @@ export const getTilesToLoad = (region: Region, width: number): string[] => { return tilesToLoad } -export const loadTiles = async (tilesToLoad: string[], params: RequestParams): Promise<LoadedResult> => { - const chunk = tilesToLoad.splice(0, 25) +export const loadTiles = async (tilesToLoad: string[], params: RequestParams): Promise<{ result: LoadedResult, failed: string[] }> => { const loadedData: LoadedResult = { fields: {}, links: {}, @@ -68,10 +67,9 @@ export const loadTiles = async (tilesToLoad: string[], params: RequestParams): P portalsByTile: {}, fieldsByTile: {}, linksByTile: {}, - failedTiles: tilesToLoad, - loaded: false, } - await getEntities(chunk, params) + const failed = [] + await getEntities(tilesToLoad, params) .then(loaded => { const result = loaded.result if (!result || !result['map']) { @@ -79,7 +77,7 @@ export const loadTiles = async (tilesToLoad: string[], params: RequestParams): P } return Object.keys(result['map']).map(tile => { if (result['map'][tile]['error'] || !result['map'][tile]['gameEntities']) { - loadedData.failedTiles.push(tile) + failed.push(tile) return true } if (!loadedData.portalsByTile[tile]) { @@ -114,9 +112,11 @@ export const loadTiles = async (tilesToLoad: string[], params: RequestParams): P }) }) }).catch(e => { - loadedData.failedTiles = [...loadedData.failedTiles, ...chunk] + tilesToLoad.forEach(element => { + failed.push(element) + }); }) - return loadedData + return { result: loadedData, failed } } const getBoundingBox = (region: Region): BoundingBox => { diff --git a/src/Api/interfaces.ts b/src/Api/interfaces.ts index 779fcf1..0dcafc0 100644 --- a/src/Api/interfaces.ts +++ b/src/Api/interfaces.ts @@ -32,7 +32,6 @@ export interface GetPortalResponse { } export interface LoadedResult { - loaded: boolean, portalsByTile: { [tile: string]: string[] }, @@ -50,8 +49,7 @@ export interface LoadedResult { }, fields: { [guid: string]: Field - }, - failedTiles: string[] + } } diff --git a/src/Components/Login.tsx b/src/Components/Login.tsx index 6fe7957..fe20e09 100644 --- a/src/Components/Login.tsx +++ b/src/Components/Login.tsx @@ -8,20 +8,13 @@ import actions from '../Actions/actions'; const LOGIN_URL = "https://accounts.google.com/ServiceLogin?service=ah&passive=true&continue=https://appengine.google.com/_ah/conflogin%3Fcontinue%3Dhttps://intel.ingress.com/"; const HOME_URL = "https://intel.ingress.com/"; -var styles = StyleSheet.create({ - container: { - flexGrow: 1, - } -}); - type Props = { - login(csrf: string, v: string): void + login(): void actions: any auth: any } type State = { - v: string onIngress: boolean } @@ -29,7 +22,7 @@ class Login extends Component<Props, State> { webview!: WebView; constructor(props: Props) { super(props); - this.state = { v: "", onIngress: false } + this.state = { onIngress: false } } onNavigationStateChange(navState: WebViewNavigation) { @@ -47,7 +40,7 @@ class Login extends Component<Props, State> { <> <WebView ref={r => r && (this.webview = r)} - automaticallyAdjustContentInsets={false} + automaticallyAdjustContentInsets={true} thirdPartyCookiesEnabled useWebKit style={styles.container} @@ -68,4 +61,11 @@ class Login extends Component<Props, State> { } } +var styles = StyleSheet.create({ + container: { + flex: 1, + } +}); + + export default connect({ 'auth': 'auth' }, actions)(Login)
\ No newline at end of file diff --git a/src/Components/Map.tsx b/src/Components/Map.tsx index 3e878f8..68698f4 100644 --- a/src/Components/Map.tsx +++ b/src/Components/Map.tsx @@ -20,67 +20,64 @@ const draggableRange = { type Props = any type State = any class Map extends Component<Props, State> { - refresh: number | undefined + static navigationOptions = ({ navigation }) => { + return { + title: 'Карта', + }; + }; + refreshTimer: number | undefined map!: MapView; constructor(props: Props) { super(props) this.state = { user: undefined, - followUser: true, - zoom: 15, region: null, - loading: false, - objects: { links: {}, fields: {}, portals: {}, loaded: false }, } } + componentDidMount() { + this.refreshTimer = setInterval(() => { + this.refresh() + }, 30000) + } + componentWillUnmount() { + clearInterval(this.refreshTimer) + } componentWillMount() { + const setPosition = (position) => { + this.setState({ + user: { + latitude: position.coords.latitude, + longitude: position.coords.longitude + }, + }); + } navigator.geolocation.getCurrentPosition( - position => { - this.setState({ - followUser: true, - }); - }, + setPosition, error => alert(error.message), { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 } ); navigator.geolocation.watchPosition( - position => { - this.setState({ - user: { - latitude: position.coords.latitude, - longitude: position.coords.longitude - }, - }); - }, + setPosition, error => alert(error.message), { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 } ) } onRegionChange = (region: Region) => { - const zoom = this.getZoomByRegion(width, region) - this.setState({ region, zoom }) + this.setState({ region }) setImmediate(() => this.load(false)) } - getZoomByRegion(width: number, region: Region): number { - return Math.ceil(Math.log2(360 * ((width / 256) / region.longitudeDelta))) + 1 + + refresh = () => { + setImmediate(() => this.load(true)) } + load = async (refresh: boolean) => { - if (this.state.region != null && this.props.entities.loading == 0) { + if (this.state.region != null) { this.props.actions.update(this.state.region, width, refresh) } return null } - refreshByTime = () => { - setTimeout(() => { - this.load(true).then(this.refreshByTime) - }, 30000) - } - - componentDidMount() { - this.refreshByTime() - } - onPortalClick = (guid: string, coords: LatLng) => { this.setState({ selectedPortal: { guid, coords } }) } @@ -93,23 +90,15 @@ class Map extends Component<Props, State> { this.props.navigation.dispatch(navigateAction); } + goToMe = () => { + this.map.animateToCoordinate(this.state.user) + } + render() { if (!this.state.user) { return <View style={styles.spinnerContainer}><ActivityIndicator size={'large'} /></View> } - const camera = { - center: this.state.user, - altitude: 1000, - heading: 0, - pitch: 30, - zoom: 15, - } - const goToMe = () => { - this.map.animateToCoordinate(this.state.user) - } - const refresh = () => { - setImmediate(() => this.load(true)) - } + return ( <> <MapView @@ -124,13 +113,13 @@ class Map extends Component<Props, State> { loadingEnabled type={'hybrid'} > - <MapObjects onPortalClick={this.onPortalClick} zoom={this.state.zoom} /> + <MapObjects onPortalClick={this.onPortalClick} /> {this.state.selectedPortal && <Marker coordinate={this.state.selectedPortal.coords} />} </MapView> <MapOverlay - goToMe={goToMe} - refresh={refresh} - loading={this.props.entities.loading} + goToMe={this.goToMe} + refresh={this.refresh} + loading={this.props.entities.loadQueue.length} selectedPortal={this.state.selectedPortal} onOpenPortal={this.onOpenPortal} /> @@ -143,38 +132,6 @@ const styles = StyleSheet.create({ container: { flex: 1, }, - spinnerContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - panel: { - flex: 1, - backgroundColor: '#fff', - position: 'relative', - borderTopLeftRadius: 20, - borderTopRightRadius: 20, - //ios - shadowOpacity: 0.3, - shadowRadius: 3, - shadowOffset: { - height: 0, - width: 0 - }, - //android - elevation: 1 - }, - panelHeader: { - height: 40, - alignItems: 'center', - justifyContent: 'center', - }, - dash: { - backgroundColor: 'rgba(200,200,200,0.9)', - height: 6, - width: 30, - borderRadius: 3 - } }); export default connect({ 'entities': 'entities' }, actions)(Map)
\ No newline at end of file diff --git a/src/Components/MapObjects.tsx b/src/Components/MapObjects.tsx index 1d2fb12..af0afec 100644 --- a/src/Components/MapObjects.tsx +++ b/src/Components/MapObjects.tsx @@ -1,4 +1,4 @@ -import React, { ReactChild } from 'react'; +import React, { PureComponent } from 'react'; import { View, StyleSheet, Text } from 'react-native'; import { Polyline, Polygon, Marker, Region } from 'react-native-maps'; import Icon from 'react-native-vector-icons/FontAwesome'; @@ -17,74 +17,64 @@ type Props = { links: { [guid: string]: Link } fields: { [guid: string]: Field } onPortalClick: Function - zoom: number } -const MapObjects = (props: Props) => { - const renderPortal = Object.keys(props.portals).map(guid => drawPortal(guid, props.portals[guid], props.zoom, props.onPortalClick)) - const renderField = Object.keys(props.fields).map(guid => drawField(guid, props.fields[guid])) - const renderLink = Object.keys(props.links).map(guid => drawLink(guid, props.links[guid])) +class MapObjects extends PureComponent<Props> { + render() { + const props = this.props + const renderPortal = Object.keys(props.portals).map(guid => this.drawPortal(guid, props.portals[guid], props.onPortalClick)) + const renderField = Object.keys(props.fields).map(guid => this.drawField(guid, props.fields[guid])) + const renderLink = Object.keys(props.links).map(guid => this.drawLink(guid, props.links[guid])) - if (props.zoom <= 14) { - return [...renderField, ...renderLink] - } else { - return [...renderField, ...renderLink, ...renderPortal] + if (props.zoom <= 14) { + return [...renderField, ...renderLink] + } else { + return [...renderField, ...renderLink, ...renderPortal] + } + } + drawPortal = (guid: string, entity: Portal, onPortalClick: Function) => { + const size = 20 + return (<Marker + key={guid} + coordinate={entity.coords} + onPress={() => onPortalClick(guid, entity.coords)} + > + {/* <Icon name={entity.fraction == "N" ? "circle-o" : "circle"} color={fillPortalColor[entity.fraction]} size={size} /> */} + <View style={{ + borderWidth: 2, + height: size, + width: size, + borderRadius: size / 2, + borderColor: COLORS_LVL[entity.level], + backgroundColor: fillPortalColor[entity.fraction], + justifyContent: 'center', + alignItems: 'center', + }}> + <Text style={{ fontWeight: 'bold' }}>{entity.level}</Text> + </View> + </Marker>); + } + drawField = (guid: string, entity: Field) => { + return <Polygon + key={guid} + coordinates={entity.coords} + fillColor={fieldColor[entity.fraction]} + strokeColor={fieldColor[entity.fraction]} + strokeWidth={StyleSheet.hairlineWidth} + /> + } + drawLink = (guid: string, entity: Link) => { + return <Polyline + key={guid} + coordinates={entity.coords} + strokeColor={fractColor[entity.fraction]} + strokeWidth={1} + /> } -} -const drawPortal = (guid: string, entity: Portal, zoom: number, onPortalClick: Function) => { - const size = (zoom) * 1.5 - return (<Marker - key={guid} - coordinate={entity.coords} - onPress={() => onPortalClick(guid, entity.coords)} - > - {/* <Icon name={entity.fraction == "N" ? "circle-o" : "circle"} color={fillPortalColor[entity.fraction]} size={size} /> */} - <View style={{ - borderWidth: 2, - height: size, - width: size, - borderRadius: size / 2, - borderColor: COLORS_LVL[entity.level], - backgroundColor: fillPortalColor[entity.fraction], - justifyContent: 'center', - alignItems: 'center', - }}> - <Text style={{ fontWeight: 'bold' }}>{entity.level}</Text> - </View> - </Marker>); -} -const drawField = (guid: string, entity: Field) => { - return <Polygon - key={guid} - coordinates={entity.coords} - fillColor={fieldColor[entity.fraction]} - strokeColor={fieldColor[entity.fraction]} - strokeWidth={StyleSheet.hairlineWidth} - /> -} -const drawLink = (guid: string, entity: Link) => { - return <Polyline - key={guid} - coordinates={entity.coords} - strokeColor={fractColor[entity.fraction]} - strokeWidth={1} - /> } - const styles = StyleSheet.create({ }); -export default connect((store) => { - const display = store.entities.display - const portals = {} - const fields = {} - const links = {} - display.portals.forEach(guid => { portals[guid] = store.entities.portals[guid] }) - display.fields.forEach(guid => { fields[guid] = store.entities.fields[guid] }) - display.links.forEach(guid => { links[guid] = store.entities.links[guid] }) - return { - portals, fields, links - } -}, {})(MapObjects)
\ No newline at end of file +export default connect((store) => ({ portals: store.entities.portals, fields: store.entities.fields, links: store.entities.links }), {})(MapObjects)
\ No newline at end of file diff --git a/src/Components/MapOverlay.tsx b/src/Components/MapOverlay.tsx index 30160c9..faf484e 100644 --- a/src/Components/MapOverlay.tsx +++ b/src/Components/MapOverlay.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { StyleSheet, View, Text, GestureResponderEvent, ActivityIndicator, Linking } from 'react-native'; import { FontAwesome } from '@expo/vector-icons'; -import { getStatusBarHeight } from '../helper'; +import { getStatusBarHeight, getBottomSpace } from '../helper'; import { LatLng } from '../Api/interfaces'; type Props = { @@ -13,72 +13,88 @@ type Props = { } export default (props: Props) => ( - <View style={styles.overlay}> - <View style={styles.button}> - <FontAwesome.Button - name={'crosshairs'} - onPress={props.goToMe} - iconStyle={styles.icon} - color={"#000"} - size={24} - backgroundColor={"#e3e3e3"} - /> - </View> - <View style={styles.button}> - <FontAwesome.Button - name={'refresh'} - onPress={props.refresh} - iconStyle={styles.icon} - color={"#000"} - size={24} - backgroundColor={"#e3e3e3"} - /> - </View> - {props.selectedPortal ? ( - <> - <View style={styles.button}> - <FontAwesome.Button - name={'info'} - onPress={() => { - props.onOpenPortal(props.selectedPortal.guid, props.selectedPortal.coords) - }} - iconStyle={styles.icon} - color={"#000"} - size={24} - backgroundColor={"#e3e3e3"} - /> - </View> - <View style={styles.button}> - <FontAwesome.Button - name={'location-arrow'} - onPress={() => Linking.openURL(`geo:${props.selectedPortal.coords.latitude},${props.selectedPortal.coords.longitude}`)} - iconStyle={styles.icon} - color={"#000"} - size={24} - backgroundColor={"#e3e3e3"} - /> + <> + <View style={styles.overlayLeft}> + {props.loading > 0 ? ( + <View style={styles.loader}> + <ActivityIndicator color={"#000"} /> </View> - </> - ) : null} - - <View style={styles.button}> - <Text>{props.loading}</Text> + ) : null} + {props.selectedPortal ? ( + <> + <View style={styles.buttonWrapper}> + <FontAwesome.Button + style={styles.button} + name={'info'} + onPress={() => { + props.onOpenPortal(props.selectedPortal.guid, props.selectedPortal.coords) + }} + iconStyle={styles.icon} + color={"#000"} + size={25} + backgroundColor={"#e3e3e3"} + /> + </View> + <View style={styles.buttonWrapper}> + <FontAwesome.Button + style={styles.button} + name={'location-arrow'} + onPress={() => Linking.openURL(`geo:${props.selectedPortal.coords.latitude},${props.selectedPortal.coords.longitude}`)} + iconStyle={styles.icon} + color={"#000"} + size={25} + backgroundColor={"#e3e3e3"} + /> + </View> + </> + ) : null} </View> - {props.loading > 0 ? ( - <View style={styles.loader}> - <ActivityIndicator color={"#000"} /> + + <View style={styles.overlayRight}> + <View style={styles.buttonWrapper}> + <FontAwesome.Button + style={styles.button} + name={'crosshairs'} + onPress={props.goToMe} + iconStyle={styles.icon} + color={"#000"} + size={25} + backgroundColor={"#e3e3e3"} + /> </View> - ) : null} - </View> + <View style={styles.buttonWrapper}> + <FontAwesome.Button + style={styles.button} + name={'refresh'} + onPress={props.refresh} + iconStyle={styles.icon} + color={"#000"} + size={25} + backgroundColor={"#e3e3e3"} + /> + </View> + </View> + </> ); const styles = StyleSheet.create({ - overlay: { + overlayLeft: { position: 'absolute', - top: 8 + getStatusBarHeight(true), + bottom: getBottomSpace(), + left: 8, + }, + overlayRight: { + position: 'absolute', + bottom: getBottomSpace(), right: 8, }, button: { + justifyContent: 'center', + alignItems: 'center', + height: 48, + width: 48, + }, + buttonWrapper: { margin: 8, }, icon: { diff --git a/src/Components/PortalPanel.tsx b/src/Components/PortalPanel.tsx index 6cf19c2..d49d88a 100644 --- a/src/Components/PortalPanel.tsx +++ b/src/Components/PortalPanel.tsx @@ -1,12 +1,12 @@ import React, { Component, PureComponent } from 'react'; -import { StyleSheet, View, Text, GestureResponderEvent, ActivityIndicator } from 'react-native'; +import { StyleSheet, View, Text, GestureResponderEvent, ActivityIndicator, Image } from 'react-native'; // import { Button } from 'react-native-vector-icons/FontAwesome'; -import Reactotron from 'reactotron-react-native' import { getStatusBarHeight } from '../helper'; import { connect } from 'react-redux'; import actions from '../Actions/actions'; import { Portal } from '../Api/types'; import { bindActionCreators } from 'redux'; +import { COLORS_FRACTION } from '../colors'; type Props = { guid: string @@ -16,18 +16,14 @@ type Props = { class PortalPanel extends PureComponent<Props> { static navigationOptions = ({ navigation }) => { return { - title: navigation.getParam('title', 'Загрузка...'), + title: 'Информация о портале', }; - }; - componentWillMount() { - this.props.navigation.setParams({title: this.props.portal.name}) - } + }; componentDidMount() { this.props.getPortalDetails(this.props.guid) } componentWillReceiveProps(next: Props) { if (next.guid != this.props.guid) { - this.props.navigation.setParams({title: this.props.portal.name}) this.props.getPortalDetails(next.guid) } } @@ -38,9 +34,28 @@ class PortalPanel extends PureComponent<Props> { } return ( <View style={styles.overlay}> - <Text style={styles.title}>{portal.name}</Text> - <Text style={styles.subtitle}>Уровeнь: {portal.level}, здоровье: {portal.power}</Text> - <Text>{JSON.stringify(this.props)}</Text> + <View style={styles.panelRow}> + <Image style={styles.photo} source={{ uri: portal.photo }} /> + <View style={styles.panelRight}> + <Text style={[styles.title, { color: COLORS_FRACTION[portal.fraction] }]}>{portal.name}</Text> + <Text style={styles.subtitle}>Уровeнь: {portal.level}, здоровье: {portal.power}</Text> + <Text style={styles.subtitle}>Владелец: <Text style={[styles.user, { color: COLORS_FRACTION[portal.fraction] }]}>{portal.owner || 'нет'}</Text></Text> + <Text style={styles.subtitle}>Дата: {portal.timestamp && (new Date(portal.timestamp)).toLocaleString()}</Text> + </View> + </View> + <Text style={styles.title}>Резонаторы</Text> + <View> + {portal.resonators && portal.resonators.map((r, idx) => + r ? (<Text style={styles.subtitle}>{idx + 1}: {r[1]} [{r[2]}] - {r[0]}</Text>) : (<Text style={styles.subtitle}>{idx + 1}: нет</Text>) + )} + {(!portal.resonators || portal.resonators.length == 0) && (<Text style={styles.subtitle}>нет</Text>)} + </View> + <Text style={styles.title}>Моды</Text> + <View> + {portal.mods && portal.mods.map((r, idx) => + r ? (<Text style={styles.subtitle}>{idx + 1}: {r[1]} [{r[2]}] - {r[0]}</Text>) : (<Text style={styles.subtitle}>{idx + 1}: нет</Text>) + )} + </View> </View> ); } @@ -59,6 +74,12 @@ const styles = StyleSheet.create({ flex: 1, padding: 8, }, + panelRow: { + flexDirection: 'row', + }, + panelRight: { + paddingLeft: 8 + }, title: { fontWeight: 'bold', fontSize: 22, @@ -66,5 +87,12 @@ const styles = StyleSheet.create({ subtitle: { fontWeight: 'normal', fontSize: 18, + }, + user: { + fontWeight: 'bold', + }, + photo: { + width: 100, + height: 195, } });
\ No newline at end of file diff --git a/src/Components/Settings.tsx b/src/Components/Settings.tsx new file mode 100644 index 0000000..daf3216 --- /dev/null +++ b/src/Components/Settings.tsx @@ -0,0 +1,46 @@ +import React, { Component, PureComponent } from 'react'; +import { StyleSheet, View, Text, GestureResponderEvent, ActivityIndicator } from 'react-native'; +// import { Button } from 'react-native-vector-icons/FontAwesome'; +import { getStatusBarHeight } from '../helper'; +import { connect } from 'react-redux'; +import actions from '../Actions/actions'; +import { Portal } from '../Api/types'; +import { bindActionCreators } from 'redux'; + +type Props = { + guid: string + portal?: Portal +} + +class Settings extends PureComponent<Props> { + static navigationOptions = ({ navigation }) => { + return { + title: 'Настройки', + }; + }; + + render() { + return ( + <View style={styles.overlay}> + <Text>{JSON.stringify(this.props)}</Text> + </View> + ); + } +} + +export default connect((store) => ({}), (dispatch) => bindActionCreators(actions, dispatch))(Settings) + +const styles = StyleSheet.create({ + overlay: { + flex: 1, + padding: 8, + }, + title: { + fontWeight: 'bold', + fontSize: 22, + }, + subtitle: { + fontWeight: 'normal', + fontSize: 18, + } +});
\ No newline at end of file diff --git a/src/Main.js b/src/Main.js index 5a67c64..4e5c128 100644 --- a/src/Main.js +++ b/src/Main.js @@ -1,12 +1,14 @@ import React, { Component } from 'react'; -import { Platform, StyleSheet, Text, View } from 'react-native'; +import { View } from 'react-native'; import { connect } from 'redux-su'; +import IconComponent from 'react-native-vector-icons/Ionicons'; import Login from './Components/Login'; import Map from './Components/Map'; import PortalPanel from './Components/PortalPanel'; -import { createStackNavigator, createAppContainer } from 'react-navigation'; +import { createStackNavigator, createBottomTabNavigator, createAppContainer } from 'react-navigation'; +import Settings from './Components/Settings'; -const AppNavigator = createStackNavigator({ +const MapNavigator = createStackNavigator({ Map: { screen: Map, navigationOptions: { @@ -15,12 +17,26 @@ const AppNavigator = createStackNavigator({ }, Portal: { screen: PortalPanel, - navigationOptions: () => ({ - headerMode: 'float', - headerBackTitle: null - }) }, }); + +const AppNavigator = createBottomTabNavigator({ + Map: { + screen: MapNavigator, + navigationOptions: { + tabBarLabel: 'Карта', + tabBarIcon: ({tintColor}) => <IconComponent name={'ios-map'} size={25} color={tintColor} /> + } + }, + Settings: { + screen: Settings, + navigationOptions: { + tabBarLabel: 'Настройки', + tabBarIcon: ({tintColor}) => <IconComponent name={'ios-settings'} size={25} color={tintColor} /> + } + } +}) + const AppContainer = createAppContainer(AppNavigator) class App extends Component { render() { diff --git a/src/Store/store.ts b/src/Store/store.ts index 0929882..c26f019 100644 --- a/src/Store/store.ts +++ b/src/Store/store.ts @@ -4,12 +4,6 @@ import { createReducer } from 'redux-su'; import { composeWithDevTools } from 'redux-devtools-extension'; import { Portal, Link, Field } from '../Api/types'; -type Display = { - portals: string[], - fields: string[], - links: string[] -} - const reducers = { 'auth': createReducer({ 'authSet': (store: any, action: { v: string, csrf: string, user: any }) => ({ v: action.v, csrf: action.csrf, user: action.user, loading: false }), @@ -39,12 +33,10 @@ const reducers = { ({ ...store, links: { ...store.links, ...action.links } }), 'fieldsSet': (store: any, action: { fields: { [guid: string]: Field } }) => ({ ...store, fields: { ...store.fields, ...action.fields } }), - 'entitiesLoad': (store: any, action: { loading: number }) => - ({ ...store, loading: action.loading }), - 'entitiesDisplay': (store: any, action: { display: Display }) => - ({ ...store, display: action.display }), + 'setLoad': (store: any, action: { queue: string[][] }) => + ({ ...store, loadQueue: [...action.queue] }), - }, { portals: {}, fields: {}, links: {}, loading: 0, display: { portals: [], fields: [], links: [] } }) + }, { portals: {}, fields: {}, links: {}, loadQueue: [] }) } function extend(obj: any, src: any) { diff --git a/src/colors.ts b/src/colors.ts index 13b5e4f..8a3a508 100644 --- a/src/colors.ts +++ b/src/colors.ts @@ -1,3 +1,4 @@ export const COLORS = ['#FF6600', '#0088FF', '#03DC03']; // none, res, enl +export const COLORS_FRACTION = { 'N': COLORS[0], 'R': COLORS[1], 'E': COLORS[2] }; // none, res, enl export const COLORS_LVL = ['#000', '#FECE5A', '#FFA630', '#FF7315', '#E40000', '#FD2992', '#EB26CD', '#C124E0', '#9627F4']; export const COLORS_MOD = { VERY_RARE: '#b08cff', RARE: '#73a8ff', COMMON: '#8cffbf' };
\ No newline at end of file |