-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Show large version of trash photos when a thumbnail is clicked/tapped. #142
base: master
Are you sure you want to change the base?
Changes from all commits
1c6f0d7
a03443e
20c8ddc
8abdc6c
945644b
fa15cb6
6783ac0
7582327
4a76e2c
91330ee
dd4608f
63869db
d9b1330
29c9d0a
d95b316
e77dc1b
77bf5fc
3af2039
d96dbd4
8309b29
f6ca72f
8133359
ce90a2c
45c0002
1186663
68d13d1
537bc0d
679e2b7
8622948
eb30fcb
bcfc76a
7912558
a40e768
29fa20d
6c1a0d6
ca4df71
726522d
22407b2
14dc65a
2ed5e84
d180ed8
4fce1bc
2fe340d
0df5af3
2a4ed94
cd99423
ed100d7
748f50f
eecf9c8
85fb587
93c1d2e
17556e8
6f8a17c
a867aec
ea8d2cc
30ff9d3
a498b36
744ea98
b2d8b92
437ba00
894010c
a23ec1c
1fd3b9f
93174d3
b63cf08
df4a91a
68df76b
c09468c
5586f6f
af1dc52
203b379
85ed0ff
d861a3d
d181d23
6720690
54d14ef
5590148
f551bfb
2f87be8
ede1588
a855955
9c21d5d
435dfcc
40d42d4
0cf7954
c05ddb3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Getting started | ||
|
||
This document will help you set up the project for development. | ||
|
||
## Basic dependencies | ||
Install git, docker, docker-compose, node and yarn. | ||
|
||
|
||
## Backend services | ||
Consult the dedicated [readme](devops/dev/README.md) to set them up, run | ||
them and verify they work. | ||
|
||
### Adding dummy data | ||
This repository comes without trashpoint data. You will need some to work | ||
on the related APIS or marker related functionality. | ||
|
||
**TODO:** expand with wcd_dummy instructions | ||
|
||
|
||
## Web-app (admin backend) | ||
Consult the dedicated [readme](web-app/README.md), but in essence: | ||
|
||
```bash | ||
npm install # the first time | ||
npm start | ||
``` | ||
|
||
## Mobile-apps | ||
Consult the dedicated [readme](mobile-app/README.md). | ||
|
||
|
||
## Extra reading | ||
|
||
* [Contributing](./CONTRIBUTING.md) to World Cleanup Day App and Platform | ||
* [Code of Conduct](./CODE_OF_CONDUCT.md) | ||
* [Governance](./GOVERNANCE.md) | ||
* [Roadmap](./ROADMAP.md) | ||
* Issues marked [good first issue](https://github.com/letsdoitworld/World-Cleanup-Day/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { | ||
Image, | ||
Modal, | ||
TouchableOpacity, | ||
} from 'react-native'; | ||
import { Ionicons } from '@expo/vector-icons'; | ||
|
||
import styles from './styles'; | ||
|
||
const PhotoModal = ({ visible, onRequestClose, photoUrl }) => { | ||
return ( | ||
<Modal | ||
visible={visible} | ||
onRequestClose={onRequestClose} | ||
style={styles.modal} | ||
> | ||
<Image | ||
style={{ | ||
flex: 1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. extract to consts |
||
}} | ||
resizeMode="contain" | ||
source={{ uri: photoUrl }} | ||
> | ||
<TouchableOpacity | ||
onPress={onRequestClose} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this.onRequestClose.bind(this) |
||
style={styles.buttonBody} | ||
> | ||
<Ionicons | ||
size={styles.$iconSize} | ||
name="md-close" | ||
style={styles.buttonIcon} | ||
/> | ||
</TouchableOpacity> | ||
</Image> | ||
</Modal> | ||
); | ||
}; | ||
|
||
PhotoModal.propTypes = { | ||
visible: PropTypes.bool.isRequired, | ||
onRequestClose: PropTypes.func.isRequired, | ||
photoUrl: PropTypes.string.isRequired, | ||
}; | ||
|
||
export default PhotoModal; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as PhotoModal } from './PhotoModal.js'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import EStyleSheet from 'react-native-extended-stylesheet'; | ||
import { getHeightPercentage, getWidthPercentage } from '../../shared/helpers'; | ||
|
||
const styles = EStyleSheet.create({ | ||
$iconSize: getWidthPercentage(20), | ||
buttonIcon: { | ||
textAlign: 'center', | ||
color: 'white', | ||
}, | ||
buttonBody: { | ||
width: getWidthPercentage(30), | ||
height: getHeightPercentage(30), | ||
marginLeft: getWidthPercentage(265), | ||
marginTop: getHeightPercentage(20), | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
backgroundColor: '#fe6669', | ||
borderRadius: 4, | ||
}, | ||
modal: { | ||
backgroundColor: '#d8d8d8', | ||
}, | ||
}); | ||
|
||
export default styles; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,16 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Image, View, Text, TouchableOpacity, ScrollView } from 'react-native'; | ||
import { | ||
View, | ||
Text, | ||
TouchableOpacity, | ||
ScrollView, | ||
} from 'react-native'; | ||
import { Ionicons } from '@expo/vector-icons'; | ||
import _ from 'lodash'; | ||
import { translate } from 'react-i18next'; | ||
|
||
import { AlertModal } from '../AlertModal'; | ||
import { PhotoModal } from '../PhotoModal'; | ||
import { LazyImage } from './components/LazyImage'; | ||
|
||
import styles from './styles'; | ||
|
@@ -35,6 +40,7 @@ class Photo extends React.Component { | |
super(props); | ||
this.state = { | ||
showingConfirm: false, | ||
showZoomedPhoto: false, | ||
}; | ||
|
||
this.buttons = [ | ||
|
@@ -49,57 +55,92 @@ class Photo extends React.Component { | |
}, | ||
]; | ||
} | ||
|
||
setConfirmState = (showingConfirm) => { | ||
this.setState({ | ||
showingConfirm, | ||
}); | ||
}; | ||
} | ||
|
||
handlePhotoDeletePress = () => { | ||
this.setConfirmState(true); | ||
}; | ||
} | ||
|
||
handleModalClosed = () => { | ||
this.setConfirmState(false); | ||
}; | ||
} | ||
|
||
handleModalConfirmed = () => { | ||
this.setConfirmState(false); | ||
this.props.onPress(); | ||
}; | ||
this.props.onDeletePress(); | ||
} | ||
|
||
openZoomedPhoto = () => { | ||
const { photo } = this.props; | ||
this.setState({ | ||
zoomedPhotoUrl: photo.mediumPhotoUrl, | ||
showZoomedPhoto: true, | ||
}); | ||
} | ||
|
||
closeZoomedPhoto = () => { | ||
this.setState({ | ||
showZoomedPhoto: false, | ||
}); | ||
} | ||
|
||
render() { | ||
const { photo, onPress } = this.props; | ||
const { photo, onDeletePress } = this.props; | ||
const { showingConfirm } = this.state; | ||
return ( | ||
<LazyImage key={photo} style={[styles.photo]} source={{ uri: photo }}> | ||
<View> | ||
{onPress && | ||
<TouchableOpacity | ||
onPress={this.handlePhotoDeletePress} | ||
style={styles.photoButtonContainer} | ||
> | ||
<Ionicons | ||
size={styles.$photoSize} | ||
name="md-close" | ||
style={styles.photoButton} | ||
/> | ||
</TouchableOpacity>} | ||
|
||
<AlertModal | ||
visible={showingConfirm} | ||
buttons={this.buttons} | ||
onOverlayPress={this.handleModalClosed} | ||
title={this.props.t('label_delete_photo_title')} | ||
subtitle={this.props.t('label_delete_photo_subtitle')} | ||
<TouchableOpacity onPress={this.openZoomedPhoto}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this.openZoomedPhoto.bind(this) to prevent losing of this |
||
<LazyImage | ||
key={photo} | ||
style={[styles.photo]} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need to create an array whenever the render() method is invoked |
||
source={{ uri: photo.thumbnailUrl }} | ||
> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use "Reformat code" option |
||
<PhotoModal | ||
visible={this.state.showZoomedPhoto} | ||
onRequestClose={this.closeZoomedPhoto} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. .bind(this) |
||
photoUrl={this.state.zoomedPhotoUrl} | ||
/> | ||
</View> | ||
</LazyImage> | ||
<View> | ||
{onDeletePress && | ||
<TouchableOpacity | ||
onPress={this.handlePhotoDeletePress} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. .bind(this) |
||
style={styles.photoButtonContainer} | ||
> | ||
<Ionicons | ||
size={styles.$photoSize} | ||
name="md-close" | ||
style={styles.photoButton} | ||
/> | ||
</TouchableOpacity>} | ||
|
||
<AlertModal | ||
visible={showingConfirm} | ||
buttons={this.buttons} | ||
onOverlayPress={this.handleModalClosed} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. .bind(this) |
||
title={this.props.t('label_delete_photo_title')} | ||
subtitle={this.props.t('label_delete_photo_subtitle')} | ||
/> | ||
</View> | ||
</LazyImage> | ||
</TouchableOpacity> | ||
); | ||
} | ||
} | ||
|
||
Photo.defaultProps = { | ||
onPress: undefined, | ||
onDeletePress: undefined, | ||
}; | ||
|
||
Photo.propTypes = { | ||
photo: PropTypes.string.isRequired, | ||
onPress: PropTypes.func, | ||
photo: PropTypes.shape({ | ||
thumbnailUrl: PropTypes.string.isRequired, | ||
mediumPhotoUrl: PropTypes.string.isRequired, | ||
}).isRequired, | ||
onDeletePress: PropTypes.func, | ||
}; | ||
|
||
const PhotoComponent = translate()(Photo); | ||
|
@@ -131,15 +172,15 @@ const PhotoPicker = ({ | |
style={styles.photoContainer} | ||
> | ||
{hasPhotos && | ||
photos.map((uri, index) => { | ||
photos.map((photo, index) => { | ||
const onDeletePhotoPress = hasDelete | ||
? () => onDeletePress(index) | ||
: undefined; | ||
return ( | ||
<PhotoComponent | ||
key={uri} | ||
photo={uri} | ||
onPress={onDeletePhotoPress} | ||
key={photo.thumbnailUrl} | ||
photo={photo} | ||
onDeletePress={onDeletePhotoPress} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this.onDeletePhotoPress.bind(this) |
||
/> | ||
); | ||
})} | ||
|
@@ -152,15 +193,21 @@ const PhotoPicker = ({ | |
</View> | ||
); | ||
}; | ||
|
||
PhotoPicker.defaultProps = { | ||
maxPhotos: undefined, | ||
onDeletePress: undefined, | ||
onAddPress: undefined, | ||
}; | ||
|
||
PhotoPicker.propTypes = { | ||
photos: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, | ||
photos: PropTypes.arrayOf(PropTypes.shape({ | ||
thumbnailUrl: PropTypes.string.isRequired, | ||
mediumPhotoUrl: PropTypes.string.isRequired, | ||
}).isRequired).isRequired, | ||
onDeletePress: PropTypes.func, | ||
onAddPress: PropTypes.func, | ||
maxPhotos: PropTypes.number, | ||
}; | ||
|
||
export default translate()(PhotoPicker); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, use "Reformat code" option