269 lines
8.4 KiB
JavaScript
269 lines
8.4 KiB
JavaScript
import React from 'react'
|
|
import Router from 'next/router'
|
|
import Link from 'next/link'
|
|
import fetch from 'isomorphic-fetch'
|
|
import { Row, Col, Form, FormGroup, Label, Input, Button } from 'reactstrap'
|
|
import { NextAuth } from 'next-auth/client'
|
|
|
|
import Layout from '../components/MyLayout'
|
|
import Cookies from 'universal-cookie'
|
|
|
|
export default class extends React.Component {
|
|
|
|
state={
|
|
session: {},
|
|
isSignedIn: false,
|
|
name: '',
|
|
email: '',
|
|
emailVerified: false,
|
|
alertText: null,
|
|
alertStyle: null
|
|
}
|
|
|
|
static async getInitialProps({req}) {
|
|
let props = {};
|
|
props.session = await NextAuth.init({req})
|
|
props.linkedAccounts = await NextAuth.linked({req})
|
|
return props
|
|
}
|
|
|
|
constructor(props) {
|
|
super(props)
|
|
this.state = {
|
|
session: props.session,
|
|
isSignedIn: (props.session.user) ? true : false,
|
|
name: '',
|
|
email: '',
|
|
emailVerified: false,
|
|
alertText: null,
|
|
alertStyle: null
|
|
}
|
|
if (props.session.user) {
|
|
this.state.name = props.session.user.name
|
|
this.state.email = props.session.user.email
|
|
}
|
|
this.handleChange = this.handleChange.bind(this)
|
|
this.onSubmit = this.onSubmit.bind(this)
|
|
this.handleSignoutSubmit = this.handleSignoutSubmit.bind(this)
|
|
}
|
|
|
|
async componentDidMount() {
|
|
const session = await NextAuth.init({force: true})
|
|
this.setState({
|
|
session: session,
|
|
isSignedIn: (session.user) ? true : false
|
|
})
|
|
|
|
// If the user bounces off to link/unlink their account we want them to
|
|
// land back here after signing in with the other service / unlinking.
|
|
const cookies = new Cookies()
|
|
cookies.set('redirect_url', window.location.pathname, { path: '/' })
|
|
|
|
this.getProfile()
|
|
}
|
|
|
|
getProfile() {
|
|
fetch('/account/user', {
|
|
credentials: 'include'
|
|
})
|
|
.then(r => r.json())
|
|
.then(user => {
|
|
if (!user.name || !user.email) return
|
|
this.setState({
|
|
name: user.name,
|
|
email: user.email,
|
|
emailVerified: user.emailVerified
|
|
})
|
|
})
|
|
}
|
|
|
|
handleChange(event) {
|
|
this.setState({
|
|
[event.target.name]: event.target.value
|
|
})
|
|
}
|
|
|
|
async onSubmit(e) {
|
|
// Submits the URL encoded form without causing a page reload
|
|
e.preventDefault()
|
|
|
|
this.setState({
|
|
alertText: null,
|
|
alertStyle: null
|
|
})
|
|
|
|
const formData = {
|
|
_csrf: await NextAuth.csrfToken(),
|
|
//_csrf:this.state.session.csrfToken,
|
|
name: this.state.name || '',
|
|
email: this.state.email || ''
|
|
}
|
|
|
|
// URL encode form
|
|
// Note: This uses a x-www-form-urlencoded rather than sending JSON so that
|
|
// the form also in browsers without JavaScript
|
|
const encodedForm = Object.keys(formData).map((key) => {
|
|
return encodeURIComponent(key) + '=' + encodeURIComponent(formData[key])
|
|
}).join('&')
|
|
|
|
fetch('/account/user', {
|
|
credentials: 'include',
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
},
|
|
body: encodedForm
|
|
})
|
|
.then(async res => {
|
|
if (res.status === 200) {
|
|
this.getProfile()
|
|
this.setState({
|
|
alertText: 'Changes to your profile have been saved',
|
|
alertStyle: 'alert-success',
|
|
})
|
|
// Force update session so that changes to name or email are reflected
|
|
// immediately in the navbar (as we pass our session to it).
|
|
this.setState({
|
|
session: await NextAuth.init({force: true}), // Update session data
|
|
})
|
|
} else {
|
|
this.setState({
|
|
session: await NextAuth.init({force: true}), // Update session data
|
|
alertText: 'Failed to save changes to your profile',
|
|
alertStyle: 'alert-danger',
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
async handleSignoutSubmit(event) {
|
|
event.preventDefault()
|
|
|
|
// Save current URL so user is redirected back here after signing out
|
|
const cookies = new Cookies()
|
|
cookies.set('redirect_url', window.location.pathname, { path: '/' })
|
|
|
|
await NextAuth.signout()
|
|
Router.push('/')
|
|
}
|
|
|
|
render() {
|
|
if (this.state.isSignedIn === true) {
|
|
const alert = (this.state.alertText === null) ? <div/> : <div className={`alert ${this.state.alertStyle}`} role="alert">{this.state.alertText}</div>
|
|
|
|
return (
|
|
<Layout menu={this.props.menu}>
|
|
<Row className="mb-1">
|
|
<Col xs="12">
|
|
<h1 className="display-2">Your Account</h1>
|
|
<p className="lead text-muted">
|
|
Edit your profile and link accounts
|
|
</p>
|
|
</Col>
|
|
</Row>
|
|
{alert}
|
|
<Row className="mt-4">
|
|
<Col xs="12" md="8" lg="9">
|
|
<Form method="post" action="/account/user" onSubmit={this.onSubmit}>
|
|
<Input name="_csrf" type="hidden" value={this.state.session.csrfToken} onChange={()=>{}}/>
|
|
<FormGroup row>
|
|
<Label sm={2}>Name:</Label>
|
|
<Col sm={10} md={8}>
|
|
<Input name="name" value={this.state.name} onChange={this.handleChange}/>
|
|
</Col>
|
|
</FormGroup>
|
|
<FormGroup row>
|
|
<Label sm={2}>Email:</Label>
|
|
<Col sm={10} md={8}>
|
|
<Input name="email" value={(this.state.email.match(/.*@localhost\.localdomain$/)) ? '' : this.state.email} onChange={this.handleChange}/>
|
|
</Col>
|
|
</FormGroup>
|
|
<FormGroup row>
|
|
<Col sm={12} md={10}>
|
|
<p className="text-right">
|
|
<Button color="primary" type="submit">Save Changes</Button>
|
|
</p>
|
|
</Col>
|
|
</FormGroup>
|
|
</Form>
|
|
<Form id="signout" method="post" action="/auth/signout" onSubmit={this.handleSignoutSubmit}>
|
|
<input name="_csrf" type="hidden" value={this.props.session.csrfToken}/>
|
|
<Button type="submit" block className="pl-4 rounded-0 text-left dropdown-item"><span className="icon ion-md-log-out mr-1"></span> Sign out</Button>
|
|
</Form>
|
|
</Col>
|
|
<Col xs="12" md="4" lg="3">
|
|
<LinkAccounts
|
|
session={this.props.session}
|
|
linkedAccounts={this.props.linkedAccounts}
|
|
/>
|
|
</Col>
|
|
</Row>
|
|
<Row>
|
|
<Col>
|
|
<h2>Delete your account</h2>
|
|
<p>
|
|
If you delete your account it will be erased immediately.
|
|
You can sign up again at any time.
|
|
</p>
|
|
<Form id="signout" method="post" action="/account/delete">
|
|
<input name="_csrf" type="hidden" value={this.state.session.csrfToken}/>
|
|
<Button type="submit" color="outline-danger"><span className="icon ion-md-trash mr-1"></span> Delete Account</Button>
|
|
</Form>
|
|
</Col>
|
|
</Row>
|
|
</Layout>
|
|
)
|
|
} else {
|
|
return (
|
|
<Layout {...this.props} navmenu={false}>
|
|
<Row>
|
|
<Col xs="12" className="text-center pt-5 pb-5">
|
|
<p className="lead m-0">
|
|
<Link href="/auth"><a>Sign in to manage your profile</a></Link>
|
|
</p>
|
|
</Col>
|
|
</Row>
|
|
</Layout>
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
export class LinkAccounts extends React.Component {
|
|
render() {
|
|
return (
|
|
<React.Fragment>
|
|
{
|
|
Object.keys(this.props.linkedAccounts).map((provider, i) => {
|
|
return <LinkAccount key={i} provider={provider} session={this.props.session} linked={this.props.linkedAccounts[provider]}/>
|
|
})
|
|
}
|
|
</React.Fragment>
|
|
)
|
|
}
|
|
}
|
|
|
|
export class LinkAccount extends React.Component {
|
|
render() {
|
|
if (this.props.linked === true) {
|
|
return (
|
|
<form method="post" action={`/auth/oauth/${this.props.provider.toLowerCase()}/unlink`}>
|
|
<input name="_csrf" type="hidden" value={this.props.session.csrfToken}/>
|
|
<p>
|
|
<button className="btn btn-block btn-outline-danger" type="submit">
|
|
Unlink from {this.props.provider}
|
|
</button>
|
|
</p>
|
|
</form>
|
|
)
|
|
} else {
|
|
return (
|
|
<p>
|
|
<a className="btn btn-block btn-outline-primary" href={`/auth/oauth/${this.props.provider.toLowerCase()}`}>
|
|
Link with {this.props.provider}
|
|
</a>
|
|
</p>
|
|
)
|
|
}
|
|
}
|
|
} |