Skip to content

Commit

Permalink
feat: contact card with sharing buttons in settings > my account
Browse files Browse the repository at this point in the history
  • Loading branch information
Guillaume Louvigny committed Dec 18, 2018
1 parent 0705711 commit 494b4a6
Show file tree
Hide file tree
Showing 17 changed files with 450 additions and 53 deletions.
1 change: 1 addition & 0 deletions client/react-native/android/app/build.gradle
Expand Up @@ -168,6 +168,7 @@ android {
dependencies {
compile project(':lottie-react-native')
compile project(':react-native-vector-icons')
compile project(':react-native-view-shot')
compile project(':react-native-svg')
compile project(':react-native-restart')
compile project(':react-native-network-info')
Expand Down
Expand Up @@ -11,6 +11,7 @@
import com.imagepicker.ImagePickerPackage;
import com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage;
import org.reactnative.camera.RNCameraPackage;
import fr.greweb.reactnativeviewshot.RNViewShotPackage;
import org.reactnative.camera.RNCameraPackage;
import com.horcrux.svg.SvgPackage;
import com.avishayil.rnrestart.ReactNativeRestartPackage;
Expand Down Expand Up @@ -44,7 +45,8 @@ protected List<ReactPackage> getPackages() {
new CorePackage(),
new ImagePickerPackage(),
new MainReactPackage(),
new LottiePackage(),
new LottiePackage(),
new RNViewShotPackage(),
new RNCameraPackage(),
new RNNetworkInfoPackage(),
new ReactNativeExceptionHandlerPackage(),
Expand Down
2 changes: 2 additions & 0 deletions client/react-native/android/settings.gradle
Expand Up @@ -3,6 +3,8 @@ include ':lottie-react-native'
project(':lottie-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/lottie-react-native/src/android')
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
include ':react-native-view-shot'
project(':react-native-view-shot').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-view-shot/android')
include ':react-native-svg'
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
include ':react-native-restart'
Expand Down
142 changes: 142 additions & 0 deletions client/react-native/common/components/Library/ContactIdentity.js
@@ -0,0 +1,142 @@
import React from 'react'
import { View, Image, Platform, Text as RNText } from 'react-native'
import Text from './Text'
import { createMaterialTopTabNavigator } from 'react-navigation'
import QRGenerator from './QRGenerator'
import { extractPublicKeyFromId, makeShareableUrl } from '../../helpers/contacts'
import colors from '../../constants/colors'
import Icon from './Icon'
import { formattedFingerprint } from '../../helpers/fingerprint'
import { padding } from '../../styles'
import { withScreenProps } from '../../helpers/views'

const PublicKey = ({ data: { id } }) => <View
style={[{ flexDirection: 'row', justifyContent: 'center' }, padding]}>
<RNText style={{
textAlign: 'left',
width: 248,
height: 248,
backgroundColor: colors.inputGrey,
color: colors.fakeBlack,
borderRadius: 8,
flexWrap: 'wrap',
fontFamily: Platform.OS === 'ios' ? 'Courier' : 'monospace',
padding: 8,
}}>
{extractPublicKeyFromId(id)}
</RNText>
</View>

export const QrCode = ({ data: { id, displayName } }) => <View
style={[{ flexDirection: 'row', justifyContent: 'center' }]}>
<QRGenerator
value={makeShareableUrl({ id: extractPublicKeyFromId(id), displayName })}
size={248}
style={[{ marginTop: 16, marginBottom: 16 }]}
/>
</View>

const tabIcon = (iconName) => {
const NamedTabIcon = ({ focused }) => <Icon name={iconName} color={focused ? colors.blue : colors.darkGrey}
size={20} />

return NamedTabIcon
}

const Fingerprint = ({ data: { id } }) => <View
style={[{ flexDirection: 'row', justifyContent: 'center' }, padding]}>
<RNText style={{
textAlign: 'center',
width: 248,
backgroundColor: colors.blue25,
color: colors.blue,
borderRadius: 8,
flexWrap: 'wrap',
fontSize: 18,
fontFamily: Platform.OS === 'ios' ? 'Courier' : 'monospace',
padding: 8,
}}>
{formattedFingerprint(id)}
</RNText>
</View>

const ContactIdentityTabbedContent = createMaterialTopTabNavigator(
{
'qrcode': {
screen: withScreenProps(QrCode),
navigationOptions: {
title: 'QR Code',
tabBarIcon: tabIcon('material-qrcode'),
},
},
'public-key': {
screen: withScreenProps(PublicKey),
navigationOptions: {
title: 'Public key',
tabBarIcon: tabIcon('material-key-variant'),
},
},
'fingerprint': {
screen: withScreenProps(Fingerprint),
navigationOptions: {
title: 'Fingerprint',
tabBarIcon: tabIcon('material-fingerprint'),
},
},
},
{
initialRouteName: 'qrcode',
swipeEnabled: Platform.OS !== 'android',
animationEnabled: true,
backBehavior: 'none',
tabBarOptions: {
activeTintColor: colors.fakeBlack,
inactiveTintColor: colors.fakeBlack,
showIcon: true,
showLabel: true,
upperCaseLabel: false,
style: {
backgroundColor: colors.white,
marginBottom: 0,
marginTop: 0,
},
tabStyle: {
marginBottom: 0,
marginTop: 0,
},
indicatorStyle: {
backgroundColor: colors.blue,
marginBottom: 0,
marginTop: 0,
},
labelStyle: {
fontSize: 12,
marginBottom: 0,
marginTop: 0,
},
},
},
)

const ContactIdentity = ({ data }) => <>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<Image
style={{
width: 78,
height: 78,
borderRadius: 39,
marginBottom: 4,
marginTop: 0,
}}
source={{
uri: 'https://api.adorable.io/avatars/120/' + data.id + '.png',
}}
/>
</View>
<Text large color={colors.fakeBlack} center padding>{data.displayName}</Text>
<View style={{ marginLeft: 15, marginRight: 15, marginBottom: 8, height: Platform.OS === 'android' ? 330 : undefined }}>
{<ContactIdentityTabbedContent screenProps={{ data }} />}
</View>
</>

export default ContactIdentity
82 changes: 57 additions & 25 deletions client/react-native/common/components/Library/ModalScreen.js
@@ -1,44 +1,76 @@
import React from 'react'
import { View } from 'react-native'
import { StackActions } from 'react-navigation'
import { StackActions, withNavigation } from 'react-navigation'
import colors from '../../constants/colors'
import Button from './Button'

const ModalScreen = props => {
const {
children,
navigation,
showDismiss,
width,
footer,
...otherProps
} = props

return <View
style={[{
backgroundColor: colors.white,
left: 5,
right: 5,
marginBottom: 30,
marginTop: 30,
return <>
<View style={{
position: 'absolute',
flex: 1,
}]}
{...otherProps}
>
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: -1,
}}>
<View
style={{
backgroundColor: colors.transparentGrey,
flex: 1,
}}
/>
</View>
<View style={{
backgroundColor: colors.white,
width: width || 320,
position: 'absolute',
flex: 1,
}}>
<Button onPress={() => {
if (props.onDismiss !== undefined) {
props.onDismiss()
} else {
navigation.dispatch(StackActions.pop({
n: 1,
}))
}
}}>Dismiss</Button>
<View
style={[{
backgroundColor: colors.white,
borderRadius: 10,
}]}
{...otherProps}
>
{showDismiss
? <View style={{
flex: 1,
marginTop: 10,
marginRight: 10,
alignSelf: 'flex-end',
zIndex: 1,
}}>
<Button onPress={() => {
if (props.onDismiss !== undefined) {
props.onDismiss()
} else {
navigation.dispatch(StackActions.pop({
n: 1,
}))
}
}} icon={'x'} color={colors.fakeBlack} large />

</View>
: null}
<View style={{
marginTop: -24,
}}>
{children}
</View>
</View>
{footer}
</View>
{children}
</View>
</>
}

export default ModalScreen
export default withNavigation(ModalScreen)
4 changes: 2 additions & 2 deletions client/react-native/common/components/Library/QRGenerator.js
Expand Up @@ -2,8 +2,8 @@ import React from 'react'
import QRCode from 'react-native-qrcode-svg'
import { View } from 'react-native'

export default ({ value, size, style }) => <>
export default ({ value, ecl, size, style }) => <>
<View style={[{ width: size, height: size }, ...(style || [])]}>
<QRCode size={size} value={value} />
<QRCode size={size} value={value} ecl={ecl || 'L'} />
</View>
</>
50 changes: 50 additions & 0 deletions client/react-native/common/components/Library/SaveViewToCamera.js
@@ -0,0 +1,50 @@
import ViewShot from 'react-native-view-shot'
import React, { Component } from 'react'
import { View, CameraRoll } from 'react-native'
import { StackActions } from 'react-navigation'

export class ViewExportComponent extends Component {
async componentDidMount () {
const resolve = this.props.navigation.getParam('resolve')
const reject = this.props.navigation.getParam('reject')

try {
const uri = await this.refs.viewShot.capture({ format: 'jpg' })

resolve(uri)
} catch (e) {
reject(e)
}

this.props.navigation.dispatch(StackActions.pop({
n: 1,
}))
}

render () {
const view = this.props.navigation.getParam('view')

return (
<View style={{ opacity: 0 }}>
<ViewShot ref='viewShot'>
{view}
</ViewShot>
</View>
)
}
}

export default async ({ view, navigation }) => {
try {
const uri = await new Promise((resolve, reject) => {
navigation.push('virtual/view-export', {
resolve,
reject,
view,
})
})
await CameraRoll.saveToCameraRoll(uri, 'photo')
} catch (e) {
throw e
}
}
Expand Up @@ -108,7 +108,7 @@ export default createTabNavigator(
)

export const ByPublicKeyModal = props => (
<ModalScreen navigation={props.navigation}>
<ModalScreen navigation={props.navigation} showDismiss>
<PublicKeyWithActions
addButton
initialKey={props.navigation.getParam('initialKey', '')}
Expand Down

0 comments on commit 494b4a6

Please sign in to comment.