react-google-contacts
Version:
A Google Button to import user's gmail contacts
298 lines (266 loc) • 8.02 kB
JavaScript
/* eslint-disable no-underscore-dangle */
/* eslint-disable better-mutation/no-mutating-methods */
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Icon from './icon'
import ButtonContent from './button-content'
const SCOPE = 'https://www.googleapis.com/auth/contacts.other.readonly'
class GoogleContacts extends Component {
constructor(props) {
super(props)
this.signIn = this.signIn.bind(this)
this.handleImportContacts = this.handleImportContacts.bind(this)
this.handleParseContacts = this.handleParseContacts.bind(this)
this.state = {
hovered: false,
active: false
}
this.allData = []
}
componentDidMount() {
this.allData = []
const { jsSrc } = this.props
;((d, s, id, cb) => {
const element = d.getElementsByTagName(s)[0]
const fjs = element
let js = element
js = d.createElement(s)
js.id = id
js.src = jsSrc
if (fjs && fjs.parentNode) {
fjs.parentNode.insertBefore(js, fjs)
} else {
d.head.appendChild(js)
}
js.onload = cb
})(document, 'script', 'google-contacts')
}
handleImportContacts(res, pageToken = null) {
const { onFailure, maxResults } = this.props
if (res) {
const authResponse = res.getAuthResponse()
window.gapi.load('client', () => {
window.gapi.client
.request({
path: 'https://people.googleapis.com/v1/otherContacts',
params: {
readMask: 'names,emailAddresses',
pageSize: maxResults > 1000 ? 1000 : maxResults,
...(pageToken && { pageToken })
},
headers: {
'GData-Version': '3.0',
Authorization: `Bearer ${authResponse.access_token}`
}
})
.then(
response => this.handleNextDataFetch(response, res),
err => onFailure(err)
)
})
}
}
handleNextDataFetch(response, authResponse) {
const { maxResults } = this.props
// Parse the response body
const parsedData = JSON.parse(response.body)
// Store the fetched data so that we can use it later
this.allData = [...this.allData, ...parsedData.otherContacts]
// If we have more data and the number of data we fethced is less than maxResults then fetch again using the nextPageToken
if ('nextPageToken' in parsedData && maxResults < this.allData.length) {
this.handleImportContacts(authResponse, parsedData.nextPageToken)
} else {
this.handleParseContacts()
}
}
handleParseContacts() {
const { onSuccess, onFailure } = this.props
const results = []
try {
for (let index = 0; index < this.allData.length; index += 1) {
const element = this.allData[index]
results.push({
email: element.emailAddresses[0].value,
title: 'names' in element ? element.names[0].displayName : element.emailAddresses[0].value
})
}
onSuccess(results)
} catch (error) {
onFailure('Error to fetch contact')
}
}
signIn(e) {
this.allData = []
const {
clientId,
cookiePolicy,
loginHint,
hostedDomain,
redirectUri,
discoveryDocs,
onRequest,
onFailure,
uxMode,
accessType,
responseType,
prompt,
onSuccess
} = this.props
const { disable } = this.state
const params = {
client_id: clientId,
cookie_policy: cookiePolicy,
login_hint: loginHint,
hosted_domain: hostedDomain,
discoveryDocs,
ux_mode: uxMode,
redirect_uri: redirectUri,
scope: SCOPE,
access_type: accessType
}
if (responseType === 'code') {
params.access_type = 'offline'
}
if (e) {
e.preventDefault() // to prevent submit if used within form
}
if (!disable) {
const _signIn = () => {
const auth2 = window.gapi.auth2.getAuthInstance()
const options = { prompt }
onRequest()
if (responseType === 'code') {
auth2.grantOfflineAccess(options).then(
res => onSuccess(res),
err => onFailure(err)
)
} else {
auth2.signIn(options).then(
res => this.handleImportContacts(res),
err => onFailure(err)
)
}
}
window.gapi.load('auth2', () => {
if (!window.gapi.auth2.getAuthInstance()) {
window.gapi.auth2.init(params).then(_signIn)
} else {
_signIn()
}
})
}
}
render() {
const { tag, type, className, disabledStyle, buttonText, children, render, theme, icon, disabled: disabledProps } = this.props
const { active, hovered, disabled: disabledState } = this.state
const disabled = disabledState || disabledProps
if (render) {
return render({ onClick: this.signIn })
}
const initialStyle = {
backgroundColor: theme === 'dark' ? 'rgb(66, 133, 244)' : '#fff',
display: 'inline-flex',
alignItems: 'center',
color: theme === 'dark' ? '#fff' : 'rgba(0, 0, 0, .54)',
boxShadow: '0 2px 2px 0 rgba(0, 0, 0, .24), 0 0 1px 0 rgba(0, 0, 0, .24)',
padding: 0,
borderRadius: 2,
border: '1px solid transparent',
fontSize: 14,
fontWeight: '500',
fontFamily: 'Roboto, sans-serif'
}
const hoveredStyle = {
cursor: 'pointer',
opacity: 0.9
}
const activeStyle = {
cursor: 'pointer',
backgroundColor: theme === 'dark' ? '#3367D6' : '#eee',
color: theme === 'dark' ? '#fff' : 'rgba(0, 0, 0, .54)',
opacity: 1
}
const defaultStyle = (() => {
if (disabled) {
return Object.assign({}, initialStyle, disabledStyle)
}
if (active) {
if (theme === 'dark') {
return Object.assign({}, initialStyle, activeStyle)
}
return Object.assign({}, initialStyle, activeStyle)
}
if (hovered) {
return Object.assign({}, initialStyle, hoveredStyle)
}
return initialStyle
})()
const googleLoginButton = React.createElement(
tag,
{
onMouseEnter: () => this.setState({ hovered: true }),
onMouseLeave: () => this.setState({ hovered: false, active: false }),
onMouseDown: () => this.setState({ active: true }),
onMouseUp: () => this.setState({ active: false }),
onClick: this.signIn,
style: defaultStyle,
type,
disabled,
className
},
[
icon && <Icon key={1} active={active} />,
<ButtonContent key={2} icon={icon}>
{children || buttonText}
</ButtonContent>
]
)
return googleLoginButton
}
}
GoogleContacts.propTypes = {
accessType: PropTypes.string,
buttonText: PropTypes.node,
children: PropTypes.node,
className: PropTypes.string,
clientId: PropTypes.string.isRequired,
cookiePolicy: PropTypes.string,
disabled: PropTypes.bool,
disabledStyle: PropTypes.object,
discoveryDocs: PropTypes.array,
hostedDomain: PropTypes.string,
icon: PropTypes.bool,
jsSrc: PropTypes.string,
loginHint: PropTypes.string,
maxResults: PropTypes.number,
onFailure: PropTypes.func.isRequired,
onRequest: PropTypes.func,
onSuccess: PropTypes.func.isRequired,
prompt: PropTypes.string,
redirectUri: PropTypes.string,
render: PropTypes.func,
responseType: PropTypes.string,
tag: PropTypes.string,
theme: PropTypes.string,
type: PropTypes.string,
uxMode: PropTypes.string
}
GoogleContacts.defaultProps = {
accessType: 'online',
buttonText: 'Import from Gmail',
cookiePolicy: 'single_host_origin',
disabled: false,
disabledStyle: {
opacity: 0.6
},
icon: true,
jsSrc: 'https://apis.google.com/js/api.js',
maxResults: 999,
onRequest: () => {},
prompt: 'consent',
tag: 'button',
theme: 'light',
type: 'button',
uxMode: 'popup'
}
export default GoogleContacts