Migrate components to typescript
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,6 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
public/bundle.js
|
public/bundle.js
|
||||||
config/
|
config/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
src/data/awsClient
|
||||||
|
src/components/AWS.tsx
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the circles which the specify users is in that circle
|
|
||||||
* @param {object} circles
|
|
||||||
* @param {string} followingId
|
|
||||||
*/
|
|
||||||
export const getUserBelongCircles = (circles,followingId) => {
|
|
||||||
let userBelongCircles = []
|
|
||||||
Object.keys(circles).forEach((cid) => {
|
|
||||||
if(cid.trim() !== '-Followers' && circles[cid].users){
|
|
||||||
let isExist = Object.keys(circles[cid].users).indexOf(followingId) > -1
|
|
||||||
if(isExist){
|
|
||||||
userBelongCircles.push(cid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return userBelongCircles
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the following users
|
|
||||||
* @param {object} circles
|
|
||||||
*/
|
|
||||||
export const getFollowingUsers = (circles) => {
|
|
||||||
let followingUsers = {}
|
|
||||||
Object.keys(circles).forEach((cid) => {
|
|
||||||
if(cid.trim() !== '-Followers' && circles[cid].users){
|
|
||||||
Object.keys(circles[cid].users).forEach((userId)=>{
|
|
||||||
let isExist = Object.keys(followingUsers).indexOf(userId) > -1
|
|
||||||
if(!isExist){
|
|
||||||
followingUsers[userId] = {
|
|
||||||
...circles[cid].users[userId]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return followingUsers
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
getUserBelongCircles,
|
|
||||||
getFollowingUsers
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
// - Import react component
|
|
||||||
import { storageRef } from 'app/firebaseClient/'
|
|
||||||
|
|
||||||
//- Import actions
|
|
||||||
|
|
||||||
// - Get file Extension
|
|
||||||
const getExtension = (fileName) => {
|
|
||||||
let re = /(?:\.([^.]+))?$/
|
|
||||||
return re.exec(fileName)[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts image to canvas returns new canvas element
|
|
||||||
const convertImageToCanvas = (image) => {
|
|
||||||
let canvas = document.createElement('canvas')
|
|
||||||
canvas.width = image.width
|
|
||||||
canvas.height = image.height
|
|
||||||
canvas.getContext('2d').drawImage(image, 0, 0)
|
|
||||||
|
|
||||||
return canvas
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload image on the server
|
|
||||||
* @param {file} file
|
|
||||||
* @param {string} fileName
|
|
||||||
*/
|
|
||||||
const uploadImage = (file, fileName, progress) => {
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Create a storage refrence
|
|
||||||
let storegeFile = storageRef.child(`images/${fileName}`)
|
|
||||||
|
|
||||||
// Upload file
|
|
||||||
let task = storegeFile.put(file)
|
|
||||||
task.then((result) => {
|
|
||||||
resolve(result)
|
|
||||||
}).catch((error) => {
|
|
||||||
reject(error)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Upload storage bar
|
|
||||||
task.on('state_changed', (snapshot) => {
|
|
||||||
let percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
|
|
||||||
progress(percentage, true)
|
|
||||||
}, (error) => {
|
|
||||||
console.log('========== Upload Image ============')
|
|
||||||
console.log(error)
|
|
||||||
console.log('====================================')
|
|
||||||
|
|
||||||
}, (complete) => {
|
|
||||||
progress(100, false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constraint image size
|
|
||||||
* @param {file} file
|
|
||||||
* @param {number} maxWidth
|
|
||||||
* @param {number} maxHeight
|
|
||||||
*/
|
|
||||||
const constraintImage = (file,fileName, maxWidth, maxHeight) => {
|
|
||||||
// Ensure it's an image
|
|
||||||
if(file.type.match(/image.*/)) {
|
|
||||||
|
|
||||||
// Load the image
|
|
||||||
let reader = new FileReader()
|
|
||||||
reader.onload = function (readerEvent) {
|
|
||||||
let image = new Image()
|
|
||||||
image.onload = function (imageEvent) {
|
|
||||||
|
|
||||||
// Resize the image
|
|
||||||
let canvas = document.createElement('canvas'),
|
|
||||||
max_size = 986,// TODO : pull max size from a site config
|
|
||||||
width = image.width,
|
|
||||||
height = image.height
|
|
||||||
if (width > height) {
|
|
||||||
if (width > max_size) {
|
|
||||||
height *= max_size / width
|
|
||||||
width = max_size
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (height > max_size) {
|
|
||||||
width *= max_size / height
|
|
||||||
height = max_size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
canvas.width = width
|
|
||||||
canvas.height = height
|
|
||||||
canvas.getContext('2d').drawImage(image, 0, 0, width, height)
|
|
||||||
let dataUrl = canvas.toDataURL()
|
|
||||||
let resizedImage = dataURLToBlob(dataUrl)
|
|
||||||
let evt = new CustomEvent('onSendResizedImage', { detail: {resizedImage,fileName} })
|
|
||||||
window.dispatchEvent(evt)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
image.src = readerEvent.target.result
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert data URL to blob
|
|
||||||
* @param {object} dataURL
|
|
||||||
*/
|
|
||||||
const dataURLToBlob = (dataURL) => {
|
|
||||||
|
|
||||||
let BASE64_MARKER = 'base64,'
|
|
||||||
if (dataURL.indexOf(BASE64_MARKER) == -1) {
|
|
||||||
let parts = dataURL.split(',')
|
|
||||||
let contentType = parts[0].split(':')[1]
|
|
||||||
let raw = parts[1]
|
|
||||||
|
|
||||||
return new Blob([raw], {type: contentType})
|
|
||||||
}
|
|
||||||
|
|
||||||
let parts = dataURL.split(BASE64_MARKER)
|
|
||||||
let contentType = parts[0].split(':')[1]
|
|
||||||
let raw = window.atob(parts[1])
|
|
||||||
let rawLength = raw.length
|
|
||||||
|
|
||||||
let uInt8Array = new Uint8Array(rawLength)
|
|
||||||
|
|
||||||
for (let i = 0 ;i < rawLength; ++i) {
|
|
||||||
uInt8Array[i] = raw.charCodeAt(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Blob([uInt8Array], {type: contentType})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
dataURLToBlob,
|
|
||||||
convertImageToCanvas,
|
|
||||||
getExtension,
|
|
||||||
constraintImage,
|
|
||||||
uploadImage
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
|
|
||||||
// Get tags from post content
|
|
||||||
export const detectTags = (content,character) => {
|
|
||||||
|
|
||||||
return content.split(" ").filter((word) => {
|
|
||||||
return (word.slice(0,1) === character)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
export const getContentTags = (content) => {
|
|
||||||
let newTags = []
|
|
||||||
let tags = detectTags(content,'#')
|
|
||||||
tags.forEach((tag)=>{
|
|
||||||
newTags.push(tag.slice(1))
|
|
||||||
})
|
|
||||||
return newTags
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sortObjectsDate = (objects) => {
|
|
||||||
let sortedObjects = objects
|
|
||||||
|
|
||||||
// Sort posts with creation date
|
|
||||||
sortedObjects.sort((a, b) => {
|
|
||||||
return parseInt(b.creationDate) - parseInt(a.creationDate)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return sortedObjects
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// - Import react component
|
|
||||||
import { storageRef } from 'app/firebaseClient/'
|
|
||||||
|
|
||||||
//- Import actions
|
|
||||||
|
|
||||||
const isValidEmail = (email) => {
|
|
||||||
let re = /^(([^<>()\[\]\\.,:\s@"]+(\.[^<>()\[\]\\.,:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
|
||||||
return re.test(email)
|
|
||||||
}
|
|
||||||
|
|
||||||
function queryString(name, url = window.location.href) {
|
|
||||||
name = name.replace(/[[]]/g, "\\$&");
|
|
||||||
|
|
||||||
const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)", "i");
|
|
||||||
const results = regex.exec(url);
|
|
||||||
|
|
||||||
if (!results) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!results[2]) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
isValidEmail,
|
|
||||||
queryString
|
|
||||||
}
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
// - Import react components
|
|
||||||
import React, { Component } from 'react'
|
|
||||||
import { connect } from 'react-redux'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {push} from 'react-router-redux'
|
|
||||||
import {grey400, darkBlack, lightBlack} from 'material-ui/styles/colors'
|
|
||||||
import { List, ListItem } from 'material-ui/List'
|
|
||||||
import SvgGroup from 'material-ui/svg-icons/action/group-work'
|
|
||||||
import IconButton from 'material-ui/IconButton'
|
|
||||||
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert'
|
|
||||||
import IconMenu from 'material-ui/IconMenu'
|
|
||||||
import TextField from 'material-ui/TextField'
|
|
||||||
import MenuItem from 'material-ui/MenuItem'
|
|
||||||
import IconButtonElement from 'IconButtonElement'
|
|
||||||
import Dialog from 'material-ui/Dialog'
|
|
||||||
import Divider from 'material-ui/Divider'
|
|
||||||
import FlatButton from 'material-ui/FlatButton'
|
|
||||||
import RaisedButton from 'material-ui/RaisedButton'
|
|
||||||
import SvgClose from 'material-ui/svg-icons/navigation/close'
|
|
||||||
import AppBar from 'material-ui/AppBar'
|
|
||||||
|
|
||||||
// - Import app components
|
|
||||||
import UserAvatar from 'UserAvatar'
|
|
||||||
|
|
||||||
|
|
||||||
// - Import API
|
|
||||||
|
|
||||||
|
|
||||||
// - Import actions
|
|
||||||
import * as circleActions from 'circleActions'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create component class
|
|
||||||
*/
|
|
||||||
export class Circle extends Component {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
/**
|
|
||||||
* Circle object
|
|
||||||
*/
|
|
||||||
circle: PropTypes.object.isRequired,
|
|
||||||
/**
|
|
||||||
* Circle identifier
|
|
||||||
*/
|
|
||||||
id: PropTypes.string.isRequired
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component constructor
|
|
||||||
* @param {object} props is an object properties of component
|
|
||||||
*/
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
//Defaul state
|
|
||||||
this.state = {
|
|
||||||
/**
|
|
||||||
* If is true circle is open to show users in circle list
|
|
||||||
*/
|
|
||||||
open:false,
|
|
||||||
/**
|
|
||||||
* Circle name on change
|
|
||||||
*/
|
|
||||||
circleName: this.props.circle.name,
|
|
||||||
/**
|
|
||||||
* Save operation will be disable if user doesn't meet requirement
|
|
||||||
*/
|
|
||||||
disabledSave: false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binding functions to `this`
|
|
||||||
this.handleToggleCircle = this.handleToggleCircle.bind(this)
|
|
||||||
this.handleDeleteCircle = this.handleDeleteCircle.bind(this)
|
|
||||||
this.handleUpdateCircle = this.handleUpdateCircle.bind(this)
|
|
||||||
this.handleChangeCircleName = this.handleChangeCircleName.bind(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle chage circle name
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof Circle
|
|
||||||
*/
|
|
||||||
handleChangeCircleName = (evt) => {
|
|
||||||
const {value} = evt.target
|
|
||||||
this.setState({
|
|
||||||
circleName: value,
|
|
||||||
disabledSave: (!value || value.trim() === '')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update user's circle
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof Circle
|
|
||||||
*/
|
|
||||||
handleUpdateCircle = () => {
|
|
||||||
const {circleName} = this.state
|
|
||||||
if(circleName && circleName.trim() !== ''){
|
|
||||||
this.props.updateCircle({name:circleName,id: this.props.id})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle delete circle
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof Circle
|
|
||||||
*/
|
|
||||||
handleDeleteCircle = () => {
|
|
||||||
this.props.deleteCircle(this.props.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle circle to close/open
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof Circle
|
|
||||||
*/
|
|
||||||
handleToggleCircle = () => {
|
|
||||||
this.setState({
|
|
||||||
open: !this.state.open
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
userList = () => {
|
|
||||||
const {users} = this.props.circle
|
|
||||||
const {userInfo} = this.props
|
|
||||||
let usersParsed =[]
|
|
||||||
|
|
||||||
if(users){
|
|
||||||
Object.keys(users).forEach((key, index) => {
|
|
||||||
const { fullName} = users[key]
|
|
||||||
let avatar = userInfo && userInfo[key] ? userInfo[key].avatar || '' : ''
|
|
||||||
usersParsed.push(<ListItem
|
|
||||||
key={`${this.props.id}.${key}`}
|
|
||||||
style={{backgroundColor: '#e2e2e2'}}
|
|
||||||
value={2}
|
|
||||||
primaryText={fullName}
|
|
||||||
leftAvatar={<UserAvatar fullName={fullName} fileName={avatar}/>}
|
|
||||||
onClick={()=> this.props.goTo(`/${key}`)}
|
|
||||||
/>)
|
|
||||||
|
|
||||||
})
|
|
||||||
return usersParsed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Right icon menue of circle
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof Circle
|
|
||||||
*/
|
|
||||||
rightIconMenu =(
|
|
||||||
<IconMenu iconButtonElement={IconButtonElement} style={{ display: "block", position: "absolute", top: "0px", right: "12px" }}>
|
|
||||||
<MenuItem primaryText="Delete circle" onClick={this.handleDeleteCircle} />
|
|
||||||
<MenuItem primaryText="Circle settings" onClick={this.props.openCircleSettings} />
|
|
||||||
</IconMenu>
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Reneder component DOM
|
|
||||||
* @return {react element} return the DOM which rendered by component
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
|
|
||||||
const circleTitle =(
|
|
||||||
<div>
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: "space-between" }}>
|
|
||||||
<div style={{paddingRight: '10px'}}>
|
|
||||||
<SvgClose onClick={this.props.closeCircleSettings} hoverColor={grey400} style={{cursor: 'pointer'}} />
|
|
||||||
</div>
|
|
||||||
<div style={{
|
|
||||||
color: 'rgba(0,0,0,0.87)',
|
|
||||||
flex: '1 1',
|
|
||||||
font: '500 20px Roboto,RobotoDraft,Helvetica,Arial,sans-serif'
|
|
||||||
}}>
|
|
||||||
Circle settings
|
|
||||||
</div>
|
|
||||||
<div style={{marginTop: '-9px'}}>
|
|
||||||
<FlatButton label="SAVE" primary={true} disabled={this.state.disabledSave} onClick={this.handleUpdateCircle} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Divider />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ListItem
|
|
||||||
key={this.props.id}
|
|
||||||
style={{backgroundColor: '#fff', borderBottom: '1px solid rgba(0,0,0,0.12)',height: '72px',padding: '12px 0' }}
|
|
||||||
primaryText={<span style={{color:'rgba(0,0,0,0.87)',fontSize:'16px',marginRight:'8px',whiteSpace:'nowrap',textOverflow:'ellipsis',overflow:'hidden'}}>{this.props.circle.name}</span>}
|
|
||||||
leftIcon={<SvgGroup style={{width:'40px',height:'40px',transform: 'translate(0px, -9px)',fill:'#bdbdbd'}} />}
|
|
||||||
rightIconButton={this.rightIconMenu}
|
|
||||||
initiallyOpen={false}
|
|
||||||
onClick={this.handleToggleCircle}
|
|
||||||
open={this.state.open}
|
|
||||||
nestedItems={this.userList()}
|
|
||||||
>
|
|
||||||
<Dialog
|
|
||||||
id={this.props.id}
|
|
||||||
title={circleTitle}
|
|
||||||
modal={false}
|
|
||||||
open={this.props.openSetting}
|
|
||||||
onRequestClose={this.props.closeCircleSettings}
|
|
||||||
overlayStyle={{ background: "rgba(0,0,0,0.12)" }}
|
|
||||||
contentStyle={{maxWidth: '400px'}}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<TextField
|
|
||||||
hintText="Circle name"
|
|
||||||
floatingLabelText="Circle name"
|
|
||||||
onChange={this.handleChangeCircleName}
|
|
||||||
value={this.state.circleName}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</ListItem>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map dispatch to props
|
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
|
||||||
let {uid} = ownProps
|
|
||||||
return {
|
|
||||||
deleteCircle: (id) => dispatch(circleActions.dbDeleteCircle(id)),
|
|
||||||
updateCircle: (circle) => dispatch(circleActions.dbUpdateCircle(circle)),
|
|
||||||
closeCircleSettings: () => dispatch(circleActions.closeCircleSettings(uid,ownProps.id)),
|
|
||||||
openCircleSettings: () => dispatch(circleActions.openCircleSettings(uid,ownProps.id)),
|
|
||||||
goTo: (url)=> dispatch(push(url))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map state to props
|
|
||||||
* @param {object} state is the obeject from redux store
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
|
||||||
let {uid} = state.authorize
|
|
||||||
return {
|
|
||||||
openSetting: state.circle ? (state.circle.userCircles[uid] ? (state.circle.userCircles[uid][ownProps.id].openCircleSettings || false) : false ) : false,
|
|
||||||
userInfo: state.user.info
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Connect component to redux store
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Circle)
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
// - Import react components
|
|
||||||
import React, {Component} from 'react'
|
|
||||||
import {connect} from 'react-redux'
|
|
||||||
import Faker from 'faker'
|
|
||||||
|
|
||||||
// - Import app components
|
|
||||||
|
|
||||||
// - Import actions
|
|
||||||
import * as commentActions from 'commentActions'
|
|
||||||
|
|
||||||
// - Define variable
|
|
||||||
const buttonStyle = {
|
|
||||||
marginTop: '5px'
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Create CommentWrite component class
|
|
||||||
export class CommentWrite extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
inputValue:''
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binding functions to `this`
|
|
||||||
this.handleRef = this.handleRef.bind(this)
|
|
||||||
this.focus = this.focus.bind(this)
|
|
||||||
this.handleAddComment = this.handleAddComment.bind(this)
|
|
||||||
this.handleOnChange = this.handleOnChange.bind(this)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOnChange = (evt) =>{
|
|
||||||
this.setState({inputValue:evt.target.value})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRef = c => {
|
|
||||||
this.inputRef = c
|
|
||||||
}
|
|
||||||
|
|
||||||
focus = () => {
|
|
||||||
this.inputRef.focus()
|
|
||||||
}
|
|
||||||
handleAddComment = (evt) => {
|
|
||||||
this.props.send(this.state.inputValue,this.props.postId,this.props.close)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render DOM
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<textarea autoFocus defaultValue={this.props.commentText} onChange={this.handleOnChange}/>
|
|
||||||
|
|
||||||
<Button basic style={buttonStyle} onClick={this.handleAddComment} color='teal'>Add Comment</Button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Map dispatch to props
|
|
||||||
const mapDispatchToProps = (dispatch,ownProps) => {
|
|
||||||
return{
|
|
||||||
send: (text,postId,callBack) => {
|
|
||||||
dispatch(commentActions.dbAddComment(null,{
|
|
||||||
postId: postId,
|
|
||||||
text: text
|
|
||||||
},callBack))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Map state to props
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Connect component to store
|
|
||||||
export default connect(mapStateToProps,mapDispatchToProps)(CommentWrite)
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
// - Impoer react components
|
|
||||||
import React, { Component } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { connect } from 'react-redux'
|
|
||||||
import { GridList, GridTile } from 'material-ui/GridList'
|
|
||||||
import IconButton from 'material-ui/IconButton'
|
|
||||||
import Subheader from 'material-ui/Subheader'
|
|
||||||
import StarBorder from 'material-ui/svg-icons/toggle/star-border'
|
|
||||||
import FloatingActionButton from 'material-ui/FloatingActionButton'
|
|
||||||
import SvgUpload from 'material-ui/svg-icons/file/cloud-upload'
|
|
||||||
import SvgAddImage from 'material-ui/svg-icons/image/add-a-photo'
|
|
||||||
import SvgDelete from 'material-ui/svg-icons/action/delete'
|
|
||||||
import { grey200, grey600 } from 'material-ui/styles/colors'
|
|
||||||
import FlatButton from 'material-ui/FlatButton'
|
|
||||||
import uuid from 'uuid'
|
|
||||||
|
|
||||||
|
|
||||||
// - Import actions
|
|
||||||
import * as imageGalleryActions from 'imageGalleryActions'
|
|
||||||
import * as globalActions from 'globalActions'
|
|
||||||
|
|
||||||
// - Import app components
|
|
||||||
import Img from 'Img'
|
|
||||||
|
|
||||||
// - Import API
|
|
||||||
import FileAPI from 'FileAPI'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create ImageGallery component class
|
|
||||||
*/
|
|
||||||
export class ImageGallery extends Component {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function to ser image url on parent component
|
|
||||||
*/
|
|
||||||
open: PropTypes.func
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component constructor
|
|
||||||
* @param {object} props is an object properties of component
|
|
||||||
*/
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
// Binding function to `this`
|
|
||||||
this.close = this.close.bind(this)
|
|
||||||
this.onFileChange = this.onFileChange.bind(this)
|
|
||||||
this.handleSetImage = this.handleSetImage.bind(this)
|
|
||||||
this.handleDeleteImage = this.handleDeleteImage.bind(this)
|
|
||||||
this.imageList = this.imageList.bind(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle set image
|
|
||||||
* @param {event} evt passed by on click event on add image
|
|
||||||
* @param {string} name is the name of the image
|
|
||||||
*/
|
|
||||||
handleSetImage = (evt, URL,fullPath) => {
|
|
||||||
this.props.set(URL,fullPath)
|
|
||||||
this.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle delete image
|
|
||||||
* @param {event} evt passed by on click event on delete image
|
|
||||||
* @param {integer} id is the image identifier which selected to delete
|
|
||||||
*/
|
|
||||||
handleDeleteImage = (evt, id) => {
|
|
||||||
this.props.deleteImage(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
window.addEventListener("onSendResizedImage", this.handleSendResizedImage)
|
|
||||||
}
|
|
||||||
componentWillUnmount() {
|
|
||||||
window.removeEventListener("onSendResizedImage", this.handleSendResizedImage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle send image resize event that pass the resized image
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof ImageGallery
|
|
||||||
*/
|
|
||||||
handleSendResizedImage = (event) => {
|
|
||||||
|
|
||||||
const { resizedImage, fileName } = event.detail
|
|
||||||
const {saveImageGallery, progressChange} = this.props
|
|
||||||
|
|
||||||
FileAPI.uploadImage(resizedImage, fileName, (percent, status) => {
|
|
||||||
progressChange(percent,status)
|
|
||||||
}).then((result) => {
|
|
||||||
|
|
||||||
/* Add image to image gallery */
|
|
||||||
saveImageGallery(result.downloadURL,result.metadata.fullPath)
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle on change file upload
|
|
||||||
*/
|
|
||||||
onFileChange = (evt) => {
|
|
||||||
|
|
||||||
const extension = FileAPI.getExtension(evt.target.files[0].name)
|
|
||||||
let fileName = (`${uuid()}.${extension}`)
|
|
||||||
let image = FileAPI.constraintImage(evt.target.files[0], fileName)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide image gallery
|
|
||||||
*/
|
|
||||||
close = () => {
|
|
||||||
this.props.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
imageList = () => {
|
|
||||||
|
|
||||||
return this.props.images.map((image, index) => {
|
|
||||||
|
|
||||||
return (<GridTile
|
|
||||||
key={image.id}
|
|
||||||
title={<SvgDelete hoverColor={grey200} color="white" color="white" style={{ marginLeft: "5px", cursor: "pointer" }} onClick={evt => this.handleDeleteImage(evt, image.id)} />}
|
|
||||||
subtitle={<span></span>}
|
|
||||||
actionIcon={<SvgAddImage hoverColor={grey200} color="white" style={{ marginRight: "5px", cursor: "pointer" }} onClick={evt => this.handleSetImage(evt, image.URL,image.fullPath)} />}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div style={{ overflowY: "hidden", overflowX: "auto" }}>
|
|
||||||
<ul style={{ whiteSpace: "nowrap", padding: "0 6px", margin: "8px 0 0 0", verticalAlign: "bottom", flexShrink: 0, listStyleType: "none" }}>
|
|
||||||
<div style={{ display: "block" }}>
|
|
||||||
<div style={{ display: "block", marginRight: "8px", transition: "transform .25s" }}>
|
|
||||||
<li style={{ width: "100%", margin: 0, verticalAlign: "bottom", position: "static", display: "inline-block" }}>
|
|
||||||
<Img fileName={image.URL} style={{ width: "100%", height: "auto" }} />
|
|
||||||
|
|
||||||
</li>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</GridTile>)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the post text changed
|
|
||||||
* @param {event} evt is an event passed by change post text callback funciton
|
|
||||||
* @param {string} data is the post content which user writes
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component styles
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
const styles = {
|
|
||||||
root: {
|
|
||||||
display: 'flex',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
justifyContent: 'space-around',
|
|
||||||
},
|
|
||||||
gridList: {
|
|
||||||
width: 500,
|
|
||||||
height: 450,
|
|
||||||
overflowY: 'auto',
|
|
||||||
},
|
|
||||||
uploadButton: {
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
},
|
|
||||||
uploadInput: {
|
|
||||||
cursor: 'pointer',
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
opacity: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={styles.root}>
|
|
||||||
<GridList
|
|
||||||
cellHeight={180}
|
|
||||||
style={styles.gridList}
|
|
||||||
>
|
|
||||||
<GridTile >
|
|
||||||
|
|
||||||
<div style={{ display: "flex", backgroundColor: "rgba(222, 222, 222, 0.52)", flexDirection: "column", justifyContent: "center", alignItems: "center", height: "100%" }}>
|
|
||||||
|
|
||||||
<FlatButton
|
|
||||||
label="Upload Photo"
|
|
||||||
labelStyle={{ fontWeight: 100 }}
|
|
||||||
labelPosition="before"
|
|
||||||
style={styles.uploadButton}
|
|
||||||
containerElement="label"
|
|
||||||
>
|
|
||||||
<input type="file" onChange={this.onFileChange} accept="image/*" style={styles.uploadInput} />
|
|
||||||
</FlatButton>
|
|
||||||
</div>
|
|
||||||
</GridTile>
|
|
||||||
{this.imageList()}
|
|
||||||
</GridList>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map dispatch to props
|
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
|
||||||
return {
|
|
||||||
saveImageGallery: (imageURL,imageFullPath) => dispatch(imageGalleryActions.dbSaveImage(imageURL,imageFullPath)),
|
|
||||||
deleteImage: (id) => dispatch(imageGalleryActions.dbDeleteImage(id)),
|
|
||||||
progressChange : (percent,status) => dispatch(globalActions.progressChange(percent, status))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map state to props
|
|
||||||
* @param {object} state is the obeject from redux store
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
images: state.imageGallery.images,
|
|
||||||
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : ''
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Connect component to redux store
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ImageGallery)
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
import Master from './Master'
|
|
||||||
export default Master
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
// - Import react components
|
|
||||||
import React, {Component} from 'react'
|
|
||||||
import CircularProgress from 'material-ui/CircularProgress'
|
|
||||||
import Dialog from 'material-ui/Dialog'
|
|
||||||
|
|
||||||
// - Import app components
|
|
||||||
|
|
||||||
// - Create MasterLoading component class
|
|
||||||
export default class MasterLoading extends Component {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
|
|
||||||
// Binding functions to `this`
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Render app DOM component
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
modal={true}
|
|
||||||
open={this.props.activeLoading}
|
|
||||||
autoDetectWindowHeight={false}
|
|
||||||
overlayStyle={{backgroundColor: "white"}}
|
|
||||||
contentClassName="mLoading__content"
|
|
||||||
bodyStyle={{backgroundColor: ""}}
|
|
||||||
bodyClassName="mLoading__body"
|
|
||||||
>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div className="mLoading__context">
|
|
||||||
|
|
||||||
<CircularProgress color="white" size={80} thickness={7} />
|
|
||||||
<h1 style={{float:"right", color:"#fff"}}>Green</h1>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
// - Import react components
|
|
||||||
import React, {Component} from 'react'
|
|
||||||
|
|
||||||
// - Import components
|
|
||||||
|
|
||||||
// - Import actions
|
|
||||||
|
|
||||||
// - Create component class
|
|
||||||
export default class SidebarContent extends Component {
|
|
||||||
|
|
||||||
static qcName= 'SidebarContent'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component constructor
|
|
||||||
* @param {object} props is an object properties of component
|
|
||||||
*/
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
// Default state
|
|
||||||
this.state = {}
|
|
||||||
|
|
||||||
// Binding function to `this`
|
|
||||||
this.handleClickCover = this.handleClickCover.bind(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle click on cover of sidebar
|
|
||||||
* @param {event} evt is a click event passed to funciton
|
|
||||||
*/
|
|
||||||
handleClickCover = (evt) => {
|
|
||||||
this.props.sidebar(false,'overlay')
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reneder component DOM
|
|
||||||
* @return {react element} return the DOM which rendered by component
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
let showCoverStyle = {position: "fixed", height: "100%", width: "100%", top: "0px", left: "0px", opacity: "1", backgroundColor: "rgba(255, 255, 255, 0.54)", WebkitTapHighlightColor: "rgba(0, 0, 0, 0)", willChange: "opacity", transform: "translateZ(0px)", transition: "left 0ms cubic-bezier(0.23, 1, 0.32, 1) 0ms, opacity 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms", zIndex: "1111", pointerEvents: "auto"}
|
|
||||||
let hideCoverStyle = {position: "fixed", height: "100%", width: "100%", top: "0px", left: "-100%", opacity: "0", backgroundColor: "rgba(255, 255, 255, 0.54)", WebkitTapHighlightColor: "rgba(0, 0, 0, 0)", willChange: "opacity", transform: "translateZ(0px)", transition: "left 0ms cubic-bezier(0.23, 1, 0.32, 1) 400ms, opacity 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms", zIndex: "1111", pointerEvents: "none"}
|
|
||||||
return (
|
|
||||||
<div id='sidebar-content'>
|
|
||||||
<div style={this.props.overlay ? showCoverStyle : hideCoverStyle} style={{overflow:'hidden'}} onClick={this.handleClickCover}></div>
|
|
||||||
<div className={this.props.className} style={this.props.cstyle}>
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
// - Import react components
|
|
||||||
import React, {Component} from 'react'
|
|
||||||
|
|
||||||
// - Import components
|
|
||||||
|
|
||||||
// - Import actions
|
|
||||||
|
|
||||||
// - Create component class
|
|
||||||
export default class SidebarMain extends Component {
|
|
||||||
|
|
||||||
static qcName = 'SidebarMain'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component constructor
|
|
||||||
* @param {object} props is an object properties of component
|
|
||||||
*/
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
// Default state
|
|
||||||
this.state = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reneder component DOM
|
|
||||||
* @return {react element} return the DOM which rendered by component
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="home__main" style={this.props.cstyle} >
|
|
||||||
<div style={{height:"64px", width:"100%"}}></div>
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
// - Import react components
|
|
||||||
import React,{Component} from 'react'
|
|
||||||
import {connect} from 'react-redux'
|
|
||||||
import {push} from 'react-router-redux'
|
|
||||||
import {NavLink, withRouter} from 'react-router-dom'
|
|
||||||
import Paper from 'material-ui/Paper'
|
|
||||||
import TextField from 'material-ui/TextField'
|
|
||||||
import RaisedButton from 'material-ui/RaisedButton'
|
|
||||||
import FlatButton from 'material-ui/FlatButton'
|
|
||||||
|
|
||||||
// - Import actions
|
|
||||||
import * as authorizeActions from 'authorizeActions'
|
|
||||||
import * as globalActions from 'globalActions'
|
|
||||||
|
|
||||||
// - Import app API
|
|
||||||
import StringAPI from 'StringAPI'
|
|
||||||
|
|
||||||
|
|
||||||
// - Create Signup componet class
|
|
||||||
export class Signup extends Component {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component constructor
|
|
||||||
* @param {object} props is an object properties of component
|
|
||||||
*/
|
|
||||||
constructor(props){
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
fullNameInput: '',
|
|
||||||
fullNameInputError: '',
|
|
||||||
emailInput: '',
|
|
||||||
emailInputError: '',
|
|
||||||
passwordInput: '',
|
|
||||||
passwordInputError: '',
|
|
||||||
confirmInput: '',
|
|
||||||
confirmInputError: ''
|
|
||||||
|
|
||||||
}
|
|
||||||
// Binding function to `this`
|
|
||||||
this.handleForm = this.handleForm.bind(this)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle data on input change
|
|
||||||
* @param {event} evt is an event of inputs of element on change
|
|
||||||
*/
|
|
||||||
handleInputChange = (evt) => {
|
|
||||||
const target = evt.target
|
|
||||||
const value = target.type === 'checkbox' ? target.checked : target.value
|
|
||||||
const name = target.name
|
|
||||||
this.setState({
|
|
||||||
[name]: value
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
switch (name) {
|
|
||||||
case 'fullNameInput':
|
|
||||||
this.setState({
|
|
||||||
fullNameInputError: '',
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'emailInput':
|
|
||||||
this.setState({
|
|
||||||
emailInputError: ''
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'passwordInput':
|
|
||||||
this.setState({
|
|
||||||
confirmInputError: '',
|
|
||||||
passwordInputError: ''
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'confirmInput':
|
|
||||||
this.setState({
|
|
||||||
confirmInputError: '',
|
|
||||||
passwordInputError: ''
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'checkInput':
|
|
||||||
this.setState({
|
|
||||||
checkInputError: ''
|
|
||||||
})
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle register form
|
|
||||||
*/
|
|
||||||
handleForm = () => {
|
|
||||||
|
|
||||||
const {fullNameInput, emailInput, passwordInput, confirmInput} = this.state
|
|
||||||
const {register} = this.props
|
|
||||||
|
|
||||||
let error = false
|
|
||||||
|
|
||||||
// Validate full name
|
|
||||||
let fullNameCheck = fullNameInput.trim().toLowerCase()
|
|
||||||
|
|
||||||
if (fullNameCheck.indexOf('test') > -1
|
|
||||||
|| fullNameCheck.indexOf('demo') > -1
|
|
||||||
|| fullNameCheck.indexOf('asd') > -1
|
|
||||||
|| fullNameCheck.length < 4) {
|
|
||||||
this.setState({
|
|
||||||
fullNameInputError: 'Please enter a valid name.'
|
|
||||||
})
|
|
||||||
error = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate email*/
|
|
||||||
if (!StringAPI.isValidEmail(emailInput)) {
|
|
||||||
this.setState({
|
|
||||||
emailInputError: 'Please enter a valid email.'
|
|
||||||
})
|
|
||||||
error = true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check password */
|
|
||||||
if (passwordInput === '') {
|
|
||||||
this.setState({
|
|
||||||
passwordInputError: 'This field is required.'
|
|
||||||
})
|
|
||||||
error = true
|
|
||||||
|
|
||||||
}
|
|
||||||
if (confirmInput === '') {
|
|
||||||
this.setState({
|
|
||||||
confirmInputError: 'This field is required.'
|
|
||||||
})
|
|
||||||
error = true
|
|
||||||
|
|
||||||
}
|
|
||||||
else if(confirmInput !== passwordInput){
|
|
||||||
this.setState({
|
|
||||||
passwordInputError: 'This field sould be equal to confirm password.',
|
|
||||||
confirmInputError: 'This field sould be equal to password.'
|
|
||||||
})
|
|
||||||
error = true
|
|
||||||
|
|
||||||
}
|
|
||||||
if (!error) {
|
|
||||||
register({
|
|
||||||
email: emailInput,
|
|
||||||
password: passwordInput,
|
|
||||||
fullName: fullNameInput
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reneder component DOM
|
|
||||||
* @return {react element} return the DOM which rendered by component
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const paperStyle = {
|
|
||||||
minHeight: 500,
|
|
||||||
width: 450,
|
|
||||||
textAlign: 'center',
|
|
||||||
display: 'block',
|
|
||||||
margin: "auto"
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<h1 style={{
|
|
||||||
textAlign: "center",
|
|
||||||
padding:"20px",
|
|
||||||
fontSize: "30px",
|
|
||||||
fontWeight: 500,
|
|
||||||
lineHeight: "32px",
|
|
||||||
margin: "auto",
|
|
||||||
color: "rgba(138, 148, 138, 0.2)"
|
|
||||||
}}>Green</h1>
|
|
||||||
|
|
||||||
<div className="animate-bottom">
|
|
||||||
<Paper style={paperStyle} zDepth={1} rounded={false} >
|
|
||||||
<div style={{padding: "48px 40px 36px"}}>
|
|
||||||
<div style={{paddingLeft: "40px",
|
|
||||||
paddingRight: "40px"}}>
|
|
||||||
|
|
||||||
<h2 style={{
|
|
||||||
textAlign: "left",
|
|
||||||
paddingTop: "16px",
|
|
||||||
fontSize: "24px",
|
|
||||||
fontWeight: 400,
|
|
||||||
lineHeight: "32px",
|
|
||||||
margin: 0}}>Sign up</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TextField
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
errorText={this.state.fullNameInputError}
|
|
||||||
name="fullNameInput"
|
|
||||||
floatingLabelStyle={{fontSize:"15px"}}
|
|
||||||
floatingLabelText="Full Name"
|
|
||||||
type="text"
|
|
||||||
/><br />
|
|
||||||
<TextField
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
errorText={this.state.emailInputError}
|
|
||||||
name="emailInput"
|
|
||||||
floatingLabelStyle={{fontSize:"15px"}}
|
|
||||||
floatingLabelText="Email"
|
|
||||||
type="email"
|
|
||||||
/><br />
|
|
||||||
<TextField
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
errorText={this.state.passwordInputError }
|
|
||||||
name="passwordInput"
|
|
||||||
floatingLabelStyle={{fontSize:"15px"}}
|
|
||||||
floatingLabelText="Password"
|
|
||||||
type="password"
|
|
||||||
/><br />
|
|
||||||
<TextField
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
errorText={this.state.confirmInputError }
|
|
||||||
name="confirmInput"
|
|
||||||
floatingLabelStyle={{fontSize:"15px"}}
|
|
||||||
floatingLabelText="Confirm Password"
|
|
||||||
type="password"
|
|
||||||
/><br />
|
|
||||||
<br />
|
|
||||||
<div className="signup__button-box">
|
|
||||||
<div>
|
|
||||||
<FlatButton label="Login" onClick={this.props.loginPage} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<RaisedButton label="Create" primary={true} onClick={this.handleForm}/>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</Paper>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map dispatch to props
|
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapDispatchToProps = (dispatch,ownProps) => {
|
|
||||||
return {
|
|
||||||
showError: (message) => {
|
|
||||||
dispatch(action(types.SHOW_ERROR_MESSAGE_GLOBAL)(error.message))
|
|
||||||
},
|
|
||||||
register: (data) => {
|
|
||||||
dispatch(authorizeActions.dbSignup(data))
|
|
||||||
},
|
|
||||||
loginPage: () => {
|
|
||||||
dispatch(push("/login"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map state to props
|
|
||||||
* @param {object} state is the obeject from redux store
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapStateToProps = (state,ownProps) => {
|
|
||||||
return{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Connect component to redux store
|
|
||||||
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(Signup))
|
|
||||||
@@ -1,295 +0,0 @@
|
|||||||
// - Import react components
|
|
||||||
import React, { Component } from 'react'
|
|
||||||
import { connect } from 'react-redux'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {push} from 'react-router-redux'
|
|
||||||
import Paper from 'material-ui/Paper'
|
|
||||||
import FlatButton from 'material-ui/FlatButton'
|
|
||||||
import RaisedButton from 'material-ui/RaisedButton'
|
|
||||||
import Popover, { PopoverAnimationVertical } from 'material-ui/Popover'
|
|
||||||
import Menu from 'material-ui/Menu'
|
|
||||||
import MenuItem from 'material-ui/MenuItem'
|
|
||||||
import Checkbox from 'material-ui/Checkbox'
|
|
||||||
import TextField from 'material-ui/TextField'
|
|
||||||
|
|
||||||
// - Import app components
|
|
||||||
import UserAvatar from 'UserAvatar'
|
|
||||||
|
|
||||||
|
|
||||||
// - Import API
|
|
||||||
import CircleAPI from 'CircleAPI'
|
|
||||||
|
|
||||||
|
|
||||||
// - Import actions
|
|
||||||
import * as circleActions from 'circleActions'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create component class
|
|
||||||
*/
|
|
||||||
export class UserBox extends Component {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
/**
|
|
||||||
* User identifier
|
|
||||||
*/
|
|
||||||
userId: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* User information
|
|
||||||
*/
|
|
||||||
user:PropTypes.object
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component constructor
|
|
||||||
* @param {object} props is an object properties of component
|
|
||||||
*/
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
//Defaul state
|
|
||||||
this.state = {
|
|
||||||
/**
|
|
||||||
* It will be true if user follow popover is open
|
|
||||||
*/
|
|
||||||
open: false,
|
|
||||||
/**
|
|
||||||
* The value of circle input
|
|
||||||
*/
|
|
||||||
circleName: '',
|
|
||||||
/**
|
|
||||||
* It will be true if the text field for adding group is empty
|
|
||||||
*/
|
|
||||||
disabledAddCircle: true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binding functions to `this`
|
|
||||||
this.handleChangeName = this.handleChangeName.bind(this)
|
|
||||||
this.handleCreateCricle = this.handleCreateCricle.bind(this)
|
|
||||||
this.handleFollowUser = this.handleFollowUser.bind(this)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
handleFollowUser = (checked,cid) => {
|
|
||||||
const {userId,user} = this.props
|
|
||||||
const {avatar,fullName} = user
|
|
||||||
if (checked) {
|
|
||||||
this.props.addFollowingUser(cid,{avatar,userId,fullName})
|
|
||||||
} else {
|
|
||||||
this.props.deleteFollowingUser(cid,userId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle create circle
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof UserBox
|
|
||||||
*/
|
|
||||||
handleCreateCricle = () => {
|
|
||||||
const {circleName} = this.state
|
|
||||||
if(circleName && circleName.trim() !== '' ){
|
|
||||||
this.props.createCircle(this.state.circleName)
|
|
||||||
this.setState({
|
|
||||||
circleName: '',
|
|
||||||
disabledAddCircle: true
|
|
||||||
})}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle change group name input to the state
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof UserBox
|
|
||||||
*/
|
|
||||||
handleChangeName = (evt) => {
|
|
||||||
this.setState({
|
|
||||||
circleName: evt.target.value,
|
|
||||||
disabledAddCircle: (evt.target.value === undefined || evt.target.value.trim() === '')
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle touch tab on follow popover
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof UserBox
|
|
||||||
*/
|
|
||||||
handleTouchTap = (evt) => {
|
|
||||||
// This prevents ghost click.
|
|
||||||
evt.preventDefault()
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
open: true,
|
|
||||||
anchorEl: evt.currentTarget,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle close follow popover
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof UserBox
|
|
||||||
*/
|
|
||||||
handleRequestClose = () => {
|
|
||||||
this.setState({
|
|
||||||
open: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
circleList = () => {
|
|
||||||
let { circles, userId, userBelongCircles } = this.props
|
|
||||||
|
|
||||||
if (circles) {
|
|
||||||
|
|
||||||
return Object.keys(circles).map((key, index) => {
|
|
||||||
if(key.trim() !== '-Followers'){
|
|
||||||
let isBelong = userBelongCircles.indexOf(key) > -1
|
|
||||||
|
|
||||||
return <Checkbox
|
|
||||||
key={key}
|
|
||||||
style={{ padding: '10px' }}
|
|
||||||
label={circles[key].name}
|
|
||||||
labelStyle={{
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
width: '100%'
|
|
||||||
}}
|
|
||||||
onCheck={(evt,checked) => this.handleFollowUser(checked,key)}
|
|
||||||
checked={isBelong}
|
|
||||||
/>}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reneder component DOM
|
|
||||||
* @return {react element} return the DOM which rendered by component
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
paper: {
|
|
||||||
height: 254,
|
|
||||||
width: 243,
|
|
||||||
margin: 10,
|
|
||||||
textAlign: 'center',
|
|
||||||
maxWidth: '257px'
|
|
||||||
},
|
|
||||||
followButton: {
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: '8px',
|
|
||||||
left: 0,
|
|
||||||
right: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper style={styles.paper} zDepth={1} className='grid-cell'>
|
|
||||||
<div style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
height: '100%',
|
|
||||||
position: 'relative',
|
|
||||||
padding: '30px'
|
|
||||||
|
|
||||||
}}>
|
|
||||||
<div onClick={() => this.props.goTo(`/${this.props.userId}`)} style={{cursor:'pointer'}}>
|
|
||||||
<UserAvatar
|
|
||||||
fullName={this.props.fullName}
|
|
||||||
fileName={this.props.avatar}
|
|
||||||
size={90}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div onClick={() => this.props.goTo(`/${this.props.userId}`)} className='people__name' style={{cursor:'pointer'}}>
|
|
||||||
<div>
|
|
||||||
{this.props.user.fullName}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={styles.followButton}>
|
|
||||||
<FlatButton
|
|
||||||
label={(this.props.belongCirclesCount && this.props.belongCirclesCount < 1) ? 'Follow'
|
|
||||||
: (this.props.belongCirclesCount > 1 ? `${this.props.belongCirclesCount} Circles` : ((this.props.firstBelongCircle) ? this.props.firstBelongCircle.name : 'Follow'))}
|
|
||||||
primary={true}
|
|
||||||
onTouchTap={this.handleTouchTap}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Popover
|
|
||||||
open={this.state.open}
|
|
||||||
anchorEl={this.state.anchorEl}
|
|
||||||
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
|
|
||||||
targetOrigin={{ horizontal: 'left', vertical: 'top' }}
|
|
||||||
onRequestClose={this.handleRequestClose}
|
|
||||||
animation={PopoverAnimationVertical}
|
|
||||||
>
|
|
||||||
<Menu >
|
|
||||||
<div style={{
|
|
||||||
position: 'relative',
|
|
||||||
display: 'block',
|
|
||||||
maxHeight: '220px'
|
|
||||||
}}>
|
|
||||||
<div style={{ overflowY: 'auto', height: '100%' }}>
|
|
||||||
{this.circleList()}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ padding: '10px' }}>
|
|
||||||
<TextField
|
|
||||||
hintText="Group name"
|
|
||||||
onChange={this.handleChangeName}
|
|
||||||
value={this.state.circleName}
|
|
||||||
/><br />
|
|
||||||
<FlatButton label="ADD" primary={true} disabled={this.state.disabledAddCircle} onClick={this.handleCreateCricle} />
|
|
||||||
</div>
|
|
||||||
</Menu>
|
|
||||||
</Popover>
|
|
||||||
</Paper>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map dispatch to props
|
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
|
||||||
return {
|
|
||||||
createCircle: (name) => dispatch(circleActions.dbAddCircle(name)),
|
|
||||||
addFollowingUser: (cid,user) => dispatch(circleActions.dbAddFollowingUser(cid,user)),
|
|
||||||
deleteFollowingUser: (cid,followingId) => dispatch(circleActions.dbDeleteFollowingUser(cid,followingId)),
|
|
||||||
goTo: (url)=> dispatch(push(url))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map state to props
|
|
||||||
* @param {object} state is the obeject from redux store
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
|
||||||
const { uid } = state.authorize
|
|
||||||
const circles = state.circle ? (state.circle.userCircles[uid]|| {}) : {}
|
|
||||||
const userBelongCircles = CircleAPI.getUserBelongCircles(circles,ownProps.userId)
|
|
||||||
|
|
||||||
return {
|
|
||||||
circles: circles,
|
|
||||||
userBelongCircles: userBelongCircles || [],
|
|
||||||
belongCirclesCount: userBelongCircles.length || 0,
|
|
||||||
firstBelongCircle: userBelongCircles ? (circles ? circles[userBelongCircles[0]] : {}) : {},
|
|
||||||
avatar: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].avatar || '' : '',
|
|
||||||
fullName: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].fullName || '' : ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Connect component to redux store
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(UserBox)
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
// - Import react components
|
|
||||||
import React, {Component} from 'react'
|
|
||||||
|
|
||||||
|
|
||||||
// - Import components
|
|
||||||
|
|
||||||
// - Import actions
|
|
||||||
|
|
||||||
// - Create component class
|
|
||||||
export default class {component name} extends Component {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component constructor
|
|
||||||
* @param {object} props is an object properties of component
|
|
||||||
*/
|
|
||||||
constructor(props){
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
// Default state
|
|
||||||
this.state = {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reneder component DOM
|
|
||||||
* @return {react element} return the DOM which rendered by component
|
|
||||||
*/
|
|
||||||
render(){
|
|
||||||
return(
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map dispatch to props
|
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapDispatchToProps = (dispatch,ownProps) => {
|
|
||||||
return{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map state to props
|
|
||||||
* @param {object} state is the obeject from redux store
|
|
||||||
* @param {object} ownProps is the props belong to component
|
|
||||||
* @return {object} props of component
|
|
||||||
*/
|
|
||||||
const mapStateToProps = (state,ownProps) => {
|
|
||||||
return{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// - Connect component to redux store
|
|
||||||
export default connect(mapStateToProps,mapDispatchToProps)(Sidebar)
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
// - Import react components
|
|
||||||
import { firebaseRef, firebaseAuth } from 'app/firebaseClient/'
|
|
||||||
|
|
||||||
import { SocialError } from 'domain/common'
|
|
||||||
import { Notification } from 'domain/notifications'
|
|
||||||
import { INotificationService } from 'services/notifications'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Firbase notification service
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @class NotificationService
|
|
||||||
* @implements {INotificationService}
|
|
||||||
*/
|
|
||||||
export class NotificationService implements INotificationService {
|
|
||||||
|
|
||||||
public addNotification: (notification: Notification)
|
|
||||||
=> Promise<void> = (notification: Notification) => {
|
|
||||||
return new Promise<void>((resolve,reject) => {
|
|
||||||
firebaseRef.child(`userNotifies/${notification.notifyRecieverUserId}`)
|
|
||||||
.push(notification)
|
|
||||||
.then(() => {
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
reject(new SocialError(error.code, error.message))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
public getNotifications: (userId: string)
|
|
||||||
=> Promise<{ [notifyId: string]: Notification }> = (userId) => {
|
|
||||||
return new Promise<{ [notifyId: string]: Notification }>((resolve,reject) => {
|
|
||||||
let notifiesRef: any = firebaseRef.child(`userNotifies/${userId}`)
|
|
||||||
notifiesRef.on('value', (snapshot: any) => {
|
|
||||||
let notifies: {[notifyId: string]: Notification} = snapshot.val() || {}
|
|
||||||
resolve(notifies)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
public deleteNotification: (notificationId: string, userId: string)
|
|
||||||
=> Promise <void> = (notificationId, userId) => {
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
let updates: any = {}
|
|
||||||
updates[`userNotifies/${userId}/${notificationId}`] = null
|
|
||||||
firebaseRef.update(updates)
|
|
||||||
.then(() => {
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
reject(new SocialError(error.code, error.message))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
public setSeenNotification: (notificationId: string, userId: string, notification: Notification)
|
|
||||||
=> Promise <void> = (notificationId, userId, notification) => {
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
let updates: any = {}
|
|
||||||
updates[`userNotifies/${userId}/${notificationId}`] = notification
|
|
||||||
firebaseRef.update(updates)
|
|
||||||
.then(() => {
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
reject(new SocialError(error.code, error.message))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* Global state
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @class GlobalState
|
|
||||||
*/
|
|
||||||
export class GlobalState {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set percent of loading progress and visibility for Master component
|
|
||||||
*
|
|
||||||
* @type {{
|
|
||||||
* percent: number,
|
|
||||||
* visible: Boolean
|
|
||||||
* }}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
progress: {
|
|
||||||
percent: number
|
|
||||||
visible: Boolean
|
|
||||||
} = {
|
|
||||||
percent : 0,
|
|
||||||
visible : false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If loading is enabled {true} or not false
|
|
||||||
*
|
|
||||||
* @type {Boolean}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
loadingStatus: Boolean = true
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If user date is loaded {true} or not {false}
|
|
||||||
*
|
|
||||||
* @type {Boolean}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
defaultLoadDataStatus: Boolean = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If message popup is open {true} or not {false}
|
|
||||||
*
|
|
||||||
* @type {Boolean}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
messageOpen: Boolean = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The text of popup global message
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
message: string = ''
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Window size
|
|
||||||
*
|
|
||||||
* @type {number}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
windowWidth: number = 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Window height
|
|
||||||
*
|
|
||||||
* @type {number}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
windowHeight: number = 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The text of website header
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
headerTitle: string = ''
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Top loading is visible {true} or not {false}
|
|
||||||
*
|
|
||||||
* @type {Boolean}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
showTopLoading: Boolean = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Top loading message queue
|
|
||||||
*
|
|
||||||
* @type {number}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
topLoadingQueue: number = 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temp date storage
|
|
||||||
*
|
|
||||||
* @type {*}
|
|
||||||
* @memberof IGlobalState
|
|
||||||
*/
|
|
||||||
temp: any = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
let React = require('react')
|
|
||||||
let ReactDOM = require('react-dom')
|
|
||||||
let TestUtils = require('react-dom/test-utils')
|
|
||||||
let expect = require('expect')
|
|
||||||
let $ = require('jquery')
|
|
||||||
|
|
||||||
let {CommentWrite} = require('CommentWrite')
|
|
||||||
|
|
||||||
describe('CommentWrite', () => {
|
|
||||||
it('should exist', () => {
|
|
||||||
expect(CommentWrite).toExist()
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"inversify": "^4.3.0",
|
"inversify": "^4.3.0",
|
||||||
"keycode": "^2.1.9",
|
"keycode": "^2.1.9",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"material-ui": "^0.19.3",
|
"material-ui": "^0.19.4",
|
||||||
"moment": "^2.18.1",
|
"moment": "^2.18.1",
|
||||||
"morgan": "^1.8.1",
|
"morgan": "^1.8.1",
|
||||||
"node-env-file": "^0.1.8",
|
"node-env-file": "^0.1.8",
|
||||||
@@ -60,13 +60,16 @@
|
|||||||
"@types/lodash": "^4.14.77",
|
"@types/lodash": "^4.14.77",
|
||||||
"@types/material-ui": "^0.18.2",
|
"@types/material-ui": "^0.18.2",
|
||||||
"@types/node": "^8.0.33",
|
"@types/node": "^8.0.33",
|
||||||
|
"@types/prop-types": "^15.5.2",
|
||||||
"@types/react": "^16.0.10",
|
"@types/react": "^16.0.10",
|
||||||
"@types/react-dom": "^16.0.1",
|
"@types/react-dom": "^16.0.1",
|
||||||
|
"@types/react-event-listener": "^0.4.4",
|
||||||
"@types/react-redux": "^5.0.10",
|
"@types/react-redux": "^5.0.10",
|
||||||
"@types/react-router-dom": "^4.0.8",
|
"@types/react-router-dom": "^4.0.8",
|
||||||
"@types/react-router-redux": "^5.0.8",
|
"@types/react-router-redux": "^5.0.8",
|
||||||
"@types/react-tap-event-plugin": "0.0.30",
|
"@types/react-tap-event-plugin": "0.0.30",
|
||||||
"@types/redux-logger": "^3.0.4",
|
"@types/redux-logger": "^3.0.4",
|
||||||
|
"@types/uuid": "^3.4.3",
|
||||||
"@types/webpack": "^3.0.13",
|
"@types/webpack": "^3.0.13",
|
||||||
"babel-core": "^6.24.1",
|
"babel-core": "^6.24.1",
|
||||||
"babel-loader": "^7.1.2",
|
"babel-loader": "^7.1.2",
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
var moment = require('moment');
|
|
||||||
|
|
||||||
console.log(moment().format());
|
|
||||||
|
|
||||||
// January 1st 1970 @ 12:00am -> 0
|
|
||||||
// January 1st 1970 @ 12:01am -> -60
|
|
||||||
|
|
||||||
var now = moment();
|
|
||||||
console.log('Current timestamp', now.unix());
|
|
||||||
|
|
||||||
var timestamp = 1459111648;
|
|
||||||
var currentMoment = moment.unix(timestamp);
|
|
||||||
console.log('current moment', currentMoment.format('MMM D, YY @ h:mm a'));
|
|
||||||
|
|
||||||
// January 3rd, 2016 @ 12:13 AM
|
|
||||||
console.log('current moment', currentMoment.format('MMMM Do, YYYY @ h:mm A'));
|
|
||||||
@@ -1,24 +1,27 @@
|
|||||||
|
|
||||||
// - Import react components
|
// - Import react components
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { push } from 'react-router-redux'
|
import { push } from 'react-router-redux'
|
||||||
|
|
||||||
// -Import domain
|
// -Import domain
|
||||||
import { User } from 'domain/users'
|
import { User } from 'core/domain/users'
|
||||||
import { SocialError } from 'domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
|
import { UserRegisterModel } from 'models/users/userRegisterModel'
|
||||||
|
|
||||||
// - Import action types
|
// - Import action types
|
||||||
import { AuthorizeActionType } from 'constants/authorizeActionType'
|
import { AuthorizeActionType } from 'constants/authorizeActionType'
|
||||||
|
|
||||||
// - Import services
|
// - Import services
|
||||||
import { IAuthorizeService } from 'services/authorize'
|
import { IAuthorizeService } from 'core/services/authorize'
|
||||||
import { IServiceProvider, ServiceProvide } from 'factories'
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
|
|
||||||
const serviceProvider: IServiceProvider = new ServiceProvide()
|
|
||||||
const authorizeService: IAuthorizeService = serviceProvider.createAuthorizeService()
|
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as globalActions from 'actions/globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
|
|
||||||
|
const serviceProvider: IServiceProvider = new ServiceProvide()
|
||||||
|
const authorizeService: IAuthorizeService = serviceProvider.createAuthorizeService()
|
||||||
|
|
||||||
/* _____________ CRUD DB _____________ */
|
/* _____________ CRUD DB _____________ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,10 +58,15 @@ export const dbLogout = () => {
|
|||||||
*
|
*
|
||||||
* @param user for registering
|
* @param user for registering
|
||||||
*/
|
*/
|
||||||
export const dbSignup = (user: User) => {
|
export const dbSignup = (user: UserRegisterModel) => {
|
||||||
return (dispatch: any, getState: any) => {
|
return (dispatch: Function, getState: Function) => {
|
||||||
dispatch(globalActions.showNotificationRequest())
|
dispatch(globalActions.showNotificationRequest())
|
||||||
return authorizeService.registerUser(user).then((result) => {
|
let newUser = new User()
|
||||||
|
newUser.email = user.email
|
||||||
|
newUser.password = user.password
|
||||||
|
newUser.fullName = user.fullName
|
||||||
|
|
||||||
|
return authorizeService.registerUser(newUser).then((result) => {
|
||||||
dispatch(signup({
|
dispatch(signup({
|
||||||
userId: result.uid,
|
userId: result.uid,
|
||||||
...user
|
...user
|
||||||
@@ -123,7 +131,7 @@ export const logout = () => {
|
|||||||
* User registeration call
|
* User registeration call
|
||||||
* @param user for registering
|
* @param user for registering
|
||||||
*/
|
*/
|
||||||
export const signup = (user: User) => {
|
export const signup = (user: UserRegisterModel) => {
|
||||||
return {
|
return {
|
||||||
type: AuthorizeActionType.SIGNUP,
|
type: AuthorizeActionType.SIGNUP,
|
||||||
payload: { ...user }
|
payload: { ...user }
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// - Import domain
|
// - Import domain
|
||||||
import { User } from 'domain/users'
|
import { User } from 'core/domain/users'
|
||||||
import { Circle, UserFollower } from 'domain/circles'
|
import { Circle, UserFollower } from 'core/domain/circles'
|
||||||
import { SocialError } from 'domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
// - Import utility components
|
// - Import utility components
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
@@ -15,8 +15,8 @@ import * as postActions from 'actions/postActions'
|
|||||||
import * as userActions from 'actions/userActions'
|
import * as userActions from 'actions/userActions'
|
||||||
import * as notifyActions from 'actions/notifyActions'
|
import * as notifyActions from 'actions/notifyActions'
|
||||||
|
|
||||||
import { IServiceProvider,ServiceProvide } from 'factories'
|
import { IServiceProvider,ServiceProvide } from 'core/factories'
|
||||||
import { ICircleService } from 'services/circles'
|
import { ICircleService } from 'core/services/circles'
|
||||||
|
|
||||||
const serviceProvider: IServiceProvider = new ServiceProvide()
|
const serviceProvider: IServiceProvider = new ServiceProvide()
|
||||||
const circleService: ICircleService = serviceProvider.createCircleService()
|
const circleService: ICircleService = serviceProvider.createCircleService()
|
||||||
@@ -51,7 +51,7 @@ export let dbAddCircle = (circleName: string) => {
|
|||||||
* @param {string} cid is circle identifier
|
* @param {string} cid is circle identifier
|
||||||
* @param {User} userFollowing is the user for following
|
* @param {User} userFollowing is the user for following
|
||||||
*/
|
*/
|
||||||
export let dbAddFollowingUser = (cid: string, userFollowing: User) => {
|
export let dbAddFollowingUser = (cid: string, userFollowing: UserFollower) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
|
|
||||||
let uid: string = getState().authorize.uid
|
let uid: string = getState().authorize.uid
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
// - Import domain
|
// - Import domain
|
||||||
import { Comment } from 'domain/comments'
|
import { Comment } from 'core/domain/comments'
|
||||||
import { SocialError } from 'domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
// - Import action types
|
// - Import action types
|
||||||
import { CommentActionType } from 'constants/commentActionType'
|
import { CommentActionType } from 'constants/commentActionType'
|
||||||
@@ -12,8 +12,8 @@ import { CommentActionType } from 'constants/commentActionType'
|
|||||||
import * as globalActions from 'actions/globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
import * as notifyActions from 'actions/notifyActions'
|
import * as notifyActions from 'actions/notifyActions'
|
||||||
|
|
||||||
import { IServiceProvider, ServiceProvide } from 'factories'
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
import { ICommentService } from 'services/comments'
|
import { ICommentService } from 'core/services/comments'
|
||||||
|
|
||||||
const serviceProvider: IServiceProvider = new ServiceProvide()
|
const serviceProvider: IServiceProvider = new ServiceProvide()
|
||||||
const commentService: ICommentService = serviceProvider.createCommentService()
|
const commentService: ICommentService = serviceProvider.createCommentService()
|
||||||
@@ -26,7 +26,7 @@ const commentService: ICommentService = serviceProvider.createCommentService()
|
|||||||
* @param {object} newComment user comment
|
* @param {object} newComment user comment
|
||||||
* @param {function} callBack will be fired when server responsed
|
* @param {function} callBack will be fired when server responsed
|
||||||
*/
|
*/
|
||||||
export const dbAddComment = (ownerPostUserId: string,newComment: Comment, callBack: Function) => {
|
export const dbAddComment = (ownerPostUserId: string | null,newComment: Comment, callBack: Function) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
|
|
||||||
dispatch(globalActions.showTopLoading())
|
dispatch(globalActions.showTopLoading())
|
||||||
@@ -74,14 +74,9 @@ export const dbGetComments = () => {
|
|||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
let uid: string = getState().authorize.uid
|
let uid: string = getState().authorize.uid
|
||||||
if (uid) {
|
if (uid) {
|
||||||
|
return commentService.getComments((comments: {[postId: string]: {[commentId: string]: Comment}}) => {
|
||||||
return commentService.getComments()
|
|
||||||
.then((comments: {[postId: string]: {[commentId: string]: Comment}}) => {
|
|
||||||
dispatch(addCommentList(comments))
|
dispatch(addCommentList(comments))
|
||||||
})
|
})
|
||||||
.catch((error: SocialError) => {
|
|
||||||
dispatch(globalActions.showErrorMessage(error.message))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,24 +126,20 @@ export const dbUpdateComment = (id: string, postId: string, text: string) => {
|
|||||||
* @param {string} id of comment
|
* @param {string} id of comment
|
||||||
* @param {string} postId is the identifier of the post which comment belong to
|
* @param {string} postId is the identifier of the post which comment belong to
|
||||||
*/
|
*/
|
||||||
export const dbDeleteComment = (id: string, postId: string) => {
|
export const dbDeleteComment = (id?: string | null, postId?: string) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
|
|
||||||
if (id === undefined || id === null) {
|
if (id === undefined || id === null) {
|
||||||
dispatch(globalActions.showErrorMessage('comment id can not be null or undefined'))
|
dispatch(globalActions.showErrorMessage('comment id can not be null or undefined'))
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('====================================')
|
|
||||||
console.log(id,postId)
|
|
||||||
console.log('====================================')
|
|
||||||
dispatch(globalActions.showTopLoading())
|
dispatch(globalActions.showTopLoading())
|
||||||
|
|
||||||
// Get current user id
|
// Get current user id
|
||||||
let uid: string = getState().authorize.uid
|
let uid: string = getState().authorize.uid
|
||||||
|
|
||||||
return commentService.deleteComment(id,postId)
|
return commentService.deleteComment(id!,postId!)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch(deleteComment(id, postId))
|
dispatch(deleteComment(id!, postId!))
|
||||||
dispatch(globalActions.hideTopLoading())
|
dispatch(globalActions.hideTopLoading())
|
||||||
|
|
||||||
}, (error: SocialError) => {
|
}, (error: SocialError) => {
|
||||||
@@ -146,7 +146,7 @@ export const hideTopLoading = () => {
|
|||||||
/**
|
/**
|
||||||
* Store temp data
|
* Store temp data
|
||||||
*/
|
*/
|
||||||
export const temp = (data: string) => {
|
export const temp = (data: any) => {
|
||||||
return{
|
return{
|
||||||
type: GlobalActionType.TEMP,
|
type: GlobalActionType.TEMP,
|
||||||
payload: data
|
payload: data
|
||||||
@@ -154,6 +154,16 @@ export const temp = (data: string) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear temp data
|
||||||
|
*/
|
||||||
|
export const clearTemp = () => {
|
||||||
|
return{
|
||||||
|
type: GlobalActionType.CLEAR_TEMP
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// - Load data for guest
|
// - Load data for guest
|
||||||
export const loadDataGuest = () => {
|
export const loadDataGuest = () => {
|
||||||
// tslint:disable-next-line:no-empty
|
// tslint:disable-next-line:no-empty
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
// - Import domain
|
// - Import domain
|
||||||
import { Image } from 'domain/imageGallery'
|
import { Image } from 'core/domain/imageGallery'
|
||||||
import { SocialError } from 'domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
// - Import action types
|
// - Import action types
|
||||||
import { ImageGalleryActionType } from 'constants/imageGalleryActionType'
|
import { ImageGalleryActionType } from 'constants/imageGalleryActionType'
|
||||||
@@ -14,8 +14,8 @@ import * as globalActions from 'actions/globalActions'
|
|||||||
// - Import app API
|
// - Import app API
|
||||||
import FileAPI from 'api/FileAPI'
|
import FileAPI from 'api/FileAPI'
|
||||||
|
|
||||||
import { IServiceProvider, ServiceProvide } from 'factories'
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
import { IImageGalleryService } from 'services/imageGallery'
|
import { IImageGalleryService } from 'core/services/imageGallery'
|
||||||
|
|
||||||
const serviceProvider: IServiceProvider = new ServiceProvide()
|
const serviceProvider: IServiceProvider = new ServiceProvide()
|
||||||
const imageGalleryService: IImageGalleryService = serviceProvider.createImageGalleryService()
|
const imageGalleryService: IImageGalleryService = serviceProvider.createImageGalleryService()
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
// - Import domain
|
// - Import domain
|
||||||
import { Notification } from 'domain/notifications'
|
import { Notification } from 'core/domain/notifications'
|
||||||
import { SocialError } from 'domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
// - Import action types
|
// - Import action types
|
||||||
import { NotificationActionType } from 'constants/notificationActionType'
|
import { NotificationActionType } from 'constants/notificationActionType'
|
||||||
@@ -12,8 +12,8 @@ import { NotificationActionType } from 'constants/notificationActionType'
|
|||||||
import * as globalActions from 'actions/globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
import * as userActions from 'actions/userActions'
|
import * as userActions from 'actions/userActions'
|
||||||
|
|
||||||
import { IServiceProvider, ServiceProvide } from 'factories'
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
import { INotificationService } from 'services/notifications'
|
import { INotificationService } from 'core/services/notifications'
|
||||||
|
|
||||||
const serviceProvider: IServiceProvider = new ServiceProvide()
|
const serviceProvider: IServiceProvider = new ServiceProvide()
|
||||||
const notificationService: INotificationService = serviceProvider.createNotificationService()
|
const notificationService: INotificationService = serviceProvider.createNotificationService()
|
||||||
@@ -50,18 +50,17 @@ export const dbAddNotification = (newNotify: Notification) => {
|
|||||||
* Get all notificaitions from database
|
* Get all notificaitions from database
|
||||||
*/
|
*/
|
||||||
export const dbGetNotifications = () => {
|
export const dbGetNotifications = () => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: Function , getState: Function) => {
|
||||||
let uid: string = getState().authorize.uid
|
let uid: string = getState().authorize.uid
|
||||||
if (uid) {
|
if (uid) {
|
||||||
|
return notificationService.getNotifications(uid,
|
||||||
return notificationService.getNotifications(uid)
|
(notifications: { [notifyId: string]: Notification} ) => {
|
||||||
.then((notifies: { [notifyId: string]: Notification }) => {
|
Object.keys(notifications).forEach((key => {
|
||||||
Object.keys(notifies).forEach((key => {
|
if (!getState().user.info[notifications[key].notifierUserId]) {
|
||||||
if (!getState().user.info[notifies[key].notifierUserId]) {
|
dispatch(userActions.dbGetUserInfoByUserId(notifications[key].notifierUserId,''))
|
||||||
dispatch(userActions.dbGetUserInfoByUserId(notifies[key].notifierUserId,''))
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
dispatch(addNotifyList(notifies))
|
dispatch(addNotifyList(notifications))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
import { Action } from 'redux'
|
import { Action } from 'redux'
|
||||||
|
|
||||||
// - Import domain
|
// - Import domain
|
||||||
import { Post } from 'domain/posts'
|
import { Post } from 'core/domain/posts'
|
||||||
import { SocialError } from 'domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
// - Import utility components
|
// - Import utility components
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
@@ -14,8 +14,8 @@ import { PostActionType } from 'constants/postActionType'
|
|||||||
// - Import actions
|
// - Import actions
|
||||||
import * as globalActions from 'actions/globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
|
|
||||||
import { IServiceProvider, ServiceProvide } from 'factories'
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
import { IPostService } from 'services/posts'
|
import { IPostService } from 'core/services/posts'
|
||||||
|
|
||||||
const serviceProvider: IServiceProvider = new ServiceProvide()
|
const serviceProvider: IServiceProvider = new ServiceProvide()
|
||||||
const postService: IPostService = serviceProvider.createPostService()
|
const postService: IPostService = serviceProvider.createPostService()
|
||||||
@@ -68,7 +68,7 @@ export let dbAddPost = (newPost: any, callBack: Function) => {
|
|||||||
* @param {object} newPost
|
* @param {object} newPost
|
||||||
* @param {function} callBack
|
* @param {function} callBack
|
||||||
*/
|
*/
|
||||||
export const dbAddImagePost = (newPost: any, callBack: Function) => {
|
export const dbAddImagePost = (newPost: Post, callBack: Function) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
|
|
||||||
dispatch(globalActions.showTopLoading())
|
dispatch(globalActions.showTopLoading())
|
||||||
@@ -82,8 +82,8 @@ export const dbAddImagePost = (newPost: any, callBack: Function) => {
|
|||||||
viewCount: 0,
|
viewCount: 0,
|
||||||
body: newPost.body,
|
body: newPost.body,
|
||||||
ownerUserId: uid,
|
ownerUserId: uid,
|
||||||
ownerDisplayName: newPost.name,
|
ownerDisplayName: newPost.ownerDisplayName,
|
||||||
ownerAvatar: newPost.avatar,
|
ownerAvatar: newPost.ownerAvatar,
|
||||||
lastEditDate: 0,
|
lastEditDate: 0,
|
||||||
tags: newPost.tags || [],
|
tags: newPost.tags || [],
|
||||||
commentCounter: 0,
|
commentCounter: 0,
|
||||||
@@ -115,7 +115,7 @@ export const dbAddImagePost = (newPost: any, callBack: Function) => {
|
|||||||
* @param {object} newPost
|
* @param {object} newPost
|
||||||
* @param {func} callBack //TODO: anti pattern should change to parent state or move state to redux
|
* @param {func} callBack //TODO: anti pattern should change to parent state or move state to redux
|
||||||
*/
|
*/
|
||||||
export const dbUpdatePost = (newPost: any, callBack: Function) => {
|
export const dbUpdatePost = (newPost: Post, callBack: Function) => {
|
||||||
console.log(newPost)
|
console.log(newPost)
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ export const dbUpdatePost = (newPost: any, callBack: Function) => {
|
|||||||
|
|
||||||
// Write the new data simultaneously in the list
|
// Write the new data simultaneously in the list
|
||||||
let updates: any = {}
|
let updates: any = {}
|
||||||
let post: Post = getState().post.userPosts[uid][newPost.id]
|
let post: Post = getState().post.userPosts[uid][newPost.id!]
|
||||||
let updatedPost: Post = {
|
let updatedPost: Post = {
|
||||||
postTypeId: post.postTypeId,
|
postTypeId: post.postTypeId,
|
||||||
creationDate: post.creationDate,
|
creationDate: post.creationDate,
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
// - Import react components
|
// - Import react components
|
||||||
|
|
||||||
// - Import domain
|
// - Import domain
|
||||||
import { Profile } from 'domain/users'
|
import { Profile } from 'core/domain/users'
|
||||||
import { SocialError } from 'domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
// - Import action types
|
// - Import action types
|
||||||
import { UserActionType } from 'constants/userActionType'
|
import { UserActionType } from 'constants/userActionType'
|
||||||
@@ -11,8 +11,8 @@ import { UserActionType } from 'constants/userActionType'
|
|||||||
import * as globalActions from 'actions/globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
import * as userActions from 'actions/userActions'
|
import * as userActions from 'actions/userActions'
|
||||||
|
|
||||||
import { IServiceProvider, ServiceProvide } from 'factories'
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
import { IUserService } from 'services/users'
|
import { IUserService } from 'core/services/users'
|
||||||
|
|
||||||
const serviceProvider: IServiceProvider = new ServiceProvide()
|
const serviceProvider: IServiceProvider = new ServiceProvide()
|
||||||
const userService: IUserService = serviceProvider.createUserService()
|
const userService: IUserService = serviceProvider.createUserService()
|
||||||
@@ -48,9 +48,14 @@ export const dbGetUserInfo = () => {
|
|||||||
* @param {string} callerKey
|
* @param {string} callerKey
|
||||||
*/
|
*/
|
||||||
export const dbGetUserInfoByUserId = (uid: string, callerKey: string) => {
|
export const dbGetUserInfoByUserId = (uid: string, callerKey: string) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: Function, getState: Function) => {
|
||||||
if (uid) {
|
if (uid) {
|
||||||
|
|
||||||
|
let caller = getState().global.temp.caller
|
||||||
|
if ( caller && caller.indexOf(`dbGetUserInfoByUserId-${uid}`) > -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dispatch(globalActions.temp({caller: `dbGetUserInfoByUserId-${uid}`}))
|
||||||
return userService.getUserProfile(uid).then((userProfile: Profile) => {
|
return userService.getUserProfile(uid).then((userProfile: Profile) => {
|
||||||
|
|
||||||
dispatch(addUserInfo(uid, {
|
dispatch(addUserInfo(uid, {
|
||||||
@@ -76,7 +81,6 @@ export const dbGetUserInfoByUserId = (uid: string, callerKey: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updata user information
|
* Updata user information
|
||||||
* @param {object} newInfo
|
* @param {object} newInfo
|
||||||
@@ -4,14 +4,14 @@ import moment from 'moment'
|
|||||||
import { VoteActionType } from 'constants/voteActionType'
|
import { VoteActionType } from 'constants/voteActionType'
|
||||||
|
|
||||||
// - Import domain
|
// - Import domain
|
||||||
import { Vote } from 'domain/votes'
|
import { Vote } from 'core/domain/votes'
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as globalActions from 'actions/globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
import * as notifyActions from 'actions/notifyActions'
|
import * as notifyActions from 'actions/notifyActions'
|
||||||
|
|
||||||
import { IServiceProvider, ServiceProvide } from 'factories'
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
import { IVoteService } from 'services/votes'
|
import { IVoteService } from 'core/services/votes'
|
||||||
|
|
||||||
const serviceProvider: IServiceProvider = new ServiceProvide()
|
const serviceProvider: IServiceProvider = new ServiceProvide()
|
||||||
const voteService: IVoteService = serviceProvider.createVoteService()
|
const voteService: IVoteService = serviceProvider.createVoteService()
|
||||||
47
src/api/CircleAPI.ts
Normal file
47
src/api/CircleAPI.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { Circle, UserFollower } from 'core/domain/circles'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the circles' id which the specify users is in that circle
|
||||||
|
* @param {object} circles
|
||||||
|
* @param {string} followingId
|
||||||
|
*/
|
||||||
|
export const getUserBelongCircles = (circles: {[circleId: string]: Circle},followingId: string) => {
|
||||||
|
let userBelongCircles: string[] = []
|
||||||
|
Object.keys(circles).forEach((cid) => {
|
||||||
|
if (cid.trim() !== '-Followers' && circles[cid].users) {
|
||||||
|
let isExist = Object.keys(circles[cid].users).indexOf(followingId) > -1
|
||||||
|
if (isExist) {
|
||||||
|
userBelongCircles.push(cid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return userBelongCircles
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the following users
|
||||||
|
* @param {object} circles
|
||||||
|
*/
|
||||||
|
export const getFollowingUsers = (circles: {[circleId: string]: Circle}) => {
|
||||||
|
let followingUsers: {[userId: string]: UserFollower} = {}
|
||||||
|
Object.keys(circles).forEach((cid) => {
|
||||||
|
if (cid.trim() !== '-Followers' && circles[cid].users) {
|
||||||
|
Object.keys(circles[cid].users).forEach((userId) => {
|
||||||
|
let isExist = Object.keys(followingUsers).indexOf(userId) > -1
|
||||||
|
if (!isExist) {
|
||||||
|
followingUsers[userId] = {
|
||||||
|
...circles[cid].users[userId]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return followingUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getUserBelongCircles,
|
||||||
|
getFollowingUsers
|
||||||
|
}
|
||||||
147
src/api/FileAPI.ts
Normal file
147
src/api/FileAPI.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
// - Import react component
|
||||||
|
import { storageRef } from 'data/firebaseClient'
|
||||||
|
|
||||||
|
// - Interface declaration
|
||||||
|
interface FileReaderEventTarget extends EventTarget {
|
||||||
|
result: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileReaderEvent extends Event {
|
||||||
|
target: FileReaderEventTarget
|
||||||
|
getMessage (): string
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Get file Extension
|
||||||
|
const getExtension = (fileName: string) => {
|
||||||
|
let re: RegExp = /(?:\.([^.]+))?$/
|
||||||
|
return re.exec(fileName)![1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts image to canvas returns new canvas element
|
||||||
|
const convertImageToCanvas = (image: HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | ImageBitmap) => {
|
||||||
|
let canvas = document.createElement('canvas')
|
||||||
|
canvas.width = image.width
|
||||||
|
canvas.height = image.height
|
||||||
|
canvas.getContext('2d')!.drawImage(image, 0, 0)
|
||||||
|
|
||||||
|
return canvas
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload image on the server
|
||||||
|
* @param {file} file
|
||||||
|
* @param {string} fileName
|
||||||
|
*/
|
||||||
|
const uploadImage = (file: any, fileName: string, progress: (percentage: number, status: boolean) => void) => {
|
||||||
|
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
// Create a storage refrence
|
||||||
|
let storegeFile = storageRef.child(`images/${fileName}`)
|
||||||
|
|
||||||
|
// Upload file
|
||||||
|
let task = storegeFile.put(file)
|
||||||
|
task.then((result) => {
|
||||||
|
resolve(result)
|
||||||
|
}).catch((error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Upload storage bar
|
||||||
|
task.on('state_changed', (snapshot: any) => {
|
||||||
|
let percentage: number = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
|
||||||
|
progress(percentage, true)
|
||||||
|
}, (error) => {
|
||||||
|
console.log('========== Upload Image ============')
|
||||||
|
console.log(error)
|
||||||
|
console.log('====================================')
|
||||||
|
|
||||||
|
}, () => {
|
||||||
|
progress(100, false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constraint image size
|
||||||
|
* @param {file} file
|
||||||
|
* @param {number} maxWidth
|
||||||
|
* @param {number} maxHeight
|
||||||
|
*/
|
||||||
|
const constraintImage = (file: File,fileName: string, maxWidth?: number, maxHeight?: number) => {
|
||||||
|
// Ensure it's an image
|
||||||
|
if (file.type.match(/image.*/)) {
|
||||||
|
// Load the image
|
||||||
|
let reader = new FileReader()
|
||||||
|
reader.onload = function (readerEvent: FileReaderEvent) {
|
||||||
|
let image = new Image()
|
||||||
|
image.onload = function (imageEvent: Event) {
|
||||||
|
|
||||||
|
// Resize the image
|
||||||
|
let canvas: HTMLCanvasElement = document.createElement('canvas')
|
||||||
|
let maxSize: number = 986// TODO : pull max size from a site config
|
||||||
|
let width: number = image.width
|
||||||
|
let height: number = image.height
|
||||||
|
if (width > height) {
|
||||||
|
if (width > maxSize) {
|
||||||
|
height *= maxSize / width
|
||||||
|
width = maxSize
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (height > maxSize) {
|
||||||
|
width *= maxSize / height
|
||||||
|
height = maxSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.width = width
|
||||||
|
canvas.height = height
|
||||||
|
canvas.getContext('2d')!.drawImage(image, 0, 0, width, height)
|
||||||
|
let dataUrl = canvas.toDataURL()
|
||||||
|
let resizedImage = dataURLToBlob(dataUrl)
|
||||||
|
let evt = new CustomEvent('onSendResizedImage', { detail: {resizedImage,fileName} })
|
||||||
|
window.dispatchEvent(evt)
|
||||||
|
|
||||||
|
}
|
||||||
|
image.src = readerEvent.target.result
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert data URL to blob
|
||||||
|
* @param {object} dataURL
|
||||||
|
*/
|
||||||
|
const dataURLToBlob = (dataURL: string) => {
|
||||||
|
|
||||||
|
let BASE64_MARKER = 'base64,'
|
||||||
|
if (dataURL.indexOf(BASE64_MARKER) === -1) {
|
||||||
|
let parts = dataURL.split(',')
|
||||||
|
let contentType = parts[0].split(':')[1]
|
||||||
|
let raw = parts[1]
|
||||||
|
|
||||||
|
return new Blob([raw], {type: contentType})
|
||||||
|
}
|
||||||
|
|
||||||
|
let parts = dataURL.split(BASE64_MARKER)
|
||||||
|
let contentType = parts[0].split(':')[1]
|
||||||
|
let raw = window.atob(parts[1])
|
||||||
|
let rawLength = raw.length
|
||||||
|
|
||||||
|
let uInt8Array = new Uint8Array(rawLength)
|
||||||
|
|
||||||
|
for (let i = 0 ;i < rawLength; ++i) {
|
||||||
|
uInt8Array[i] = raw.charCodeAt(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Blob([uInt8Array], {type: contentType})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
dataURLToBlob,
|
||||||
|
convertImageToCanvas,
|
||||||
|
getExtension,
|
||||||
|
constraintImage,
|
||||||
|
uploadImage
|
||||||
|
|
||||||
|
}
|
||||||
29
src/api/PostAPI.ts
Normal file
29
src/api/PostAPI.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
// Get tags from post content
|
||||||
|
export const detectTags: (content: string, character: string) => string[] = (content: string, character: string) => {
|
||||||
|
|
||||||
|
return content.split(' ').filter((word) => {
|
||||||
|
return (word.slice(0,1) === character)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
export const getContentTags = (content: string) => {
|
||||||
|
let newTags: string[] = []
|
||||||
|
let tags = detectTags(content,'#')
|
||||||
|
tags.forEach((tag) => {
|
||||||
|
newTags.push(tag.slice(1))
|
||||||
|
})
|
||||||
|
return newTags
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sortObjectsDate = (objects: any) => {
|
||||||
|
let sortedObjects = objects
|
||||||
|
|
||||||
|
// Sort posts with creation date
|
||||||
|
sortedObjects.sort((a: any, b: any) => {
|
||||||
|
return parseInt(b.creationDate,10) - parseInt(a.creationDate,10)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return sortedObjects
|
||||||
|
}
|
||||||
30
src/api/StringAPI.ts
Normal file
30
src/api/StringAPI.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// - Import react component
|
||||||
|
import { storageRef } from 'data/firebaseClient'
|
||||||
|
|
||||||
|
// - Import actions
|
||||||
|
|
||||||
|
const isValidEmail = (email: string) => {
|
||||||
|
let re = /^(([^<>()\[\]\\.,:\s@"]+(\.[^<>()\[\]\\.,:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||||
|
return re.test(email)
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryString (name: string, url: string = window.location.href) {
|
||||||
|
name = name.replace(/[[]]/g, '\\$&')
|
||||||
|
|
||||||
|
const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)', 'i')
|
||||||
|
const results = regex.exec(url)
|
||||||
|
|
||||||
|
if (!results) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (!results[2]) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeURIComponent(results[2].replace(/\+/g, ' '))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
isValidEmail,
|
||||||
|
queryString
|
||||||
|
}
|
||||||
270
src/components/circle/CircleComponent.tsx
Normal file
270
src/components/circle/CircleComponent.tsx
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
// - Import react components
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { push } from 'react-router-redux'
|
||||||
|
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors'
|
||||||
|
import { List, ListItem } from 'material-ui/List'
|
||||||
|
import SvgGroup from 'material-ui/svg-icons/action/group-work'
|
||||||
|
import IconButton from 'material-ui/IconButton'
|
||||||
|
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert'
|
||||||
|
import IconMenu from 'material-ui/IconMenu'
|
||||||
|
import TextField from 'material-ui/TextField'
|
||||||
|
import MenuItem from 'material-ui/MenuItem'
|
||||||
|
import IconButtonElement from 'layouts/IconButtonElement'
|
||||||
|
import Dialog from 'material-ui/Dialog'
|
||||||
|
import Divider from 'material-ui/Divider'
|
||||||
|
import FlatButton from 'material-ui/FlatButton'
|
||||||
|
import RaisedButton from 'material-ui/RaisedButton'
|
||||||
|
import SvgClose from 'material-ui/svg-icons/navigation/close'
|
||||||
|
import AppBar from 'material-ui/AppBar'
|
||||||
|
|
||||||
|
// - Import app components
|
||||||
|
import UserAvatar from 'components/userAvatar'
|
||||||
|
|
||||||
|
// - Import API
|
||||||
|
|
||||||
|
// - Import actions
|
||||||
|
import * as circleActions from 'actions/circleActions'
|
||||||
|
|
||||||
|
import { ICircleComponentProps } from './ICircleComponentProps'
|
||||||
|
import { ICircleComponentState } from './ICircleComponentState'
|
||||||
|
import { Circle } from 'core/domain/circles'
|
||||||
|
import { Profile } from 'core/domain/users/profile'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create component class
|
||||||
|
*/
|
||||||
|
export class CircleComponent extends Component<ICircleComponentProps, ICircleComponentState> {
|
||||||
|
|
||||||
|
styles = {
|
||||||
|
userListItem: {
|
||||||
|
backgroundColor: '#e2e2e2'
|
||||||
|
},
|
||||||
|
rightIconMenu: {
|
||||||
|
display: 'block',
|
||||||
|
position: 'absolute',
|
||||||
|
top: '0px',
|
||||||
|
right: '12px'
|
||||||
|
},
|
||||||
|
settingOverlay: {
|
||||||
|
background: 'rgba(0,0,0,0.12)'
|
||||||
|
},
|
||||||
|
settingContent: {
|
||||||
|
maxWidth: '400px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component constructor
|
||||||
|
* @param {object} props is an object properties of component
|
||||||
|
*/
|
||||||
|
constructor (props: ICircleComponentProps) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
// Defaul state
|
||||||
|
this.state = {
|
||||||
|
/**
|
||||||
|
* If is true circle is open to show users in circle list
|
||||||
|
*/
|
||||||
|
open: false,
|
||||||
|
/**
|
||||||
|
* Circle name on change
|
||||||
|
*/
|
||||||
|
circleName: this.props.circle.name,
|
||||||
|
/**
|
||||||
|
* Save operation will be disable if user doesn't meet requirement
|
||||||
|
*/
|
||||||
|
disabledSave: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binding functions to `this`
|
||||||
|
this.handleToggleCircle = this.handleToggleCircle.bind(this)
|
||||||
|
this.handleDeleteCircle = this.handleDeleteCircle.bind(this)
|
||||||
|
this.handleUpdateCircle = this.handleUpdateCircle.bind(this)
|
||||||
|
this.handleChangeCircleName = this.handleChangeCircleName.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle chage circle name
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @memberof CircleComponent
|
||||||
|
*/
|
||||||
|
handleChangeCircleName = (evt: any) => {
|
||||||
|
const { value } = evt.target
|
||||||
|
this.setState({
|
||||||
|
circleName: value,
|
||||||
|
disabledSave: (!value || value.trim() === '')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user's circle
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @memberof CircleComponent
|
||||||
|
*/
|
||||||
|
handleUpdateCircle = () => {
|
||||||
|
const { circleName } = this.state
|
||||||
|
if (circleName && circleName.trim() !== '') {
|
||||||
|
this.props.updateCircle!({ name: circleName, id: this.props.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle delete circle
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @memberof CircleComponent
|
||||||
|
*/
|
||||||
|
handleDeleteCircle = () => {
|
||||||
|
this.props.deleteCircle!(this.props.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle circle to close/open
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @memberof CircleComponent
|
||||||
|
*/
|
||||||
|
handleToggleCircle = () => {
|
||||||
|
this.setState({
|
||||||
|
open: !this.state.open
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
userList = () => {
|
||||||
|
const { users } = this.props.circle
|
||||||
|
const { userInfo } = this.props
|
||||||
|
let usersParsed: any = []
|
||||||
|
|
||||||
|
if (users) {
|
||||||
|
Object.keys(users).forEach((key, index) => {
|
||||||
|
const { fullName } = users[key]
|
||||||
|
let avatar = userInfo && userInfo[key] ? userInfo[key].avatar || '' : ''
|
||||||
|
usersParsed.push(<ListItem
|
||||||
|
key={`${this.props.id}.${key}`}
|
||||||
|
style={this.styles.userListItem as any}
|
||||||
|
value={2}
|
||||||
|
primaryText={fullName}
|
||||||
|
leftAvatar={<UserAvatar fullName={fullName} fileName={avatar as any} />}
|
||||||
|
onClick={() => this.props.goTo!(`/${key}`)}
|
||||||
|
/>)
|
||||||
|
|
||||||
|
})
|
||||||
|
return usersParsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right icon menue of circle
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @memberof CircleComponent
|
||||||
|
*/
|
||||||
|
// tslint:disable-next-line:member-ordering
|
||||||
|
rightIconMenu: any = (
|
||||||
|
<IconMenu iconButtonElement={IconButtonElement} style={this.styles.rightIconMenu as any}>
|
||||||
|
<MenuItem primaryText='Delete circle' onClick={this.handleDeleteCircle} />
|
||||||
|
<MenuItem primaryText='Circle settings' onClick={this.props.openCircleSettings} />
|
||||||
|
</IconMenu>
|
||||||
|
)
|
||||||
|
/**
|
||||||
|
* Reneder component DOM
|
||||||
|
* @return {react element} return the DOM which rendered by component
|
||||||
|
*/
|
||||||
|
render () {
|
||||||
|
|
||||||
|
const circleTitle = (
|
||||||
|
<div>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||||
|
<div style={{ paddingRight: '10px' }}>
|
||||||
|
<SvgClose onClick={this.props.closeCircleSettings} hoverColor={grey400} style={{ cursor: 'pointer' }} />
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
color: 'rgba(0,0,0,0.87)',
|
||||||
|
flex: '1 1',
|
||||||
|
font: '500 20px Roboto,RobotoDraft,Helvetica,Arial,sans-serif'
|
||||||
|
}}>
|
||||||
|
Circle settings
|
||||||
|
</div>
|
||||||
|
<div style={{ marginTop: '-9px' }}>
|
||||||
|
<FlatButton label='SAVE' primary={true} disabled={this.state.disabledSave} onClick={this.handleUpdateCircle} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ListItem
|
||||||
|
key={this.props.id}
|
||||||
|
style={{ backgroundColor: '#fff', borderBottom: '1px solid rgba(0,0,0,0.12)', height: '72px', padding: '12px 0' }}
|
||||||
|
primaryText={<span style={{ color: 'rgba(0,0,0,0.87)', fontSize: '16px', marginRight: '8px', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }}>{this.props.circle.name}</span>}
|
||||||
|
leftIcon={<SvgGroup style={{ width: '40px', height: '40px', transform: 'translate(0px, -9px)', fill: '#bdbdbd' }} />}
|
||||||
|
rightIconButton={this.rightIconMenu}
|
||||||
|
initiallyOpen={false}
|
||||||
|
onClick={this.handleToggleCircle}
|
||||||
|
open={this.state.open}
|
||||||
|
nestedItems={this.userList()}
|
||||||
|
>
|
||||||
|
<Dialog
|
||||||
|
key={this.props.id}
|
||||||
|
title={circleTitle}
|
||||||
|
modal={false}
|
||||||
|
open={this.props.openSetting!}
|
||||||
|
onRequestClose={this.props.closeCircleSettings}
|
||||||
|
overlayStyle={this.styles.settingOverlay as any}
|
||||||
|
contentStyle={this.styles.settingContent as any}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
hintText='Circle name'
|
||||||
|
floatingLabelText='Circle name'
|
||||||
|
onChange={this.handleChangeCircleName}
|
||||||
|
value={this.state.circleName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</ListItem>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map dispatch to props
|
||||||
|
* @param {func} dispatch is the function to dispatch action to reducers
|
||||||
|
* @param {object} ownProps is the props belong to component
|
||||||
|
* @return {object} props of component
|
||||||
|
*/
|
||||||
|
const mapDispatchToProps = (dispatch: any, ownProps: ICircleComponentProps) => {
|
||||||
|
let { uid } = ownProps
|
||||||
|
return {
|
||||||
|
deleteCircle: (id: string) => dispatch(circleActions.dbDeleteCircle(id)),
|
||||||
|
updateCircle: (circle: Circle) => dispatch(circleActions.dbUpdateCircle(circle)),
|
||||||
|
closeCircleSettings: () => dispatch(circleActions.closeCircleSettings(uid, ownProps.id)),
|
||||||
|
openCircleSettings: () => dispatch(circleActions.openCircleSettings(uid, ownProps.id)),
|
||||||
|
goTo: (url: string) => dispatch(push(url))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map state to props
|
||||||
|
* @param {object} state is the obeject from redux store
|
||||||
|
* @param {object} ownProps is the props belong to component
|
||||||
|
* @return {object} props of component
|
||||||
|
*/
|
||||||
|
const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => {
|
||||||
|
let { uid } = state.authorize
|
||||||
|
return {
|
||||||
|
openSetting: state.circle ? (state.circle.userCircles[uid] ? (state.circle.userCircles[uid][ownProps.id].openCircleSettings || false) : false) : false,
|
||||||
|
userInfo: state.user.info
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Connect component to redux store
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(CircleComponent as any)
|
||||||
84
src/components/circle/ICircleComponentProps.ts
Normal file
84
src/components/circle/ICircleComponentProps.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { Comment } from 'core/domain/comments'
|
||||||
|
import { Profile } from 'core/domain/users'
|
||||||
|
import { Circle } from 'core/domain/circles'
|
||||||
|
|
||||||
|
export interface ICircleComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @type {Circle}
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
circle: Circle
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Circle identifier
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
id: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User identifier
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
uid: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
updateCircle?: Function
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
deleteCircle?: Function
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Users profile
|
||||||
|
*
|
||||||
|
* @type {{[userId: string]: Profile}}
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
userInfo?: {[userId: string]: Profile}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close setting box of circle
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
closeCircleSettings?: any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Circle setting dialog is open {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
openSetting?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change route location
|
||||||
|
*
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
goTo?: (url: string) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open setting box for a circle
|
||||||
|
*
|
||||||
|
* @memberof ICircleComponentProps
|
||||||
|
*/
|
||||||
|
openCircleSettings?: () => any
|
||||||
|
}
|
||||||
27
src/components/circle/ICircleComponentState.ts
Normal file
27
src/components/circle/ICircleComponentState.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
export interface ICircleComponentState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Circle name
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICircleComponentState
|
||||||
|
*/
|
||||||
|
circleName: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If circle user list is open {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICircleComponentState
|
||||||
|
*/
|
||||||
|
open: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save button is disabled {true} or not false
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICircleComponentState
|
||||||
|
*/
|
||||||
|
disabledSave: boolean
|
||||||
|
}
|
||||||
2
src/components/circle/index.ts
Normal file
2
src/components/circle/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import CircleComponent from './CircleComponent'
|
||||||
|
export default CircleComponent
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
// - Import react components
|
// - Import react components
|
||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { NavLink} from 'react-router-dom'
|
import { NavLink } from 'react-router-dom'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { createAction as action } from 'redux-actions'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import Linkify from 'react-linkify'
|
import Linkify from 'react-linkify'
|
||||||
|
|
||||||
|
|
||||||
// - Import material UI libraries
|
// - Import material UI libraries
|
||||||
import { List, ListItem } from 'material-ui/List'
|
import { List, ListItem } from 'material-ui/List'
|
||||||
import Divider from 'material-ui/Divider'
|
import Divider from 'material-ui/Divider'
|
||||||
@@ -21,27 +19,24 @@ import MenuItem from 'material-ui/MenuItem'
|
|||||||
import TextField from 'material-ui/TextField'
|
import TextField from 'material-ui/TextField'
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import UserAvatar from 'UserAvatar'
|
import UserAvatarComponent from 'components/userAvatar'
|
||||||
|
|
||||||
|
|
||||||
// - Import API
|
// - Import API
|
||||||
|
|
||||||
// - Import action types
|
// - Import action types
|
||||||
import * as types from 'actionTypes'
|
import * as types from 'constants/actionTypes'
|
||||||
|
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as commentActions from 'commentActions'
|
import * as commentActions from 'actions/commentActions'
|
||||||
import * as userActions from 'userActions'
|
import * as userActions from 'actions/userActions'
|
||||||
|
|
||||||
|
|
||||||
|
import { ICommentComponentProps } from './ICommentComponentProps'
|
||||||
|
import { ICommentComponentState } from './ICommentComponentState'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create component class
|
* Create component class
|
||||||
*/
|
*/
|
||||||
export class Comment extends Component {
|
export class CommentComponent extends Component<ICommentComponentProps,ICommentComponentState> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
/**
|
/**
|
||||||
@@ -55,20 +50,84 @@ export class Comment extends Component {
|
|||||||
/**
|
/**
|
||||||
* If it's true the comment is disable to write
|
* If it's true the comment is disable to write
|
||||||
*/
|
*/
|
||||||
disableComments: PropTypes.bool,
|
disableComments: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOM styles
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @memberof CommentComponent
|
||||||
|
*/
|
||||||
|
styles = {
|
||||||
|
comment: {
|
||||||
|
marginBottom: '12px'
|
||||||
|
},
|
||||||
|
iconButton: {
|
||||||
|
width: 16,
|
||||||
|
height: 16
|
||||||
|
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
fontSize: '13px',
|
||||||
|
paddingRight: '10px',
|
||||||
|
fontWeight: 400,
|
||||||
|
color: 'rgba(0,0,0,0.87)',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
overflow: 'hidden'
|
||||||
|
|
||||||
|
},
|
||||||
|
commentBody: {
|
||||||
|
fontSize: '13px',
|
||||||
|
lineHeight: '20px',
|
||||||
|
color: 'rgba(0,0,0,0.87)',
|
||||||
|
fontWeight: 300,
|
||||||
|
height: '',
|
||||||
|
display: 'block'
|
||||||
|
|
||||||
|
},
|
||||||
|
rightIconMenuItem: {
|
||||||
|
fontSize: '14px'
|
||||||
|
},
|
||||||
|
textarea: {
|
||||||
|
fontWeight: 100,
|
||||||
|
fontSize: '14px',
|
||||||
|
border: 'none',
|
||||||
|
width: '100%',
|
||||||
|
outline: 'none',
|
||||||
|
resize: 'none'
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
float: 'right',
|
||||||
|
clear: 'both',
|
||||||
|
zIndex: 5,
|
||||||
|
margin: '0px 5px 5px 0px',
|
||||||
|
fontWeight: 400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fields
|
||||||
|
*
|
||||||
|
* @type {*}
|
||||||
|
* @memberof CommentComponent
|
||||||
|
*/
|
||||||
|
textareaRef: any
|
||||||
|
divCommentRef: any
|
||||||
|
inputText: any
|
||||||
|
divComment: any
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor (props: ICommentComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.textareaRef = i => { this.inputText = i }
|
this.textareaRef = (i: any) => { this.inputText = i }
|
||||||
this.divCommentRef = i => { this.divComment = i }
|
this.divCommentRef = (i: any) => { this.divComment = i }
|
||||||
|
|
||||||
//Defaul state
|
// Defaul state
|
||||||
this.state = {
|
this.state = {
|
||||||
/**
|
/**
|
||||||
* Comment text
|
* Comment text
|
||||||
@@ -85,7 +144,7 @@ export class Comment extends Component {
|
|||||||
/**
|
/**
|
||||||
* If it's true the post owner is the logged in user which this post be long to the comment
|
* If it's true the post owner is the logged in user which this post be long to the comment
|
||||||
*/
|
*/
|
||||||
isPostOwner: PropTypes.bool
|
isPostOwner: false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,22 +157,20 @@ export class Comment extends Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle show edit comment
|
* Handle show edit comment
|
||||||
* @param {event} evt is an event passed by clicking on edit button
|
* @param {event} evt is an event passed by clicking on edit button
|
||||||
*/
|
*/
|
||||||
handleEditComment = (evt) => {
|
handleEditComment = (evt: any) => {
|
||||||
this.inputText.style.height = this.divComment.clientHeight + 'px'
|
this.inputText.style.height = this.divComment.clientHeight + 'px'
|
||||||
this.props.openEditor()
|
this.props.openEditor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle cancel edit
|
* Handle cancel edit
|
||||||
* @param {event} evt is an event passed by clicking on cancel button
|
* @param {event} evt is an event passed by clicking on cancel button
|
||||||
*/
|
*/
|
||||||
handleCancelEdit = (evt) => {
|
handleCancelEdit = (evt: any) => {
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
text: this.state.initialText
|
text: this.state.initialText
|
||||||
@@ -125,7 +182,7 @@ export class Comment extends Component {
|
|||||||
* Handle edit comment
|
* Handle edit comment
|
||||||
* @param {event} evt is an event passed by clicking on post button
|
* @param {event} evt is an event passed by clicking on post button
|
||||||
*/
|
*/
|
||||||
handleUpdateComment = (evt) => {
|
handleUpdateComment = (evt: any) => {
|
||||||
|
|
||||||
this.props.update(this.props.comment.id, this.props.comment.postId, this.state.text)
|
this.props.update(this.props.comment.id, this.props.comment.postId, this.state.text)
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -134,14 +191,12 @@ export class Comment extends Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When comment text changed
|
* When comment text changed
|
||||||
* @param {event} evt is an event passed by change comment text callback funciton
|
* @param {event} evt is an event passed by change comment text callback funciton
|
||||||
* @param {string} data is the comment text which user writes
|
* @param {string} data is the comment text which user writes
|
||||||
*/
|
*/
|
||||||
handleOnChange = (evt) => {
|
handleOnChange = (evt: any) => {
|
||||||
const data = evt.target.value
|
const data = evt.target.value
|
||||||
this.inputText.style.height = evt.target.scrollHeight + 'px'
|
this.inputText.style.height = evt.target.scrollHeight + 'px'
|
||||||
if (data.length === 0 || data.trim() === '' || data.trim() === this.state.initialText) {
|
if (data.length === 0 || data.trim() === '' || data.trim() === this.state.initialText) {
|
||||||
@@ -149,8 +204,7 @@ export class Comment extends Component {
|
|||||||
text: data,
|
text: data,
|
||||||
editDisabled: true
|
editDisabled: true
|
||||||
})
|
})
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
text: data,
|
text: data,
|
||||||
editDisabled: false
|
editDisabled: false
|
||||||
@@ -159,119 +213,81 @@ export class Comment extends Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a comment
|
* Delete a comment
|
||||||
* @param {event} evt an event passed by click on delete comment
|
* @param {event} evt an event passed by click on delete comment
|
||||||
* @param {string} id comment identifire
|
* @param {string} id comment identifire
|
||||||
* @param {string} postId post identifier which comment belong to
|
* @param {string} postId post identifier which comment belong to
|
||||||
*/
|
*/
|
||||||
handleDelete = (evt, id, postId) => {
|
handleDelete = (evt: any, id?: string| null, postId?: string) => {
|
||||||
this.props.delete(id, postId)
|
this.props.delete(id, postId)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount () {
|
||||||
const {userId} = this.props.comment
|
const {userId} = this.props.comment
|
||||||
if (!this.props.isCommentOwner && !this.props.info[userId]) {
|
if (!this.props.isCommentOwner && !this.props.info[userId!]) {
|
||||||
this.props.getUserInfo()
|
this.props.getUserInfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
/**
|
|
||||||
* DOM styles
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @memberof Comment
|
|
||||||
*/
|
|
||||||
const styles = {
|
|
||||||
comment: {
|
|
||||||
marginBottom: '12px'
|
|
||||||
},
|
|
||||||
iconButton: {
|
|
||||||
width: 16,
|
|
||||||
height: 16
|
|
||||||
|
|
||||||
},
|
|
||||||
author:{
|
|
||||||
fontSize: "13px",
|
|
||||||
paddingRight: "10px",
|
|
||||||
fontWeight: 400,
|
|
||||||
color: "rgba(0,0,0,0.87)",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
overflow: "hidden"
|
|
||||||
|
|
||||||
},
|
|
||||||
commentBody:{
|
|
||||||
fontSize: "13px",
|
|
||||||
lineHeight: "20px",
|
|
||||||
color: "rgba(0,0,0,0.87)",
|
|
||||||
fontWeight: 300,
|
|
||||||
height: "",
|
|
||||||
display: "block"
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comment object from props
|
* Comment object from props
|
||||||
*/
|
*/
|
||||||
const {comment} = this.props
|
const {comment} = this.props
|
||||||
|
|
||||||
const iconButtonElement = (
|
const iconButtonElement = (
|
||||||
<IconButton style={styles.iconButton} iconStyle={styles.iconButton}
|
<IconButton style={this.styles.iconButton} iconStyle={this.styles.iconButton}
|
||||||
touch={true}
|
touch={true}
|
||||||
>
|
>
|
||||||
<MoreVertIcon color={grey400} viewBox='9 0 24 24' />
|
<MoreVertIcon color={grey400} viewBox='9 0 24 24' />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
const RightIconMenu = () => (
|
const RightIconMenu = () => (
|
||||||
<IconMenu iconButtonElement={iconButtonElement} style={{ display: "block", position: "absolute", top: "0px", right: "4px" }}>
|
<IconMenu iconButtonElement={iconButtonElement} style={{ display: 'block', position: 'absolute', top: '0px', right: '4px' }}>
|
||||||
<MenuItem style={{ fontSize: "14px" }}>Reply</MenuItem>
|
<MenuItem style={{ fontSize: '14px' }}>Reply</MenuItem>
|
||||||
{this.props.isCommentOwner ? (<MenuItem style={{ fontSize: "14px" }} onClick={this.handleEditComment}>Edit</MenuItem>): ''}
|
{this.props.isCommentOwner ? (<MenuItem style={this.styles.rightIconMenuItem} onClick={this.handleEditComment}>Edit</MenuItem>) : ''}
|
||||||
{(this.props.isCommentOwner || this.props.isPostOwner) ? ( <MenuItem style={{ fontSize: "14px" }} onClick={(evt) => this.handleDelete(evt, comment.id, comment.postId)}>Delete</MenuItem>): ''}
|
{(this.props.isCommentOwner || this.props.isPostOwner) ? ( <MenuItem style={{ fontSize: '14px' }} onClick={(evt) => this.handleDelete(evt, comment.id, comment.postId)}>Delete</MenuItem>) : ''}
|
||||||
</IconMenu>
|
</IconMenu>
|
||||||
)
|
)
|
||||||
|
|
||||||
const Author = () => (
|
const Author = () => (
|
||||||
<div style={{ marginTop: "-11px" }}>
|
<div style={{ marginTop: '-11px' }}>
|
||||||
<span style={styles.author}>{comment.userDisplayName}</span><span style={{
|
<span style={this.styles.author as any}>{comment.userDisplayName}</span><span style={{
|
||||||
fontWeight: 100,
|
fontWeight: 100,
|
||||||
fontSize: "10px"
|
fontSize: '10px'
|
||||||
}}>{moment.unix(comment.creationDate).fromNow()}</span>
|
}}>{moment.unix(comment.creationDate!).fromNow()}</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
const commentBody = (
|
const commentBody = (
|
||||||
<p style={styles.commentBody}>{comment.text}</p>
|
<p style={this.styles.commentBody as any}>{comment.text}</p>
|
||||||
)
|
)
|
||||||
|
|
||||||
const {userId} = comment
|
const {userId} = comment
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="animate-top" style={styles.comment} key={comment.id}>
|
<div className='animate-top' style={this.styles.comment} key={comment.id!}>
|
||||||
<Paper zDepth={0} className="animate2-top10" style={{ position: "relative", padding: "", display: (!this.state.display ? "block" : "none") }}>
|
<Paper zDepth={0} className='animate2-top10' style={{ position: 'relative', padding: '', display: (!this.state.display ? 'block' : 'none') }}>
|
||||||
<div style={{ marginLeft: "0px", padding: "16px 56px 0px 72px", position: "relative" }}>
|
<div style={{ marginLeft: '0px', padding: '16px 56px 0px 72px', position: 'relative' }}>
|
||||||
<NavLink to={`/${userId}`}><UserAvatar fullName={this.props.fullName} fileName={this.props.avatar} style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", position: "absolute", top: "8px", left: "16px" }} size={36} /></NavLink>
|
<NavLink to={`/${userId}`}><UserAvatarComponent fullName={this.props.fullName} fileName={this.props.avatar} style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', position: 'absolute', top: '8px', left: '16px' }} size={36} /></NavLink>
|
||||||
<NavLink to={`/${userId}`}> <Author /></NavLink>
|
<NavLink to={`/${userId}`}> <Author /></NavLink>
|
||||||
{(!this.props.isCommentOwner && !this.props.isPostOwner && this.props.disableComments )? '' : (<RightIconMenu />)}
|
{(!this.props.isCommentOwner && !this.props.isPostOwner && this.props.disableComments ) ? '' : (<RightIconMenu />)}
|
||||||
<div style={{ outline: "none", marginLeft: "16px", flex: "auto", flexGrow: 1 }}>
|
<div style={{ outline: 'none', marginLeft: '16px', flex: 'auto', flexGrow: 1 }}>
|
||||||
<textarea ref={this.textareaRef} className="animate2-top10" style={{ fontWeight: 100, fontSize: "14px", border: "none", width: "100%", outline: "none", resize: "none", display: (this.props.comment.editorStatus ? 'block' : 'none') }} onChange={this.handleOnChange} value={this.state.text}></textarea>
|
<textarea ref={this.textareaRef} className='animate2-top10' style={ Object.assign({}, this.styles.textarea, { display: (this.props.comment.editorStatus ? 'block' : 'none') })} onChange={this.handleOnChange} value={this.state.text!}></textarea>
|
||||||
<Linkify properties={{target: '_blank', style: {color: 'blue'}}}>
|
<Linkify properties={{target: '_blank', style: {color: 'blue'}}}>
|
||||||
<div ref={this.divCommentRef} className="animate2-top10" style={{ fontWeight: 100, fontSize: "14px", height: "100%", border: "none", width: "100%", outline: "none", resize: "none", display: (!this.props.comment.editorStatus ? 'block' : 'none') }}>{this.state.text}</div>
|
<div ref={this.divCommentRef} className='animate2-top10' style={{ fontWeight: 100, fontSize: '14px', height: '100%', border: 'none', width: '100%', outline: 'none', resize: 'none', display: (!this.props.comment.editorStatus ? 'block' : 'none') }}>{this.state.text}</div>
|
||||||
</Linkify>
|
</Linkify>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: (this.props.comment.editorStatus ? "flex" : "none"), flexDirection: "row-reverse" }}>
|
<div style={{ display: (this.props.comment.editorStatus ? 'flex' : 'none'), flexDirection: 'row-reverse' }}>
|
||||||
<FlatButton primary={true} disabled={this.state.editDisabled} label="Update" style={{ float: "right", clear: "both", zIndex: 5, margin: "0px 5px 5px 0px", fontWeight: 400 }} onClick={this.handleUpdateComment} />
|
<FlatButton primary={true} disabled={this.state.editDisabled} label='Update' style={{ float: 'right', clear: 'both', zIndex: 5, margin: '0px 5px 5px 0px', fontWeight: 400 }} onClick={this.handleUpdateComment} />
|
||||||
<FlatButton primary={true} label="Cancel" style={{ float: "right", clear: "both", zIndex: 5, margin: "0px 5px 5px 0px", fontWeight: 400 }} onClick={this.handleCancelEdit} />
|
<FlatButton primary={true} label='Cancel' style={this.styles.cancel as any} onClick={this.handleCancelEdit} />
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
@@ -280,20 +296,19 @@ export class Comment extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map dispatch to props
|
* Map dispatch to props
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
* @param {func} dispatch is the function to dispatch action to reducers
|
||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: any) => {
|
||||||
return {
|
return {
|
||||||
delete: (id, postId) => dispatch(commentActions.dbDeleteComment(id, postId)),
|
delete: (id: string| null, postId: string) => dispatch(commentActions.dbDeleteComment(id, postId)),
|
||||||
update: (id, postId, comment) => dispatch(commentActions.dbUpdateComment(id, postId, comment)),
|
update: (id: string, postId: string, comment: string) => dispatch(commentActions.dbUpdateComment(id, postId, comment)),
|
||||||
openEditor: () => dispatch(commentActions.openCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })),
|
openEditor: () => dispatch(commentActions.openCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })),
|
||||||
closeEditor: () => dispatch(commentActions.closeCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })),
|
closeEditor: () => dispatch(commentActions.closeCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })),
|
||||||
getUserInfo: () => dispatch(userActions.dbGetUserInfoByUserId(ownProps.comment.userId))
|
getUserInfo: () => dispatch(userActions.dbGetUserInfoByUserId(ownProps.comment.userId,''))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +318,7 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state: any, ownProps: any) => {
|
||||||
const {uid} = state.authorize
|
const {uid} = state.authorize
|
||||||
const avatar = state.user.info && state.user.info[ownProps.comment.userId] ? state.user.info[ownProps.comment.userId].avatar || '' : ''
|
const avatar = state.user.info && state.user.info[ownProps.comment.userId] ? state.user.info[ownProps.comment.userId].avatar || '' : ''
|
||||||
const fullName = state.user.info && state.user.info[ownProps.comment.userId] ? state.user.info[ownProps.comment.userId].fullName || '' : ''
|
const fullName = state.user.info && state.user.info[ownProps.comment.userId] ? state.user.info[ownProps.comment.userId].fullName || '' : ''
|
||||||
@@ -313,10 +328,8 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
info: state.user.info,
|
info: state.user.info,
|
||||||
avatar,
|
avatar,
|
||||||
fullName
|
fullName
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Comment)
|
export default connect(mapStateToProps, mapDispatchToProps)(CommentComponent as any)
|
||||||
98
src/components/comment/ICommentComponentProps.ts
Normal file
98
src/components/comment/ICommentComponentProps.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { Comment } from 'core/domain/comments'
|
||||||
|
import { Profile } from 'core/domain/users'
|
||||||
|
export interface ICommentComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment
|
||||||
|
*
|
||||||
|
* @type {Comment}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
comment: Comment
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open profile editor
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
openEditor: Function
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close comment editor
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
closeEditor: () => any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current user is comment owner {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
isCommentOwner: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current user is post owner {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
isPostOwner: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update comment
|
||||||
|
*
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
update: (id?: string | null, postId?: string, comment?: string | null) => any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete comment
|
||||||
|
*
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
delete: (id?: string| null, postId?: string) => any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user profile
|
||||||
|
*
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
getUserInfo: () => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User profile
|
||||||
|
*
|
||||||
|
* @type {{[userId: string]: Profile}}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
info: {[userId: string]: Profile}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User full name
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
fullName: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User avatar address
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Comment
|
||||||
|
*/
|
||||||
|
avatar: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writing comment on the post is disabled {true} or not false
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
disableComments: boolean
|
||||||
|
|
||||||
|
}
|
||||||
43
src/components/comment/ICommentComponentState.ts
Normal file
43
src/components/comment/ICommentComponentState.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
export interface ICommentComponentState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialt text comment
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
initialText?: string | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialt text comment
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICommentComponentProps
|
||||||
|
*/
|
||||||
|
text?: string | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment is in edit state {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentComponentState
|
||||||
|
*/
|
||||||
|
editDisabled: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current user is the post owner {true} or not falses
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentComponentState
|
||||||
|
*/
|
||||||
|
isPostOwner: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display comment {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentComponentState
|
||||||
|
*/
|
||||||
|
display?: boolean
|
||||||
|
}
|
||||||
2
src/components/comment/index.ts
Normal file
2
src/components/comment/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import CommentComponent from './CommentComponent'
|
||||||
|
export default CommentComponent
|
||||||
@@ -10,60 +10,76 @@ import { ListItem } from 'material-ui/List'
|
|||||||
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors'
|
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors'
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as commentActions from 'commentActions'
|
import * as commentActions from 'actions/commentActions'
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import CommentList from 'CommentList'
|
import CommentListComponent from 'components/CommentList'
|
||||||
import CommentWrite from 'CommentWrite'
|
import UserAvatarComponent from 'components/userAvatar'
|
||||||
import UserAvatar from 'UserAvatar'
|
|
||||||
|
|
||||||
|
import { ICommentGroupComponentProps } from './ICommentGroupComponentProps'
|
||||||
|
import { ICommentGroupComponentState } from './ICommentGroupComponentState'
|
||||||
|
import { Comment } from 'core/domain/comments/comment'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create component class
|
* Create component class
|
||||||
*/
|
*/
|
||||||
export class CommentGroup extends Component {
|
export class CommentGroupComponent extends Component<ICommentGroupComponentProps, ICommentGroupComponentState> {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
static propTypes = {
|
|
||||||
/**
|
/**
|
||||||
* If it's true comment box will be open
|
* If it's true comment box will be open
|
||||||
*/
|
*/
|
||||||
open: PropTypes.bool,
|
open: PropTypes.bool,
|
||||||
/**
|
/**
|
||||||
* If it's true the comment is disable to write
|
* If it's true the comment is disable to write
|
||||||
*/
|
*/
|
||||||
disableComments: PropTypes.bool,
|
disableComments: PropTypes.bool,
|
||||||
/**
|
/**
|
||||||
* The post identifier which comment belong to
|
* The post identifier which comment belong to
|
||||||
*/
|
*/
|
||||||
postId: PropTypes.string,
|
postId: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* If it's true the post owner is the logged in user which this post be long to the comment
|
* If it's true the post owner is the logged in user which this post be long to the comment
|
||||||
*/
|
*/
|
||||||
isPostOwner: PropTypes.bool,
|
isPostOwner: PropTypes.bool,
|
||||||
/**
|
/**
|
||||||
* Toggle on show/hide comment by passing callback from parent component
|
* Toggle on show/hide comment by passing callback from parent component
|
||||||
*/
|
*/
|
||||||
onToggleRequest: PropTypes.func,
|
onToggleRequest: PropTypes.func,
|
||||||
/**
|
/**
|
||||||
* The user identifier of the post owner which comment belong to
|
* The user identifier of the post owner which comment belong to
|
||||||
*/
|
*/
|
||||||
ownerPostUserId:PropTypes.string
|
ownerPostUserId: PropTypes.string
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
styles = {
|
||||||
|
commentItem: {
|
||||||
|
height: '60px',
|
||||||
|
position: '',
|
||||||
|
zIndex: ''
|
||||||
|
},
|
||||||
|
toggleShowList: {
|
||||||
|
height: '60px',
|
||||||
|
zIndex: 5
|
||||||
|
},
|
||||||
|
writeCommentTextField: {
|
||||||
|
width: '100%'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor (props: ICommentGroupComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaul state
|
* Defaul state
|
||||||
*/
|
*/
|
||||||
this.state = {
|
this.state = {
|
||||||
commentText: "",
|
commentText: '',
|
||||||
postDisable: true
|
postDisable: true
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -72,8 +88,6 @@ static propTypes = {
|
|||||||
this.commentList = this.commentList.bind(this)
|
this.commentList = this.commentList.bind(this)
|
||||||
this.handlePostComment = this.handlePostComment.bind(this)
|
this.handlePostComment = this.handlePostComment.bind(this)
|
||||||
this.clearCommentWrite = this.clearCommentWrite.bind(this)
|
this.clearCommentWrite = this.clearCommentWrite.bind(this)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,7 +104,7 @@ static propTypes = {
|
|||||||
* Post comment
|
* Post comment
|
||||||
*/
|
*/
|
||||||
handlePostComment = () => {
|
handlePostComment = () => {
|
||||||
this.props.send(this.state.commentText, this.props.postId, this.clearCommentWrite)
|
this.props.send!(this.state.commentText, this.props.postId, this.clearCommentWrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,15 +112,14 @@ static propTypes = {
|
|||||||
* @param {event} evt is an event passed by change comment text callback funciton
|
* @param {event} evt is an event passed by change comment text callback funciton
|
||||||
* @param {string} data is the comment text which user writes
|
* @param {string} data is the comment text which user writes
|
||||||
*/
|
*/
|
||||||
handleOnChange = (evt, data) => {
|
handleOnChange = (evt: any, data: any) => {
|
||||||
this.setState({ commentText: data })
|
this.setState({ commentText: data })
|
||||||
if (data.length === 0 || data.trim() === '') {
|
if (data.length === 0 || data.trim() === '') {
|
||||||
this.setState({
|
this.setState({
|
||||||
commentText: '',
|
commentText: '',
|
||||||
postDisable: true
|
postDisable: true
|
||||||
})
|
})
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
commentText: data,
|
commentText: data,
|
||||||
postDisable: false
|
postDisable: false
|
||||||
@@ -123,48 +136,45 @@ static propTypes = {
|
|||||||
let comments = this.props.comments
|
let comments = this.props.comments
|
||||||
if (comments) {
|
if (comments) {
|
||||||
|
|
||||||
|
let parsedComments: Comment[] = []
|
||||||
let parsedComments = []
|
|
||||||
Object.keys(comments).slice(0, 3).forEach((commentId) => {
|
Object.keys(comments).slice(0, 3).forEach((commentId) => {
|
||||||
parsedComments.push({
|
parsedComments.push({
|
||||||
id: commentId,
|
id: commentId,
|
||||||
...comments[commentId]
|
...comments![commentId]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if (parsedComments.length === 2) {
|
if (parsedComments.length === 2) {
|
||||||
parsedComments.push(parsedComments[0])
|
parsedComments.push(parsedComments[0])
|
||||||
}
|
} else if (parsedComments.length === 1) {
|
||||||
else if (parsedComments.length === 1) {
|
|
||||||
parsedComments.push(parsedComments[0])
|
parsedComments.push(parsedComments[0])
|
||||||
parsedComments.push(parsedComments[0])
|
parsedComments.push(parsedComments[0])
|
||||||
}
|
}
|
||||||
return parsedComments.map((comment, index) => {
|
return parsedComments.map((comment, index) => {
|
||||||
const {userInfo} = this.props
|
const {userInfo} = this.props
|
||||||
|
|
||||||
const commentAvatar = userInfo && userInfo[comment.userId] ? userInfo[comment.userId].avatar || '' : ''
|
const commentAvatar = userInfo && userInfo[comment.userId!] ? userInfo[comment.userId!].avatar || '' : ''
|
||||||
const commentFullName = userInfo && userInfo[comment.userId] ? userInfo[comment.userId].fullName || '' : ''
|
const commentFullName = userInfo && userInfo[comment.userId!] ? userInfo[comment.userId!].fullName || '' : ''
|
||||||
|
|
||||||
return (<ListItem key={index} style={{ height: "60px", position: "", zIndex: "" }} innerDivStyle={{ padding: "6px 16px 16px 72px" }}
|
return (<ListItem key={index} style={this.styles.commentItem as any} innerDivStyle={{ padding: '6px 16px 16px 72px' }}
|
||||||
leftAvatar={<UserAvatar fullName={commentFullName} fileName={commentAvatar} style={{ top: "8px" }} size={36} />}
|
leftAvatar={<UserAvatarComponent fullName={commentFullName} fileName={commentAvatar} style={{ top: '8px' }} size={36} />}
|
||||||
secondaryText={<div style={{ height: "" }}>
|
secondaryText={<div style={{ height: '' }}>
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: "13px",
|
fontSize: '13px',
|
||||||
paddingRight: "10px",
|
paddingRight: '10px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
color: "rgba(0,0,0,0.87)",
|
color: 'rgba(0,0,0,0.87)',
|
||||||
textOverflow: "ellipsis",
|
textOverflow: 'ellipsis',
|
||||||
overflow: "hidden"
|
overflow: 'hidden'
|
||||||
}}>
|
}}>
|
||||||
{comment.userDisplayName}:
|
{comment.userDisplayName}:
|
||||||
</span>
|
</span>
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: "13px",
|
fontSize: '13px',
|
||||||
lineHeight: "20px",
|
lineHeight: '20px',
|
||||||
color: "rgba(0,0,0,0.87)",
|
color: 'rgba(0,0,0,0.87)',
|
||||||
fontWeight: 300,
|
fontWeight: 300,
|
||||||
whiteSpace: "pre-wrap"
|
whiteSpace: 'pre-wrap'
|
||||||
}}>{comment.text}</span>
|
}}>{comment.text}</span>
|
||||||
|
|
||||||
</div>}
|
</div>}
|
||||||
secondaryTextLines={2}
|
secondaryTextLines={2}
|
||||||
/>
|
/>
|
||||||
@@ -180,55 +190,53 @@ static propTypes = {
|
|||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div style={this.props.comments && Object.keys(this.props.comments).length > 0 ? { display: "block" } : { display: "none" }}>
|
<div style={this.props.comments && Object.keys(this.props.comments).length > 0 ? { display: 'block' } : { display: 'none' }}>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Paper zDepth={0} className="animate-top" style={!this.props.open ? { display: "block" } : { display: "none" }}>
|
<Paper zDepth={0} className='animate-top' style={!this.props.open ? { display: 'block' } : { display: 'none' }}>
|
||||||
|
|
||||||
<div style={{ position: "relative", height: "60px" }} >
|
<div style={{ position: 'relative', height: '60px' }} >
|
||||||
<FlatButton label=" " style={{ height: "60px", zIndex: 5 }} fullWidth={true} onClick={this.props.onToggleRequest} />
|
<FlatButton label=' ' style={this.styles.toggleShowList} fullWidth={true} onClick={this.props.onToggleRequest} />
|
||||||
|
|
||||||
<div className="comment__list-show">
|
<div className='comment__list-show'>
|
||||||
{this.commentList()}
|
{this.commentList()}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
{(this.props.comments && Object.keys(this.props.comments).length > 0)
|
{(this.props.comments && Object.keys(this.props.comments).length > 0)
|
||||||
? (<Paper zDepth={0} style={this.props.open ? { display: "block", padding: "0px 0px" } : { display: "none", padding: "12px 16px" }}>
|
? (<Paper zDepth={0} style={this.props.open ? { display: 'block', padding: '0px 0px' } : { display: 'none', padding: '12px 16px' }}>
|
||||||
<CommentList comments={this.props.comments} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments}/>
|
<CommentListComponent comments={this.props.comments} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments}/>
|
||||||
|
|
||||||
</Paper>) : ''}
|
</Paper>) : ''}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{!this.props.disableComments ? (<div>
|
{!this.props.disableComments ? (<div>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Paper zDepth={0} className="animate2-top10" style={{ position: "relative", overflowY: "auto", padding: "12px 16px", display: (this.props.open ? "block" : "none") }}>
|
<Paper zDepth={0} className='animate2-top10' style={{ position: 'relative', overflowY: 'auto', padding: '12px 16px', display: (this.props.open ? 'block' : 'none') }}>
|
||||||
|
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: 'flex' }}>
|
||||||
<UserAvatar fullName={this.props.fullName} fileName={this.props.avatar} style={{ flex: "none", margin: "4px 0px" }} size={36} />
|
<UserAvatarComponent fullName={this.props.fullName!} fileName={this.props.avatar!} style={{ flex: 'none', margin: '4px 0px' }} size={36} />
|
||||||
<div style={{ outline: "none", marginLeft: "16px", flex: "auto", flexGrow: 1 }}>
|
<div style={{ outline: 'none', marginLeft: '16px', flex: 'auto', flexGrow: 1 }}>
|
||||||
<TextField
|
<TextField
|
||||||
value={this.state.commentText}
|
value={this.state.commentText}
|
||||||
onChange={this.handleOnChange}
|
onChange={this.handleOnChange}
|
||||||
hintText="Add a comment..."
|
hintText='Add a comment...'
|
||||||
underlineShow={false}
|
underlineShow={false}
|
||||||
multiLine={true}
|
multiLine={true}
|
||||||
rows={1}
|
rows={1}
|
||||||
hintStyle={{ fontWeight: 100, fontSize: "14px" }}
|
hintStyle={{ fontWeight: 100, fontSize: '14px' }}
|
||||||
rowsMax={4}
|
rowsMax={4}
|
||||||
textareaStyle={{ fontWeight: 100, fontSize: "14px" }}
|
textareaStyle={{ fontWeight: 100, fontSize: '14px' }}
|
||||||
style={{ width: '100%' }}
|
style={this.styles.writeCommentTextField}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<FlatButton primary={true} disabled={this.state.postDisable} label="Post" style={{ float: "right", clear: "both", zIndex: 5, margin: "0px 5px 5px 0px", fontWeight: 400 }} onClick={this.handlePostComment} />
|
<FlatButton primary={true} disabled={this.state.postDisable} label='Post' style={{ float: 'right', clear: 'both', zIndex: 5, margin: '0px 5px 5px 0px', fontWeight: 400 }} onClick={this.handlePostComment} />
|
||||||
</Paper>
|
</Paper>
|
||||||
</div>): ''}
|
</div>) : ''}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -240,9 +248,9 @@ static propTypes = {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: ICommentGroupComponentProps) => {
|
||||||
return {
|
return {
|
||||||
send: (text, postId, callBack) => {
|
send: (text: string, postId: string, callBack: Function) => {
|
||||||
dispatch(commentActions.dbAddComment(ownProps.ownerPostUserId,{
|
dispatch(commentActions.dbAddComment(ownProps.ownerPostUserId,{
|
||||||
postId: postId,
|
postId: postId,
|
||||||
text: text
|
text: text
|
||||||
@@ -257,15 +265,15 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state: any, ownProps: ICommentGroupComponentProps) => {
|
||||||
return {
|
return {
|
||||||
comments: state.comment.postComments[ownProps.postId],
|
comments: state.comment.postComments[ownProps.postId],
|
||||||
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar || '' : '',
|
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar || '' : '',
|
||||||
fullName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName || '' : '',
|
fullName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName || '' : '',
|
||||||
userInfo: state.user.info
|
userInfo: state.user.info
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(CommentGroup)
|
export default connect(mapStateToProps, mapDispatchToProps)(CommentGroupComponent as any)
|
||||||
93
src/components/commentGroup/ICommentGroupComponentProps.ts
Normal file
93
src/components/commentGroup/ICommentGroupComponentProps.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import { Profile } from 'core/domain/users'
|
||||||
|
import { Comment } from 'core/domain/comments'
|
||||||
|
export interface ICommentGroupComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commnets
|
||||||
|
*
|
||||||
|
* @type {{[commentId: string]: Comment}}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
comments?: {[commentId: string]: Comment}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The post identifier which comment belong to
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
postId: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Users` profile
|
||||||
|
*
|
||||||
|
* @type {{[userId: string]: Profile}}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
userInfo?: {[userId: string]: Profile}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment group is open {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
open: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment is disabled {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
disableComments: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current user is the post owner {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
isPostOwner: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User full name
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
fullName?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Avatar URL address
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
avatar?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle comment list open/close
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
onToggleRequest: () => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of post owner
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
ownerPostUserId: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send comment
|
||||||
|
*
|
||||||
|
* @type {(commentText: string, postId: string, clearCommentWrite: Function)}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
send?: (commentText: string, postId: string, clearCommentWrite: () => void) => any
|
||||||
|
|
||||||
|
}
|
||||||
19
src/components/commentGroup/ICommentGroupComponentState.ts
Normal file
19
src/components/commentGroup/ICommentGroupComponentState.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
export interface ICommentGroupComponentState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment text
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ICommentGroupComponentState
|
||||||
|
*/
|
||||||
|
commentText: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable post comment
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentGroupComponentState
|
||||||
|
*/
|
||||||
|
postDisable: boolean
|
||||||
|
}
|
||||||
2
src/components/commentGroup/index.ts
Normal file
2
src/components/commentGroup/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import CommentGroupComponent from './CommentGroupComponent'
|
||||||
|
export default CommentGroupComponent
|
||||||
@@ -4,24 +4,23 @@ import { connect } from 'react-redux'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { List, ListItem } from 'material-ui/List'
|
import { List, ListItem } from 'material-ui/List'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import Comment from 'Comment'
|
import CommentComponent from 'components/Comment'
|
||||||
import * as PostAPI from 'PostAPI'
|
|
||||||
|
import * as PostAPI from 'api/PostAPI'
|
||||||
|
|
||||||
|
import { ICommentListComponentProps } from './ICommentListComponentProps'
|
||||||
|
import { ICommentListComponentState } from './ICommentListComponentState'
|
||||||
|
import { Comment } from 'core/domain/comments'
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create component class
|
* Create component class
|
||||||
*/
|
*/
|
||||||
export class CommentList extends Component {
|
export class CommentListComponent extends Component<ICommentListComponentProps, ICommentListComponentState> {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
/**
|
/**
|
||||||
* If it's true the post owner is the logged in user which this post be long to the comment
|
* If it's true the post owner is the logged in user which this post be long to the comment
|
||||||
*/
|
*/
|
||||||
@@ -29,14 +28,14 @@ export class CommentList extends Component {
|
|||||||
/**
|
/**
|
||||||
* If it's true the comment is disable to write
|
* If it's true the comment is disable to write
|
||||||
*/
|
*/
|
||||||
disableComments: PropTypes.bool,
|
disableComments: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor (props: ICommentListComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,8 +49,6 @@ export class CommentList extends Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get comments' DOM
|
* Get comments' DOM
|
||||||
* @return {DOM} list of comments' DOM
|
* @return {DOM} list of comments' DOM
|
||||||
@@ -60,8 +57,7 @@ export class CommentList extends Component {
|
|||||||
let comments = this.props.comments
|
let comments = this.props.comments
|
||||||
if (comments) {
|
if (comments) {
|
||||||
|
|
||||||
|
let parsedComments: Comment[] = []
|
||||||
let parsedComments = []
|
|
||||||
Object.keys(comments).forEach((commentId) => {
|
Object.keys(comments).forEach((commentId) => {
|
||||||
parsedComments.push({
|
parsedComments.push({
|
||||||
id: commentId,
|
id: commentId,
|
||||||
@@ -69,10 +65,10 @@ export class CommentList extends Component {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
let sortedComments = PostAPI.sortObjectsDate(parsedComments)
|
let sortedComments = PostAPI.sortObjectsDate(parsedComments)
|
||||||
|
|
||||||
return sortedComments.map((comment, index, array) => {
|
|
||||||
|
|
||||||
return <Comment key={comment.id} comment={comment} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments}/>
|
return sortedComments.map((comment: Comment, index: number, array: Comment) => {
|
||||||
|
|
||||||
|
return <CommentComponent key={comment.id} comment={comment} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments}/>
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -83,11 +79,11 @@ export class CommentList extends Component {
|
|||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
const styles = {
|
const styles: any = {
|
||||||
list: {
|
list: {
|
||||||
width: "100%",
|
width: '100%',
|
||||||
maxHeight: 450,
|
maxHeight: 450,
|
||||||
overflowY: 'auto'
|
overflowY: 'auto'
|
||||||
}
|
}
|
||||||
@@ -95,7 +91,6 @@ export class CommentList extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
||||||
<List style={styles.list}>
|
<List style={styles.list}>
|
||||||
|
|
||||||
{this.commentList()}
|
{this.commentList()}
|
||||||
@@ -110,10 +105,9 @@ export class CommentList extends Component {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: ICommentListComponentProps) => {
|
||||||
return {
|
return {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,11 +117,11 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state: any) => {
|
||||||
return {
|
return {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(CommentList)
|
export default connect(mapStateToProps, mapDispatchToProps)(CommentListComponent as any)
|
||||||
28
src/components/commentList/ICommentListComponentProps.ts
Normal file
28
src/components/commentList/ICommentListComponentProps.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Comment } from 'core/domain/comments'
|
||||||
|
|
||||||
|
export interface ICommentListComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ad dictionary of comment
|
||||||
|
*
|
||||||
|
* @type {{[commentId: string]: Comment}}
|
||||||
|
* @memberof ICommentListComponentProps
|
||||||
|
*/
|
||||||
|
comments: {[commentId: string]: Comment}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current user is post the post owner {true} or not false
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentListComponentProps
|
||||||
|
*/
|
||||||
|
isPostOwner: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment on the post is disabled {false} or not {true}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof ICommentListComponentProps
|
||||||
|
*/
|
||||||
|
disableComments: boolean
|
||||||
|
}
|
||||||
4
src/components/commentList/ICommentListComponentState.ts
Normal file
4
src/components/commentList/ICommentListComponentState.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export interface ICommentListComponentState {
|
||||||
|
|
||||||
|
}
|
||||||
2
src/components/commentList/index.ts
Normal file
2
src/components/commentList/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import CommentListComponent from './CommentListComponent'
|
||||||
|
export default CommentListComponent
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
// - Import react components
|
// - Import react components
|
||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
@@ -18,316 +16,296 @@ import Divider from 'material-ui/Divider'
|
|||||||
import Paper from 'material-ui/Paper'
|
import Paper from 'material-ui/Paper'
|
||||||
import TextField from 'material-ui/TextField'
|
import TextField from 'material-ui/TextField'
|
||||||
|
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import ImgCover from 'ImgCover'
|
import ImgCover from 'components/imgCover'
|
||||||
import DialogTitle from 'DialogTitle'
|
import UserAvatarComponent from 'components/userAvatar'
|
||||||
import ImageGallery from 'ImageGallery'
|
import ImageGallery from 'components/imageGallery'
|
||||||
import FileAPI from 'FileAPI'
|
import DialogTitle from 'layouts/DialogTitle'
|
||||||
import UserAvatar from 'UserAvatar'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// - Import API
|
// - Import API
|
||||||
|
import FileAPI from 'api/FileAPI'
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as userActions from 'userActions'
|
import * as userActions from 'actions/userActions'
|
||||||
import * as globalActions from 'globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
import * as imageGalleryActions from 'imageGalleryActions'
|
import * as imageGalleryActions from 'actions/imageGalleryActions'
|
||||||
|
|
||||||
|
import { IEditProfileComponentProps } from './IEditProfileComponentProps'
|
||||||
|
import { IEditProfileComponentState } from './IEditProfileComponentState'
|
||||||
|
import { Profile } from 'core/domain/users'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create component class
|
* Create component class
|
||||||
*/
|
*/
|
||||||
export class EditProfile extends Component {
|
export class EditProfileComponent extends Component<IEditProfileComponentProps,IEditProfileComponentState> {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User avatar address
|
* User avatar address
|
||||||
*/
|
*/
|
||||||
avatar: PropTypes.string,
|
avatar: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* User avatar address
|
* User avatar address
|
||||||
*/
|
*/
|
||||||
banner: PropTypes.string,
|
banner: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* User full name
|
* User full name
|
||||||
*/
|
*/
|
||||||
fullName: PropTypes.string.isRequired
|
fullName: PropTypes.string.isRequired
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
styles = {
|
||||||
|
avatar: {
|
||||||
|
border: '2px solid rgb(255, 255, 255)'
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
width: '90%',
|
||||||
|
height: '100%',
|
||||||
|
margin: '0 auto',
|
||||||
|
display: 'block'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
padding: '24px 24px 20px 24px',
|
||||||
|
font: '500 20px Roboto,RobotoDraft,Helvetica,Arial,sans-serif',
|
||||||
|
display: 'flex',
|
||||||
|
wordWrap: 'break-word',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
flexGrow: 1
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
padding: '24px 24px 20px'
|
||||||
|
},
|
||||||
|
updateButton: {
|
||||||
|
marginLeft: '10px'
|
||||||
|
},
|
||||||
|
box: {
|
||||||
|
padding: '0px 24px 20px 24px',
|
||||||
|
display: 'flex'
|
||||||
|
|
||||||
|
},
|
||||||
|
dialogGallery: {
|
||||||
|
width: '',
|
||||||
|
maxWidth: '530px',
|
||||||
|
borderRadius: '4px'
|
||||||
|
},
|
||||||
|
iconButtonSmall: {
|
||||||
|
},
|
||||||
|
iconButton: {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor (props: IEditProfileComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
//Defaul state
|
// Defaul state
|
||||||
this.state = {
|
this.state = {
|
||||||
/**
|
/**
|
||||||
* If it's true the winow is in small size
|
* If it's true the winow is in small size
|
||||||
*/
|
*/
|
||||||
isSmall: false,
|
isSmall: false,
|
||||||
/**
|
/**
|
||||||
* User tag line input value
|
* User tag line input value
|
||||||
*/
|
*/
|
||||||
tagLineInput: props.info.tagLine || '',
|
tagLineInput: props.info!.tagLine || '',
|
||||||
/**
|
/**
|
||||||
* User full name input value
|
* User full name input value
|
||||||
*/
|
*/
|
||||||
fullNameInput: props.info.fullName || '',
|
fullNameInput: props.info!.fullName || '',
|
||||||
/**
|
/**
|
||||||
* Error message of full name input
|
* Error message of full name input
|
||||||
*/
|
*/
|
||||||
fullNameInputError: '',
|
fullNameInputError: '',
|
||||||
/**
|
/**
|
||||||
* User banner address
|
* User banner address
|
||||||
*/
|
*/
|
||||||
banner: this.props.banner || 'https://firebasestorage.googleapis.com/v0/b/open-social-33d92.appspot.com/o/images%2F751145a1-9488-46fd-a97e-04018665a6d3.JPG?alt=media&token=1a1d5e21-5101-450e-9054-ea4a20e06c57',
|
banner: props.banner || 'https://firebasestorage.googleapis.com/v0/b/open-social-33d92.appspot.com/o/images%2F751145a1-9488-46fd-a97e-04018665a6d3.JPG?alt=media&token=1a1d5e21-5101-450e-9054-ea4a20e06c57',
|
||||||
/**
|
/**
|
||||||
* User avatar address
|
* User avatar address
|
||||||
*/
|
*/
|
||||||
avatar: this.props.avatar || '',
|
avatar: props.avatar || '',
|
||||||
/**
|
/**
|
||||||
* It's true if the image galley for banner is open
|
* It's true if the image galley for banner is open
|
||||||
*/
|
*/
|
||||||
openBanner: false,
|
openBanner: false,
|
||||||
/**
|
/**
|
||||||
* It's true if the image gallery for avatar is open
|
* It's true if the image gallery for avatar is open
|
||||||
*/
|
*/
|
||||||
openAvatar: false
|
openAvatar: false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binding functions to `this`
|
// Binding functions to `this`
|
||||||
this.handleChangeDate = this.handleChangeDate.bind(this)
|
this.handleUpdate = this.handleUpdate.bind(this)
|
||||||
this.handleUpdate = this.handleUpdate.bind(this)
|
this.handleRequestSetAvatar = this.handleRequestSetAvatar.bind(this)
|
||||||
this.handleRequestSetAvatar = this.handleRequestSetAvatar.bind(this)
|
this.handleRequestSetBanner = this.handleRequestSetBanner.bind(this)
|
||||||
this.handleRequestSetBanner = this.handleRequestSetBanner.bind(this)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close image gallery of banner
|
* Close image gallery of banner
|
||||||
*/
|
*/
|
||||||
handleCloseBannerGallery = () => {
|
handleCloseBannerGallery = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
openBanner: false
|
openBanner: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open image gallery of banner
|
* Open image gallery of banner
|
||||||
*/
|
*/
|
||||||
handleOpenBannerGallery = () => {
|
handleOpenBannerGallery = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
openBanner: true
|
openBanner: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close image gallery of avatar
|
* Close image gallery of avatar
|
||||||
*/
|
*/
|
||||||
handleCloseAvatarGallery = () => {
|
handleCloseAvatarGallery = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
openAvatar: false
|
openAvatar: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open image gallery of avatar
|
* Open image gallery of avatar
|
||||||
*/
|
*/
|
||||||
handleOpenAvatarGallery = () => {
|
handleOpenAvatarGallery = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
openAvatar: true
|
openAvatar: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
/**
|
|
||||||
* Set banner image url
|
* Set banner image url
|
||||||
*/
|
*/
|
||||||
handleRequestSetBanner = (url) => {
|
handleRequestSetBanner = (url: string) => {
|
||||||
console.log('==========Banner==================')
|
this.setState({
|
||||||
console.log(url)
|
banner: url
|
||||||
console.log('====================================')
|
})
|
||||||
this.setState({
|
}
|
||||||
banner: url
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set avatar image url
|
* Set avatar image url
|
||||||
*/
|
*/
|
||||||
handleRequestSetAvatar = (fileName) => {
|
handleRequestSetAvatar = (fileName: string) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
avatar: fileName
|
avatar: fileName
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update profile on the server
|
* Update profile on the server
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @memberof EditProfile
|
* @memberof EditProfile
|
||||||
*/
|
*/
|
||||||
handleUpdate = () => {
|
handleUpdate = () => {
|
||||||
const {fullNameInput, tagLineInput, avatar, banner} = this.state
|
const {fullNameInput, tagLineInput, avatar, banner} = this.state
|
||||||
|
|
||||||
if (this.state.fullNameInput.trim() === '') {
|
|
||||||
this.setState({
|
|
||||||
fullNameInputError: 'This field is required'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.setState({
|
|
||||||
fullNameInputError: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
this.props.update({
|
if (this.state.fullNameInput.trim() === '') {
|
||||||
fullName: fullNameInput,
|
this.setState({
|
||||||
tagLine: tagLineInput,
|
fullNameInputError: 'This field is required'
|
||||||
avatar: avatar,
|
})
|
||||||
banner: banner
|
} else {
|
||||||
})
|
this.setState({
|
||||||
}
|
fullNameInputError: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
this.props.update!({
|
||||||
|
fullName: fullNameInput,
|
||||||
|
tagLine: tagLineInput,
|
||||||
|
avatar: avatar,
|
||||||
|
banner: banner
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle data on input change
|
* Handle data on input change
|
||||||
* @param {event} evt is an event of inputs of element on change
|
* @param {event} evt is an event of inputs of element on change
|
||||||
*/
|
*/
|
||||||
handleInputChange = (evt) => {
|
handleInputChange = (event: any) => {
|
||||||
const target = evt.target
|
const target = event.target
|
||||||
const value = target.type === 'checkbox' ? target.checked : target.value
|
const value = target.type === 'checkbox' ? target.checked : target.value
|
||||||
const name = target.name
|
const name = target.name
|
||||||
this.setState({
|
this.setState({
|
||||||
[name]: value
|
[name]: value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Handle change date
|
|
||||||
*/
|
|
||||||
handleChangeDate = (evt, date) => {
|
|
||||||
this.setState({
|
|
||||||
birthdayInput: date,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle resize event for window to change sidebar status
|
* Handle resize event for window to change sidebar status
|
||||||
* @param {event} evt is the event is passed by winodw resize event
|
* @param {any} event is the event is passed by winodw resize event
|
||||||
*/
|
*/
|
||||||
handleResize = (evt) => {
|
handleResize = (event: any) => {
|
||||||
|
|
||||||
// Set initial state
|
// Set initial state
|
||||||
let width = window.innerWidth
|
let width = window.innerWidth
|
||||||
|
|
||||||
if (width > 900) {
|
if (width > 900) {
|
||||||
this.setState({
|
this.setState({
|
||||||
isSmall: false
|
isSmall: false
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
} else {
|
||||||
else {
|
this.setState({
|
||||||
this.setState({
|
isSmall: true
|
||||||
isSmall: true
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
componentDidMount = () => {
|
|
||||||
this.handleResize()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
this.handleResize(null)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
const styles = {
|
const iconButtonElement = (
|
||||||
avatar: {
|
<IconButton style={this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton} iconStyle={this.state.isSmall ? this.styles.iconButtonSmall : this.styles.iconButton}
|
||||||
border: '2px solid rgb(255, 255, 255)'
|
|
||||||
},
|
|
||||||
paper: {
|
|
||||||
width: '90%',
|
|
||||||
height: '100%',
|
|
||||||
margin: '0 auto',
|
|
||||||
display: 'block',
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
padding: '24px 24px 20px 24px',
|
|
||||||
font: '500 20px Roboto,RobotoDraft,Helvetica,Arial,sans-serif',
|
|
||||||
display: 'flex',
|
|
||||||
wordWrap: 'break-word',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
overflow: 'hidden',
|
|
||||||
flexGrow: 1
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
padding: '24px 24px 20px'
|
|
||||||
},
|
|
||||||
updateButton: {
|
|
||||||
marginLeft: '10px'
|
|
||||||
},
|
|
||||||
box: {
|
|
||||||
padding: '0px 24px 20px 24px',
|
|
||||||
display: 'flex'
|
|
||||||
|
|
||||||
},
|
|
||||||
dialogGallery: {
|
|
||||||
width: '',
|
|
||||||
maxWidth: '530px',
|
|
||||||
borderRadius: "4px"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const iconButtonElement = (
|
|
||||||
<IconButton style={this.state.isSmall ? styles.iconButtonSmall : styles.iconButton} iconStyle={this.state.isSmall ? styles.iconButtonSmall : styles.iconButton}
|
|
||||||
touch={true}
|
touch={true}
|
||||||
>
|
>
|
||||||
<MoreVertIcon color={grey400} viewBox='10 0 24 24' />
|
<MoreVertIcon color={grey400} viewBox='10 0 24 24' />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const RightIconMenu = () => (
|
||||||
const RightIconMenu = () => (
|
|
||||||
<IconMenu iconButtonElement={iconButtonElement}>
|
<IconMenu iconButtonElement={iconButtonElement}>
|
||||||
<MenuItem style={{ fontSize: "14px" }}>Reply</MenuItem>
|
<MenuItem style={{ fontSize: '14px' }}>Reply</MenuItem>
|
||||||
<MenuItem style={{ fontSize: "14px" }}>Edit</MenuItem>
|
<MenuItem style={{ fontSize: '14px' }}>Edit</MenuItem>
|
||||||
<MenuItem style={{ fontSize: "14px" }}>Delete</MenuItem>
|
<MenuItem style={{ fontSize: '14px' }}>Delete</MenuItem>
|
||||||
</IconMenu>
|
</IconMenu>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
return (
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{/* Edit profile dialog */}
|
{/* Edit profile dialog */}
|
||||||
<Dialog
|
<Dialog
|
||||||
id='Edit-Profile'
|
key='Edit-Profile'
|
||||||
modal={false}
|
modal={false}
|
||||||
open={this.props.open}
|
open={this.props.open!}
|
||||||
onRequestClose={this.props.onRequestClose}
|
onRequestClose={this.props.onRequestClose}
|
||||||
autoScrollBodyContent={true}
|
autoScrollBodyContent={true}
|
||||||
bodyStyle={{ backgroundColor: "none", padding: 'none', borderTop: 'none', borderBottom: 'none' }}
|
bodyStyle={{ backgroundColor: 'none', padding: 'none', borderTop: 'none', borderBottom: 'none' }}
|
||||||
overlayStyle={{ background: "rgba(0,0,0,0.12)" }}
|
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
|
||||||
contentStyle={{ backgroundColor: "none", maxWidth: '450px', maxHeight: 'none', height: '100%' }}
|
contentStyle={{ backgroundColor: 'none', maxWidth: '450px', maxHeight: 'none', height: '100%' }}
|
||||||
style={{ backgroundColor: "none", maxHeight: 'none', height: '100%' }}
|
style={{ backgroundColor: 'none', maxHeight: 'none', height: '100%' }}
|
||||||
>
|
>
|
||||||
{/* Banner */}
|
{/* Banner */}
|
||||||
<div style={{ position: 'relative' }}>
|
<div style={{ position: 'relative' }}>
|
||||||
@@ -338,7 +316,7 @@ export class EditProfile extends Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className='profile__edit'>
|
<div className='profile__edit'>
|
||||||
<EventListener
|
<EventListener
|
||||||
target="window"
|
target='window'
|
||||||
onResize={this.handleResize}
|
onResize={this.handleResize}
|
||||||
/>
|
/>
|
||||||
<div className='left'>
|
<div className='left'>
|
||||||
@@ -348,25 +326,24 @@ export class EditProfile extends Component {
|
|||||||
<SvgCamera style={{ fill: 'rgba(255, 255, 255, 0.88)', transform: 'translate(6px, 6px)' }} />
|
<SvgCamera style={{ fill: 'rgba(255, 255, 255, 0.88)', transform: 'translate(6px, 6px)' }} />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<UserAvatar fullName={(this.props.info ? this.props.info.fullName : '')} fileName={this.state.avatar} size={90} style={styles.avatar} />
|
<UserAvatarComponent fullName={(this.props.info ? this.props.info.fullName : '')} fileName={this.state.avatar} size={90} style={this.styles.avatar} />
|
||||||
</div>
|
</div>
|
||||||
<div className='info'>
|
<div className='info'>
|
||||||
<div className='fullName'>
|
<div className='fullName'>
|
||||||
{this.props.fullName}
|
{this.props.fullName}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Edit user information box*/}
|
{/* Edit user information box*/}
|
||||||
<Paper style={styles.paper} zDepth={1}>
|
<Paper style={this.styles.paper} zDepth={1}>
|
||||||
<div style={styles.title}>Personal Information</div>
|
<div style={this.styles.title as any}>Personal Information</div>
|
||||||
<div style={styles.box}>
|
<div style={this.styles.box}>
|
||||||
<TextField
|
<TextField
|
||||||
floatingLabelText="Full name"
|
floatingLabelText='Full name'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
name='fullNameInput'
|
name='fullNameInput'
|
||||||
errorText={this.state.fullNameInputError}
|
errorText={this.state.fullNameInputError}
|
||||||
@@ -374,23 +351,22 @@ export class EditProfile extends Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div style={styles.box}>
|
<div style={this.styles.box}>
|
||||||
<TextField
|
<TextField
|
||||||
floatingLabelText="Tag Line"
|
floatingLabelText='Tag Line'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
name='tagLineInput'
|
name='tagLineInput'
|
||||||
value={this.state.tagLineInput}
|
value={this.state.tagLineInput}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div style={styles.actions}>
|
<div style={this.styles.actions as any}>
|
||||||
<FlatButton label="CANCEL" onClick={this.props.onRequestClose} />
|
<FlatButton label='CANCEL' onClick={this.props.onRequestClose} />
|
||||||
<RaisedButton label="UPDATE" primary={true} onClick={this.handleUpdate} style={styles.updateButton} />
|
<RaisedButton label='UPDATE' primary={true} onClick={this.handleUpdate} style={this.styles.updateButton} />
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
<div style={{ height: '16px' }}></div>
|
<div style={{ height: '16px' }}></div>
|
||||||
|
|
||||||
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
{/* Image gallery for banner*/}
|
{/* Image gallery for banner*/}
|
||||||
@@ -398,9 +374,9 @@ export class EditProfile extends Component {
|
|||||||
title={<DialogTitle title='Choose an banner image' onRequestClose={this.handleCloseBannerGallery} />}
|
title={<DialogTitle title='Choose an banner image' onRequestClose={this.handleCloseBannerGallery} />}
|
||||||
modal={false}
|
modal={false}
|
||||||
open={this.state.openBanner}
|
open={this.state.openBanner}
|
||||||
contentStyle={styles.dialogGallery}
|
contentStyle={this.styles.dialogGallery}
|
||||||
onRequestClose={this.handleCloseBannerGallery}
|
onRequestClose={this.handleCloseBannerGallery}
|
||||||
overlayStyle={{ background: "rgba(0,0,0,0.12)" }}
|
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
|
||||||
autoDetectWindowHeight={false}
|
autoDetectWindowHeight={false}
|
||||||
|
|
||||||
>
|
>
|
||||||
@@ -412,9 +388,9 @@ export class EditProfile extends Component {
|
|||||||
title={<DialogTitle title='Choose an avatar image' onRequestClose={this.handleCloseAvatarGallery} />}
|
title={<DialogTitle title='Choose an avatar image' onRequestClose={this.handleCloseAvatarGallery} />}
|
||||||
modal={false}
|
modal={false}
|
||||||
open={this.state.openAvatar}
|
open={this.state.openAvatar}
|
||||||
contentStyle={styles.dialogGallery}
|
contentStyle={this.styles.dialogGallery}
|
||||||
onRequestClose={this.handleCloseAvatarGallery}
|
onRequestClose={this.handleCloseAvatarGallery}
|
||||||
overlayStyle={{ background: "rgba(0,0,0,0.12)" }}
|
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
|
||||||
autoDetectWindowHeight={false}
|
autoDetectWindowHeight={false}
|
||||||
|
|
||||||
>
|
>
|
||||||
@@ -422,23 +398,22 @@ export class EditProfile extends Component {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map dispatch to props
|
* Map dispatch to props
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
* @param {func} dispatch is the function to dispatch action to reducers
|
||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: IEditProfileComponentProps) => {
|
||||||
return {
|
return {
|
||||||
update: (info) => dispatch(userActions.dbUpdateUserInfo(info)),
|
update: (info: Profile) => dispatch(userActions.dbUpdateUserInfo(info)),
|
||||||
onRequestClose: () => dispatch(userActions.closeEditProfile())
|
onRequestClose: () => dispatch(userActions.closeEditProfile())
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -447,14 +422,14 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state: any, ownProps: IEditProfileComponentProps) => {
|
||||||
return {
|
return {
|
||||||
open: state.user.openEditProfile,
|
open: state.user.openEditProfile,
|
||||||
info: state.user.info[state.authorize.uid],
|
info: state.user.info[state.authorize.uid],
|
||||||
avatarURL: state.imageGallery.imageURLList
|
avatarURL: state.imageGallery.imageURLList
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(EditProfile)
|
export default connect(mapStateToProps, mapDispatchToProps)(EditProfileComponent as any)
|
||||||
58
src/components/editProfile/IEditProfileComponentProps.ts
Normal file
58
src/components/editProfile/IEditProfileComponentProps.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { Profile } from 'core/domain/users'
|
||||||
|
|
||||||
|
export interface IEditProfileComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User profile
|
||||||
|
*
|
||||||
|
* @type {Profile}
|
||||||
|
* @memberof IEditProfileComponentProps
|
||||||
|
*/
|
||||||
|
info?: Profile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User profile banner addresss
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IEditProfileComponentProps
|
||||||
|
*/
|
||||||
|
banner: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User avatar address
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IEditProfileComponentProps
|
||||||
|
*/
|
||||||
|
avatar: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User full name
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IEditProfileComponentProps
|
||||||
|
*/
|
||||||
|
fullName: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit profile dialog is open {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IEditProfileComponentProps
|
||||||
|
*/
|
||||||
|
open?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user profile
|
||||||
|
*
|
||||||
|
* @memberof IEditProfileComponentProps
|
||||||
|
*/
|
||||||
|
update?: (profile: Profile) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On edit profile dialog close event
|
||||||
|
*
|
||||||
|
* @memberof IEditProfileComponentProps
|
||||||
|
*/
|
||||||
|
onRequestClose?: () => void
|
||||||
|
}
|
||||||
68
src/components/editProfile/IEditProfileComponentState.ts
Normal file
68
src/components/editProfile/IEditProfileComponentState.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
|
||||||
|
export interface IEditProfileComponentState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full name input value
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IEditProfileComponentState
|
||||||
|
*/
|
||||||
|
fullNameInput: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full name input error message
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IEditProfileComponentState
|
||||||
|
*/
|
||||||
|
fullNameInputError: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag line input value
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IEditProfileComponentState
|
||||||
|
*/
|
||||||
|
tagLineInput: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit profile page is small size {true} or big {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IEditProfileComponentState
|
||||||
|
*/
|
||||||
|
isSmall: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's banner URL
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IEditProfileComponentState
|
||||||
|
*/
|
||||||
|
banner: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's avatar URL address
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IEditProfileComponentState
|
||||||
|
*/
|
||||||
|
avatar: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image gallery dialog is open for choosing banner image {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IEditProfileComponentState
|
||||||
|
*/
|
||||||
|
openBanner: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image gallery dialog is open for choosing avatar image {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IEditProfileComponentState
|
||||||
|
*/
|
||||||
|
openAvatar: boolean
|
||||||
|
|
||||||
|
}
|
||||||
2
src/components/editProfile/index.ts
Normal file
2
src/components/editProfile/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import EditProfileComponent from './EditProfileComponent'
|
||||||
|
export default EditProfileComponent
|
||||||
@@ -4,70 +4,69 @@ import { connect } from 'react-redux'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Paper from 'material-ui/Paper'
|
import Paper from 'material-ui/Paper'
|
||||||
|
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import UserBoxList from 'UserBoxList'
|
import UserBoxList from 'components/userBoxList'
|
||||||
|
|
||||||
|
|
||||||
// - Import API
|
// - Import API
|
||||||
|
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as userActions from 'userActions'
|
import * as userActions from 'actions/userActions'
|
||||||
|
import { IFindPeopleComponentProps } from './IFindPeopleComponentProps'
|
||||||
|
import { IFindPeopleComponentState } from './IFindPeopleComponentState'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create component class
|
* Create component class
|
||||||
*/
|
*/
|
||||||
export class FindPeople extends Component {
|
export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IFindPeopleComponentState> {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor (props: IFindPeopleComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
//Defaul state
|
// Defaul state
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binding functions to `this`
|
// Binding functions to `this`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount () {
|
||||||
this.props.loadPeople()
|
this.props.loadPeople!()
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
paper: {
|
paper: {
|
||||||
height: 254,
|
height: 254,
|
||||||
width: 243,
|
width: 243,
|
||||||
margin: 10,
|
margin: 10,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
maxWidth: '257px'
|
maxWidth: '257px'
|
||||||
},
|
},
|
||||||
followButton:{
|
followButton: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: '8px',
|
bottom: '8px',
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0
|
right: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.props.peopleInfo && Object.keys(this.props.peopleInfo).length !== 0 ? (<div>
|
{this.props.peopleInfo && Object.keys(this.props.peopleInfo).length !== 0 ? (<div>
|
||||||
<div className='profile__title'>
|
<div className='profile__title'>
|
||||||
Suggestions for you
|
Suggestions for you
|
||||||
</div>
|
</div>
|
||||||
@@ -77,21 +76,20 @@ export class FindPeople extends Component {
|
|||||||
Nothing to show! :(
|
Nothing to show! :(
|
||||||
</div>)}
|
</div>)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map dispatch to props
|
* Map dispatch to props
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
* @param {func} dispatch is the function to dispatch action to reducers
|
||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: IFindPeopleComponentProps) => {
|
||||||
return {
|
return {
|
||||||
loadPeople: () => dispatch(userActions.dbGetPeopleInfo())
|
loadPeople: () => dispatch(userActions.dbGetPeopleInfo())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,11 +98,11 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state: any, ownProps: IFindPeopleComponentProps) => {
|
||||||
return {
|
return {
|
||||||
peopleInfo: state.user.info
|
peopleInfo: state.user.info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(FindPeople)
|
export default connect(mapStateToProps, mapDispatchToProps)(FindPeopleComponent as any)
|
||||||
20
src/components/findPeople/IFindPeopleComponentProps.ts
Normal file
20
src/components/findPeople/IFindPeopleComponentProps.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Profile } from 'core/domain/users/profile'
|
||||||
|
|
||||||
|
export interface IFindPeopleComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load users' profile
|
||||||
|
*
|
||||||
|
* @memberof IFindPeopleComponentProps
|
||||||
|
*/
|
||||||
|
loadPeople?: () => any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Users' profile
|
||||||
|
*
|
||||||
|
* @type {{[userId: string]: Profile}}
|
||||||
|
* @memberof IFindPeopleComponentProps
|
||||||
|
*/
|
||||||
|
peopleInfo?: {[userId: string]: Profile}
|
||||||
|
|
||||||
|
}
|
||||||
4
src/components/findPeople/IFindPeopleComponentState.ts
Normal file
4
src/components/findPeople/IFindPeopleComponentState.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export interface IFindPeopleComponentState {
|
||||||
|
|
||||||
|
}
|
||||||
2
src/components/findPeople/index.ts
Normal file
2
src/components/findPeople/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import FindPeopleComponent from './FindPeopleComponent'
|
||||||
|
export default FindPeopleComponent
|
||||||
@@ -1,33 +1,35 @@
|
|||||||
// - Import react components
|
// - Import react components
|
||||||
import React, {Component} from 'react'
|
import React, { Component } from 'react'
|
||||||
import {connect} from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import UserBoxList from 'UserBoxList'
|
import UserBoxList from 'components/userBoxList'
|
||||||
|
|
||||||
|
import { IFollowersComponentProps } from './IFollowersComponentProps'
|
||||||
|
import { IFollowersComponentState } from './IFollowersComponentState'
|
||||||
|
|
||||||
// - Import API
|
// - Import API
|
||||||
|
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create component class
|
* Create component class
|
||||||
*/
|
*/
|
||||||
export class Followers extends Component {
|
export class FollowersComponent extends Component<IFollowersComponentProps,IFollowersComponentState> {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props){
|
constructor (props: IFollowersComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
//Defaul state
|
// Defaul state
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -40,24 +42,23 @@ static propTypes = {
|
|||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{(this.props.followers && Object.keys(this.props.followers).length !==0) ? (<div>
|
{(this.props.followers && Object.keys(this.props.followers).length !== 0) ? (<div>
|
||||||
<div className='profile__title'>
|
<div className='profile__title'>
|
||||||
Followers
|
Followers
|
||||||
</div>
|
</div>
|
||||||
<UserBoxList users={this.props.followers} />
|
<UserBoxList users={this.props.followers} />
|
||||||
<div style={{ height: '24px' }}></div>
|
<div style={{ height: '24px' }}></div>
|
||||||
</div>)
|
</div>)
|
||||||
: (<div className='g__title-center'>
|
: (<div className='g__title-center'>
|
||||||
No followers!
|
No followers!
|
||||||
</div>)}
|
</div>)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map dispatch to props
|
* Map dispatch to props
|
||||||
@@ -65,11 +66,11 @@ static propTypes = {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch,ownProps) => {
|
const mapDispatchToProps = (dispatch: any,ownProps: IFollowersComponentProps) => {
|
||||||
return{
|
return{
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map state to props
|
* Map state to props
|
||||||
@@ -77,13 +78,13 @@ static propTypes = {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state,ownProps) => {
|
const mapStateToProps = (state: any,ownProps: IFollowersComponentProps) => {
|
||||||
const { uid } = state.authorize
|
const { uid } = state.authorize
|
||||||
const circles = state.circle ? state.circle.userCircles[uid] : {}
|
const circles = state.circle ? state.circle.userCircles[uid] : {}
|
||||||
return{
|
return{
|
||||||
followers: circles ? (circles['-Followers'] ? circles['-Followers'].users || {} : {}) : {}
|
followers: circles ? (circles['-Followers'] ? circles['-Followers'].users || {} : {}) : {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps,mapDispatchToProps)(Followers)
|
export default connect(mapStateToProps,mapDispatchToProps)(FollowersComponent as any)
|
||||||
12
src/components/followers/IFollowersComponentProps.ts
Normal file
12
src/components/followers/IFollowersComponentProps.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { UserFollower } from 'core/domain/circles'
|
||||||
|
|
||||||
|
export interface IFollowersComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User followers info
|
||||||
|
*
|
||||||
|
* @type {{[userId: string]: UserFollower}}
|
||||||
|
* @memberof IFindPeopleComponentProps
|
||||||
|
*/
|
||||||
|
followers?: {[userId: string]: UserFollower}
|
||||||
|
}
|
||||||
4
src/components/followers/IFollowersComponentState.ts
Normal file
4
src/components/followers/IFollowersComponentState.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export interface IFollowersComponentState {
|
||||||
|
|
||||||
|
}
|
||||||
2
src/components/followers/index.ts
Normal file
2
src/components/followers/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import FollowersComponent from './FollowersComponent'
|
||||||
|
export default FollowersComponent
|
||||||
@@ -1,34 +1,35 @@
|
|||||||
// - Import react components
|
// - Import react components
|
||||||
import React, {Component} from 'react'
|
import React, { Component } from 'react'
|
||||||
import {connect} from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import UserBoxList from 'UserBoxList'
|
import UserBoxList from 'components/userBoxList'
|
||||||
|
|
||||||
// - Import API
|
// - Import API
|
||||||
import CircleAPI from 'CircleAPI'
|
import CircleAPI from 'api/CircleAPI'
|
||||||
|
import { IFollowingComponentProps } from './IFollowingComponentProps'
|
||||||
|
import { IFollowingComponentState } from './IFollowingComponentState'
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create component class
|
* Create component class
|
||||||
*/
|
*/
|
||||||
export class Following extends Component {
|
export class FollowingComponent extends Component<IFollowingComponentProps,IFollowingComponentState> {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props){
|
constructor (props: IFollowingComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
//Defaul state
|
// Defaul state
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -41,25 +42,24 @@ static propTypes = {
|
|||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{(this.props.followingUsers && Object.keys(this.props.followingUsers).length !== 0 )? (<div>
|
{(this.props.followingUsers && Object.keys(this.props.followingUsers).length !== 0 ) ? (<div>
|
||||||
<div className='profile__title'>
|
<div className='profile__title'>
|
||||||
Following
|
Following
|
||||||
</div>
|
</div>
|
||||||
<UserBoxList users={this.props.followingUsers} />
|
<UserBoxList users={this.props.followingUsers} />
|
||||||
<div style={{ height: '24px' }}></div>
|
<div style={{ height: '24px' }}></div>
|
||||||
|
|
||||||
</div>) : (<div className='g__title-center'>
|
</div>) : (<div className='g__title-center'>
|
||||||
No following user!
|
No following user!
|
||||||
</div>)}
|
</div>)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map dispatch to props
|
* Map dispatch to props
|
||||||
@@ -67,11 +67,11 @@ static propTypes = {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch,ownProps) => {
|
const mapDispatchToProps = (dispatch: any,ownProp: IFollowingComponentProps) => {
|
||||||
return{
|
return{
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map state to props
|
* Map state to props
|
||||||
@@ -79,17 +79,17 @@ static propTypes = {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state,ownProps) => {
|
const mapStateToProps = (state: any,ownProps: IFollowingComponentProps) => {
|
||||||
const { uid } = state.authorize
|
const { uid } = state.authorize
|
||||||
const circles = state.circle ? state.circle.userCircles[uid] : {}
|
const circles = state.circle ? state.circle.userCircles[uid] : {}
|
||||||
const followingUsers = CircleAPI.getFollowingUsers(circles)
|
const followingUsers = CircleAPI.getFollowingUsers(circles)
|
||||||
return {
|
return {
|
||||||
uid,
|
uid,
|
||||||
circles,
|
circles,
|
||||||
followingUsers,
|
followingUsers
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps,mapDispatchToProps)(Following)
|
export default connect(mapStateToProps,mapDispatchToProps)(FollowingComponent as any)
|
||||||
6
src/components/following/IFollowingComponentProps.ts
Normal file
6
src/components/following/IFollowingComponentProps.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { UserFollower } from 'core/domain/circles'
|
||||||
|
|
||||||
|
export interface IFollowingComponentProps {
|
||||||
|
|
||||||
|
followingUsers?: {[userId: string]: UserFollower}
|
||||||
|
}
|
||||||
4
src/components/following/IFollowingComponentState.ts
Normal file
4
src/components/following/IFollowingComponentState.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export interface IFollowingComponentState {
|
||||||
|
|
||||||
|
}
|
||||||
2
src/components/following/index.ts
Normal file
2
src/components/following/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import FollowingComponent from './FollowingComponent'
|
||||||
|
export default FollowingComponent
|
||||||
@@ -15,34 +15,36 @@ import SvgAccountCircle from 'material-ui/svg-icons/action/account-circle'
|
|||||||
import SvgPeople from 'material-ui/svg-icons/social/people'
|
import SvgPeople from 'material-ui/svg-icons/social/people'
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import Sidebar from 'Sidebar'
|
import Sidebar from 'components/sidebar'
|
||||||
import Blog from 'Blog'
|
import StreamComponent from 'components/stream'
|
||||||
import HomeHeader from 'HomeHeader'
|
import HomeHeader from 'components/homeHeader'
|
||||||
import SidebarContent from 'SidebarContent'
|
import SidebarContent from 'components/sidebarContent'
|
||||||
import SidebarMain from 'SidebarMain'
|
import SidebarMain from 'components/sidebarMain'
|
||||||
import Profile from 'Profile'
|
import Profile from 'components/profile'
|
||||||
import PostPage from 'PostPage'
|
import PostPage from 'components/postPage'
|
||||||
import People from 'People'
|
import People from 'components/people'
|
||||||
|
|
||||||
// - Import API
|
// - Import API
|
||||||
import CircleAPI from 'CircleAPI'
|
import CircleAPI from 'api/CircleAPI'
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as globalActions from 'globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
|
|
||||||
|
import { IHomeComponentProps } from './IHomeComponentProps'
|
||||||
|
import { IHomeComponentState } from './IHomeComponentState'
|
||||||
|
|
||||||
// - Create Home component class
|
// - Create Home component class
|
||||||
export class Home extends Component {
|
export class HomeComponent extends Component<IHomeComponentProps, IHomeComponentState> {
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
constructor(props) {
|
constructor (props: IHomeComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
// Default state
|
// Default state
|
||||||
this.state = {
|
this.state = {
|
||||||
sidebarOpen: () => _,
|
sidebarOpen: () => _,
|
||||||
sidebarStatus: true,
|
sidebarStatus: true,
|
||||||
sidebaOverlay: false
|
sidebarOverlay: false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binding function to `this`
|
// Binding function to `this`
|
||||||
@@ -57,37 +59,35 @@ export class Home extends Component {
|
|||||||
* handle close sidebar
|
* handle close sidebar
|
||||||
*/
|
*/
|
||||||
handleCloseSidebar = () => {
|
handleCloseSidebar = () => {
|
||||||
this.state.sidebarOpen(false, 'overlay')
|
this.state.sidebarOpen!(false, 'overlay')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change sidebar overlay status
|
* Change sidebar overlay status
|
||||||
* @param {boolean} status if is true, the sidebar is on overlay status
|
* @param {boolean} status if is true, the sidebar is on overlay status
|
||||||
*/
|
*/
|
||||||
sidebarOverlay = (status) => {
|
sidebarOverlay = (status: boolean) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
sidebarOverlay: status
|
sidebarOverlay: status
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass function to change sidebar status
|
* Pass the function to change sidebar status
|
||||||
* @param {boolean} open is a function callback to change sidebar status out of sidebar component
|
* @param {boolean} open is a function callback to change sidebar status out of sidebar component
|
||||||
*/
|
*/
|
||||||
sidebar = (open) => {
|
sidebar = (open: (status: boolean, source: string) => void) => {
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
sidebarOpen: open
|
sidebarOpen: open
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change sidebar status if is open or not
|
* Change sidebar status if is open or not
|
||||||
* @param {boolean} status is true, if the sidebar is open
|
* @param {boolean} status is true, if the sidebar is open
|
||||||
*/
|
*/
|
||||||
sidebarStatus = (status) => {
|
sidebarStatus = (status: boolean) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
sidebarStatus: status
|
sidebarStatus: status
|
||||||
})
|
})
|
||||||
@@ -95,60 +95,59 @@ export class Home extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Render DOM component
|
* Render DOM component
|
||||||
*
|
*
|
||||||
* @returns DOM
|
* @returns DOM
|
||||||
*
|
*
|
||||||
* @memberof Home
|
* @memberof Home
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="home">
|
<div id='home'>
|
||||||
<HomeHeader sidebar={this.state.sidebarOpen} sidebarStatus={this.state.sidebarStatus} />
|
<HomeHeader sidebar={this.state.sidebarOpen} sidebarStatus={this.state.sidebarStatus} />
|
||||||
<Sidebar overlay={this.sidebarOverlay} open={this.sidebar} status={this.sidebarStatus}>
|
<Sidebar overlay={this.sidebarOverlay} open={this.sidebar} status={this.sidebarStatus}>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<Menu style={{ color: "rgb(117, 117, 117)", width: '210px' }}>
|
<Menu style={{ color: 'rgb(117, 117, 117)', width: '210px' }}>
|
||||||
{this.state.sidebarOverlay
|
{this.state.sidebarOverlay
|
||||||
? <div><MenuItem onClick={this.handleCloseSidebar} primaryText={<span style={{ color: "rgb(117, 117, 117)" }} className="sidebar__title">Green</span>} rightIcon={<SvgArrowLeft viewBox="0 3 24 24" style={{ color: "#fff", marginLeft: "15px", width: "32px", height: "32px", cursor: "pointer" }} />} /><Divider /></div>
|
? <div><MenuItem onClick={this.handleCloseSidebar} primaryText={<span style={{ color: 'rgb(117, 117, 117)' }} className='sidebar__title'>Green</span>} rightIcon={<SvgArrowLeft viewBox='0 3 24 24' style={{ color: '#fff', marginLeft: '15px', width: '32px', height: '32px', cursor: 'pointer' }} />} /><Divider /></div>
|
||||||
: ""
|
: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
<NavLink to='/'><MenuItem primaryText="Home" style={{ color: "rgb(117, 117, 117)" }} leftIcon={<SvgHome />} /></NavLink>
|
<NavLink to='/'><MenuItem primaryText='Home' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgHome />} /></NavLink>
|
||||||
<NavLink to={`/${this.props.uid}`}><MenuItem primaryText="Profile" style={{ color: "rgb(117, 117, 117)" }} leftIcon={<SvgAccountCircle />} /></NavLink>
|
<NavLink to={`/${this.props.uid}`}><MenuItem primaryText='Profile' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgAccountCircle />} /></NavLink>
|
||||||
<NavLink to='/people'><MenuItem primaryText="People" style={{ color: "rgb(117, 117, 117)" }} leftIcon={<SvgPeople />} /></NavLink>
|
<NavLink to='/people'><MenuItem primaryText='People' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgPeople />} /></NavLink>
|
||||||
<Divider />
|
<Divider />
|
||||||
<NavLink to='/settings'><MenuItem primaryText="Settings" style={{ color: "rgb(117, 117, 117)" }} leftIcon={<SvgSettings />} /></NavLink>
|
<NavLink to='/settings'><MenuItem primaryText='Settings' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgSettings />} /></NavLink>
|
||||||
<NavLink to='#'><MenuItem primaryText="Send feedback" style={{ color: "rgb(117, 117, 117)" }} leftIcon={<SvgFeedback />} /></NavLink>
|
<NavLink to='#'><MenuItem primaryText='Send feedback' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgFeedback />} /></NavLink>
|
||||||
</Menu>
|
</Menu>
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
|
|
||||||
<SidebarMain>
|
<SidebarMain>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/people/:tab?" render={() => {
|
<Route path='/people/:tab?' render={() => {
|
||||||
return (
|
return (
|
||||||
this.props.authed
|
this.props.authed
|
||||||
? <People />
|
? <People />
|
||||||
: <Redirect to="/login" />
|
: <Redirect to='/login' />
|
||||||
)
|
)
|
||||||
}} />
|
}} />
|
||||||
<Route path="/tag/:tag" render={({match}) => {
|
<Route path='/tag/:tag' render={({match}) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this.props.authed
|
this.props.authed
|
||||||
? <div className="blog"><Blog displayWriting={false} homeTitle={`#${match.params.tag}`} posts={this.props.mergedPosts} /></div>
|
? <div className='blog'><StreamComponent displayWriting={false} homeTitle={`#${match.params.tag}`} posts={this.props.mergedPosts} /></div>
|
||||||
: <Redirect to="/login" />
|
: <Redirect to='/login' />
|
||||||
)
|
)
|
||||||
}} />
|
}} />
|
||||||
<Route path="/:userId/posts/:postId/:tag?" component={PostPage} />
|
<Route path='/:userId/posts/:postId/:tag?' component={PostPage} />
|
||||||
<Route path="/:userId" component={Profile} />
|
<Route path='/:userId' component={Profile} />
|
||||||
|
|
||||||
<Route path="/" render={() => {
|
<Route path='/' render={() => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this.props.authed
|
this.props.authed
|
||||||
? <div className="blog"><Blog homeTitle='Home' posts={this.props.mergedPosts} displayWriting={true} /></div>
|
? <div className='blog'><StreamComponent homeTitle='Home' posts={this.props.mergedPosts} displayWriting={true} /></div>
|
||||||
: <Redirect to="/login" />
|
: <Redirect to='/login' />
|
||||||
)
|
)
|
||||||
}} />
|
}} />
|
||||||
</Switch>
|
</Switch>
|
||||||
@@ -167,10 +166,9 @@ export class Home extends Component {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
|
||||||
return {
|
return {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,15 +178,15 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
|
||||||
const { uid } = state.authorize
|
const { uid } = state.authorize
|
||||||
let mergedPosts = {}
|
let mergedPosts = {}
|
||||||
const circles = state.circle ? (state.circle.userCircles[uid] || {}) : {}
|
const circles = state.circle ? (state.circle.userCircles[uid] || {}) : {}
|
||||||
const followingUsers = CircleAPI.getFollowingUsers(circles)
|
const followingUsers = CircleAPI.getFollowingUsers(circles)
|
||||||
const posts = state.post.userPosts ? state.post.userPosts[state.authorize.uid] : {}
|
const posts = state.post.userPosts ? state.post.userPosts[state.authorize.uid] : {}
|
||||||
Object.keys(followingUsers).forEach((userId)=>{
|
Object.keys(followingUsers).forEach((userId) => {
|
||||||
let newPosts = state.post.userPosts ? state.post.userPosts[userId] : {}
|
let newPosts = state.post.userPosts ? state.post.userPosts[userId] : {}
|
||||||
_.merge(mergedPosts,newPosts)
|
_.merge(mergedPosts,newPosts)
|
||||||
})
|
})
|
||||||
_.merge(mergedPosts,posts)
|
_.merge(mergedPosts,posts)
|
||||||
return {
|
return {
|
||||||
@@ -199,4 +197,4 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Home))
|
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(HomeComponent as any))
|
||||||
28
src/components/home/IHomeComponentProps.ts
Normal file
28
src/components/home/IHomeComponentProps.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Post } from 'core/domain/posts'
|
||||||
|
|
||||||
|
export interface IHomeComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current user is authenticated {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IHomeComponentProps
|
||||||
|
*/
|
||||||
|
authed?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User identifier
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IHomeComponentProps
|
||||||
|
*/
|
||||||
|
uid: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merged all users posts to show in stream
|
||||||
|
*
|
||||||
|
* @type {{[postId: string]: Post}}
|
||||||
|
* @memberof IHomeComponentProps
|
||||||
|
*/
|
||||||
|
mergedPosts?: {[postId: string]: Post}
|
||||||
|
}
|
||||||
27
src/components/home/IHomeComponentState.ts
Normal file
27
src/components/home/IHomeComponentState.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
export interface IHomeComponentState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change sidebar status to {open(status:true)/close(status:false)}
|
||||||
|
*
|
||||||
|
* @type {(status: boolean, state: string)}
|
||||||
|
* @memberof IHomeComponentState
|
||||||
|
*/
|
||||||
|
sidebarOpen: (status: boolean, source: string) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sidebar status
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IHomeComponentState
|
||||||
|
*/
|
||||||
|
sidebarStatus: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sidebar overlay status
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IHomeComponentState
|
||||||
|
*/
|
||||||
|
sidebarOverlay: boolean
|
||||||
|
}
|
||||||
2
src/components/home/index.ts
Normal file
2
src/components/home/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import HomeComponent from './HomeComponent'
|
||||||
|
export default HomeComponent
|
||||||
@@ -14,24 +14,43 @@ import Paper from 'material-ui/Paper'
|
|||||||
import NotificationsIcon from 'material-ui/svg-icons/social/notifications'
|
import NotificationsIcon from 'material-ui/svg-icons/social/notifications'
|
||||||
import EventListener, { withOptions } from 'react-event-listener'
|
import EventListener, { withOptions } from 'react-event-listener'
|
||||||
|
|
||||||
|
|
||||||
// - Import components
|
// - Import components
|
||||||
import UserAvatar from 'UserAvatar'
|
import UserAvatarComponent from 'components/userAvatar'
|
||||||
import Notify from 'Notify'
|
import Notify from 'components/notify'
|
||||||
|
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as globalActions from 'globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
import * as authorizeActions from 'authorizeActions'
|
import { authorizeActions } from 'actions'
|
||||||
|
import { IHomeHeaderComponentProps } from './IHomeHeaderComponentProps'
|
||||||
|
import { IHomeHeaderComponentState } from './IHomeHeaderComponentState'
|
||||||
|
|
||||||
// - Create HomeHeader component class
|
// - Create HomeHeader component class
|
||||||
export class HomeHeader extends Component {
|
export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps,IHomeHeaderComponentState> {
|
||||||
|
|
||||||
|
styles = {
|
||||||
|
toolbarStyle: {
|
||||||
|
backgroundColor: '',
|
||||||
|
transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
fontFamily: 'Roboto, sans-serif',
|
||||||
|
position: 'fixed',
|
||||||
|
zIndex: '1101',
|
||||||
|
width: '100%',
|
||||||
|
top: '0px',
|
||||||
|
boxShadow: '0 1px 8px rgba(0,0,0,.3)'
|
||||||
|
},
|
||||||
|
avatarStyle: {
|
||||||
|
margin: 5,
|
||||||
|
cursor: 'pointer'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor (props: IHomeHeaderComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
// Default state
|
// Default state
|
||||||
@@ -56,12 +75,10 @@ export class HomeHeader extends Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle close notification menu
|
* Handle close notification menu
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @memberof HomeHeader
|
* @memberof HomeHeader
|
||||||
*/
|
*/
|
||||||
handleCloseNotify = () => {
|
handleCloseNotify = () => {
|
||||||
@@ -70,78 +87,80 @@ export class HomeHeader extends Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// On click toggle sidebar
|
// On click toggle sidebar
|
||||||
onToggleSidebar = () => {
|
onToggleSidebar = () => {
|
||||||
if (this.props.sidebarStatus) {
|
if (this.props.sidebarStatus) {
|
||||||
this.props.sidebar(false)
|
this.props.sidebar!(false,'onToggle')
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.props.sidebar(true)
|
this.props.sidebar!(true,'onToggle')
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle notification touch
|
* Handle notification touch
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @memberof HomeHeader
|
* @memberof HomeHeader
|
||||||
*/
|
*/
|
||||||
handleNotifyTouchTap = (event) => {
|
handleNotifyTouchTap = (event: any) => {
|
||||||
// This prevents ghost click.
|
// This prevents ghost click.
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
openNotifyMenu: true,
|
openNotifyMenu: true,
|
||||||
anchorEl: event.currentTarget,
|
anchorEl: event.currentTarget
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle touch on user avatar for popover
|
* Handle touch on user avatar for popover
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @memberof HomeHeader
|
* @memberof HomeHeader
|
||||||
*/
|
*/
|
||||||
handleAvatarTouchTap = (event) => {
|
handleAvatarTouchTap = (event: any) => {
|
||||||
// This prevents ghost click.
|
// This prevents ghost click.
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
openAvatarMenu: true,
|
openAvatarMenu: true,
|
||||||
anchorEl: event.currentTarget,
|
anchorEl: event.currentTarget
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle logout user
|
* Handle logout user
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @memberof HomeHeader
|
* @memberof HomeHeader
|
||||||
*/
|
*/
|
||||||
handleLogout = () => {
|
handleLogout = () => {
|
||||||
this.props.logout()
|
this.props.logout!()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle close popover
|
* Handle close popover
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @memberof HomeHeader
|
* @memberof HomeHeader
|
||||||
*/
|
*/
|
||||||
handleRequestClose = () => {
|
handleRequestClose = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
openAvatarMenu: false,
|
openAvatarMenu: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleKeyUp = () => {
|
||||||
|
// TODO: Handle key up on press ESC to close menu
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle resize event for window to manipulate home header status
|
* Handle resize event for window to manipulate home header status
|
||||||
* @param {event} evt is the event is passed by winodw resize event
|
* @param {event} evt is the event is passed by winodw resize event
|
||||||
*/
|
*/
|
||||||
handleResize = (evt) => {
|
handleResize = (event: any) => {
|
||||||
|
|
||||||
// Set initial state
|
// Set initial state
|
||||||
let width = window.innerWidth
|
let width = window.innerWidth
|
||||||
@@ -151,8 +170,7 @@ export class HomeHeader extends Component {
|
|||||||
showTitle: true
|
showTitle: true
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
} else if (width < 600 && this.state.showTitle) {
|
||||||
else if (width < 600 && this.state.showTitle) {
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
showTitle: false
|
showTitle: false
|
||||||
@@ -160,54 +178,30 @@ export class HomeHeader extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount = () => {
|
componentDidMount () {
|
||||||
this.handleResize()
|
this.handleResize(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Render app DOM component
|
// Render app DOM component
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
/**
|
|
||||||
* Styles
|
|
||||||
*/
|
|
||||||
let styles = {
|
|
||||||
toolbarStyle: {
|
|
||||||
backgroundColor: "",
|
|
||||||
transition: "all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms",
|
|
||||||
boxSizing: "border-box",
|
|
||||||
fontFamily: "Roboto, sans-serif",
|
|
||||||
position: "fixed",
|
|
||||||
zIndex: "1101",
|
|
||||||
width: "100%",
|
|
||||||
top: "0px",
|
|
||||||
boxShadow: '0 1px 8px rgba(0,0,0,.3)'
|
|
||||||
},
|
|
||||||
avatarStyle: {
|
|
||||||
margin: 5,
|
|
||||||
cursor: 'pointer'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<Toolbar style={styles.toolbarStyle} className="g__greenBox">
|
<Toolbar style={this.styles.toolbarStyle as any} className='g__greenBox'>
|
||||||
<EventListener
|
<EventListener
|
||||||
target="window"
|
target='window'
|
||||||
onResize={this.handleResize}
|
onResize={this.handleResize}
|
||||||
onKeyUp={this.handleKeyUp}
|
onKeyUp={this.handleKeyUp}
|
||||||
/>
|
/>
|
||||||
{/* Left side */}
|
{/* Left side */}
|
||||||
<ToolbarGroup firstChild={true}>
|
<ToolbarGroup firstChild={true}>
|
||||||
|
|
||||||
<IconButton iconStyle={{ color: "#fff" }} onClick={this.onToggleSidebar} >
|
<IconButton iconStyle={{ color: '#fff' }} onClick={this.onToggleSidebar} >
|
||||||
<SvgDehaze style={{ color: "#fff", marginLeft: "15px", cursor: "pointer" }} />
|
<SvgDehaze style={{ color: '#fff', marginLeft: '15px', cursor: 'pointer' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{/* Header title */}
|
{/* Header title */}
|
||||||
<ToolbarTitle style={{ color: "#fff", marginLeft: "15px" }} text="Green" />
|
<ToolbarTitle style={{ color: '#fff', marginLeft: '15px' }} text='Green' />
|
||||||
{this.state.showTitle ? <div className="homeHeader__page">{this.props.title}</div> : ''}
|
{this.state.showTitle ? <div className='homeHeader__page'>{this.props.title}</div> : ''}
|
||||||
</ToolbarGroup>
|
</ToolbarGroup>
|
||||||
<ToolbarGroup>
|
<ToolbarGroup>
|
||||||
|
|
||||||
@@ -215,26 +209,25 @@ export class HomeHeader extends Component {
|
|||||||
|
|
||||||
{/* Notification */}
|
{/* Notification */}
|
||||||
<ToolbarGroup lastChild={true}>
|
<ToolbarGroup lastChild={true}>
|
||||||
<div className="homeHeader__right">
|
<div className='homeHeader__right'>
|
||||||
{this.props.notifyCount > 0 ? (<IconButton tooltip="Notifications" onTouchTap={this.handleNotifyTouchTap}>
|
{this.props.notifyCount! > 0 ? (<IconButton tooltip='Notifications' onTouchTap={this.handleNotifyTouchTap}>
|
||||||
<div className="homeHeader__notify">
|
<div className='homeHeader__notify'>
|
||||||
<div className='title'>{this.props.notifyCount}</div>
|
<div className='title'>{this.props.notifyCount}</div>
|
||||||
</div>
|
</div>
|
||||||
</IconButton>)
|
</IconButton>)
|
||||||
|
|
||||||
: (<IconButton tooltip="Notifications" onTouchTap={this.handleNotifyTouchTap}>
|
: (<IconButton tooltip='Notifications' onTouchTap={this.handleNotifyTouchTap}>
|
||||||
<NotificationsIcon color='rgba(255, 255, 255, 0.87)' />
|
<NotificationsIcon color='rgba(255, 255, 255, 0.87)' />
|
||||||
</IconButton>)}
|
</IconButton>)}
|
||||||
<Notify open={this.state.openNotifyMenu} anchorEl={this.state.anchorEl} onRequestClose={this.handleCloseNotify}/>
|
<Notify open={this.state.openNotifyMenu} anchorEl={this.state.anchorEl} onRequestClose={this.handleCloseNotify}/>
|
||||||
|
|
||||||
|
|
||||||
{/* User avatar*/}
|
{/* User avatar*/}
|
||||||
<UserAvatar
|
<UserAvatarComponent
|
||||||
onTouchTap={this.handleAvatarTouchTap}
|
onTouchTap={this.handleAvatarTouchTap}
|
||||||
fullName={this.props.fullName}
|
fullName={this.props.fullName!}
|
||||||
fileName={this.props.avatar}
|
fileName={this.props.avatar!}
|
||||||
size={32}
|
size={32}
|
||||||
style={styles.avatarStyle}
|
style={this.styles.avatarStyle}
|
||||||
/>
|
/>
|
||||||
<Popover
|
<Popover
|
||||||
open={this.state.openAvatarMenu}
|
open={this.state.openAvatarMenu}
|
||||||
@@ -244,8 +237,8 @@ export class HomeHeader extends Component {
|
|||||||
onRequestClose={this.handleRequestClose}
|
onRequestClose={this.handleRequestClose}
|
||||||
>
|
>
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuItem style={{ backgroundColor: 'white', color: blue500, fontSize: '14px' }} primaryText="MY ACCOUNT" />
|
<MenuItem style={{ backgroundColor: 'white', color: blue500, fontSize: '14px' }} primaryText='MY ACCOUNT' />
|
||||||
<MenuItem primaryText="LOGOUT" style={{ fontSize: '14px' }} onClick={this.handleLogout.bind(this)} />
|
<MenuItem primaryText='LOGOUT' style={{ fontSize: '14px' }} onClick={this.handleLogout.bind(this)} />
|
||||||
|
|
||||||
</Menu>
|
</Menu>
|
||||||
</Popover>
|
</Popover>
|
||||||
@@ -254,26 +247,24 @@ export class HomeHeader extends Component {
|
|||||||
|
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Map dispatch to props
|
// - Map dispatch to props
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: Function, ownProps: IHomeHeaderComponentProps) => {
|
||||||
return {
|
return {
|
||||||
logout: () => dispatch(authorizeActions.dbLogout())
|
logout: () => dispatch(authorizeActions.dbLogout())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Map state to props
|
// - Map state to props
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state: any, ownProps: IHomeHeaderComponentProps) => {
|
||||||
|
|
||||||
let notifyCount = state.notify.userNotifies
|
let notifyCount = state.notify.userNotifies
|
||||||
? Object
|
? Object
|
||||||
.keys(state.notify.userNotifies)
|
.keys(state.notify.userNotifies)
|
||||||
.filter((key)=> !state.notify.userNotifies[key].isSeen).length
|
.filter((key) => !state.notify.userNotifies[key].isSeen).length
|
||||||
: 0
|
: 0
|
||||||
return {
|
return {
|
||||||
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '',
|
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '',
|
||||||
@@ -284,4 +275,4 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(HomeHeader)
|
export default connect(mapStateToProps, mapDispatchToProps)(HomeHeaderComponent as any)
|
||||||
63
src/components/homeHeader/IHomeHeaderComponentProps.ts
Normal file
63
src/components/homeHeader/IHomeHeaderComponentProps.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import StringAPI from 'api/StringAPI'
|
||||||
|
export interface IHomeHeaderComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sidebar is open {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IHomeHeaderComponentProps
|
||||||
|
*/
|
||||||
|
sidebarStatus?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout user
|
||||||
|
*
|
||||||
|
* @memberof IHomeHeaderComponentProps
|
||||||
|
*/
|
||||||
|
logout?: () => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle on resize window event
|
||||||
|
*
|
||||||
|
* @memberof IHomeHeaderComponentProps
|
||||||
|
*/
|
||||||
|
handleResize?: (event: any) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of notifications
|
||||||
|
*
|
||||||
|
* @memberof IHomeHeaderComponentProps
|
||||||
|
*/
|
||||||
|
notifyCount?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User full name
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IHomeHeaderComponentProps
|
||||||
|
*/
|
||||||
|
fullName?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's avatar URL address
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IHomeHeaderComponentProps
|
||||||
|
*/
|
||||||
|
avatar?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Top bar title
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IHomeHeaderComponentProps
|
||||||
|
*/
|
||||||
|
title?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle sidebar
|
||||||
|
*
|
||||||
|
* @memberof IHomeHeaderComponentProps
|
||||||
|
*/
|
||||||
|
sidebar?: (status: boolean, source: string) => void
|
||||||
|
}
|
||||||
35
src/components/homeHeader/IHomeHeaderComponentState.ts
Normal file
35
src/components/homeHeader/IHomeHeaderComponentState.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
export interface IHomeHeaderComponentState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popover menu on avatar is open {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IHomeHeaderComponentState
|
||||||
|
*/
|
||||||
|
openAvatarMenu: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show top bar title {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IHomeHeaderComponentState
|
||||||
|
*/
|
||||||
|
showTitle: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification menu is open {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IHomeHeaderComponentState
|
||||||
|
*/
|
||||||
|
openNotifyMenu: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the DOM element that will be used to set the position of the popover.
|
||||||
|
*
|
||||||
|
* @type {*}
|
||||||
|
* @memberof IHomeHeaderComponentState
|
||||||
|
*/
|
||||||
|
anchorEl?: HTMLElement
|
||||||
|
}
|
||||||
2
src/components/homeHeader/index.ts
Normal file
2
src/components/homeHeader/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import HomeHeaderComponent from './HomeHeaderComponent'
|
||||||
|
export default HomeHeaderComponent
|
||||||
48
src/components/imageGallery/IImageGalleryComponentProps.ts
Normal file
48
src/components/imageGallery/IImageGalleryComponentProps.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Image } from 'core/domain/imageGallery'
|
||||||
|
|
||||||
|
export interface IImageGalleryComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select image from image gallery
|
||||||
|
*
|
||||||
|
* @type {(URL: string,fullPath: string)}
|
||||||
|
* @memberof IImageGalleryComponentProps
|
||||||
|
*/
|
||||||
|
set?: (URL: string,fullPath: string) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an image
|
||||||
|
*
|
||||||
|
* @memberof IImageGalleryComponentProps
|
||||||
|
*/
|
||||||
|
deleteImage?: (imageId: string) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save image in image gallery
|
||||||
|
*
|
||||||
|
* @memberof IImageGalleryComponentProps
|
||||||
|
*/
|
||||||
|
saveImageGallery?: (URL: string,fullPath: string) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change progress state
|
||||||
|
*
|
||||||
|
* @memberof IImageGalleryComponentProps
|
||||||
|
*/
|
||||||
|
progressChange?: (percentage: number, status: boolean) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close image gallery
|
||||||
|
*
|
||||||
|
* @memberof IImageGalleryComponentProps
|
||||||
|
*/
|
||||||
|
close?: () => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of image in image gallery
|
||||||
|
*
|
||||||
|
* @type {Image[]}
|
||||||
|
* @memberof IImageGalleryComponentProps
|
||||||
|
*/
|
||||||
|
images?: Image[]
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export interface IImageGalleryComponentState {
|
||||||
|
|
||||||
|
}
|
||||||
256
src/components/imageGallery/ImageGalleryComponent.tsx
Normal file
256
src/components/imageGallery/ImageGalleryComponent.tsx
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
// - Impoer react components
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { GridList, GridTile } from 'material-ui/GridList'
|
||||||
|
import IconButton from 'material-ui/IconButton'
|
||||||
|
import Subheader from 'material-ui/Subheader'
|
||||||
|
import StarBorder from 'material-ui/svg-icons/toggle/star-border'
|
||||||
|
import FloatingActionButton from 'material-ui/FloatingActionButton'
|
||||||
|
import SvgUpload from 'material-ui/svg-icons/file/cloud-upload'
|
||||||
|
import SvgAddImage from 'material-ui/svg-icons/image/add-a-photo'
|
||||||
|
import SvgDelete from 'material-ui/svg-icons/action/delete'
|
||||||
|
import { grey200, grey600 } from 'material-ui/styles/colors'
|
||||||
|
import FlatButton from 'material-ui/FlatButton'
|
||||||
|
import uuid from 'uuid'
|
||||||
|
|
||||||
|
// - Import actions
|
||||||
|
import * as imageGalleryActions from 'actions/imageGalleryActions'
|
||||||
|
import * as globalActions from 'actions/globalActions'
|
||||||
|
|
||||||
|
// - Import app components
|
||||||
|
import Img from 'components/img'
|
||||||
|
|
||||||
|
// - Import API
|
||||||
|
import FileAPI from 'api/FileAPI'
|
||||||
|
import { IImageGalleryComponentProps } from './IImageGalleryComponentProps'
|
||||||
|
import { IImageGalleryComponentState } from './IImageGalleryComponentState'
|
||||||
|
import { Image } from 'core/domain/imageGallery'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create ImageGallery component class
|
||||||
|
*/
|
||||||
|
export class ImageGalleryComponent extends Component<IImageGalleryComponentProps, IImageGalleryComponentState> {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to ser image url on parent component
|
||||||
|
*/
|
||||||
|
open: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
styles = {
|
||||||
|
root: {
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
justifyContent: 'space-around'
|
||||||
|
},
|
||||||
|
gridList: {
|
||||||
|
width: 500,
|
||||||
|
height: 450,
|
||||||
|
overflowY: 'auto'
|
||||||
|
},
|
||||||
|
uploadButton: {
|
||||||
|
verticalAlign: 'middle'
|
||||||
|
},
|
||||||
|
uploadInput: {
|
||||||
|
cursor: 'pointer',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
opacity: 0
|
||||||
|
},
|
||||||
|
deleteImage: {
|
||||||
|
marginLeft: '5px',
|
||||||
|
cursor: 'pointer'
|
||||||
|
},
|
||||||
|
addImage: {
|
||||||
|
marginRight: '5px',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component constructor
|
||||||
|
* @param {object} props is an object properties of component
|
||||||
|
*/
|
||||||
|
constructor (props: IImageGalleryComponentProps) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
// Binding function to `this`
|
||||||
|
this.close = this.close.bind(this)
|
||||||
|
this.onFileChange = this.onFileChange.bind(this)
|
||||||
|
this.handleSetImage = this.handleSetImage.bind(this)
|
||||||
|
this.handleDeleteImage = this.handleDeleteImage.bind(this)
|
||||||
|
this.imageList = this.imageList.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle set image
|
||||||
|
* @param {event} evt passed by on click event on add image
|
||||||
|
* @param {string} name is the name of the image
|
||||||
|
*/
|
||||||
|
handleSetImage = (event: any, URL: string,fullPath: string) => {
|
||||||
|
this.props.set!(URL,fullPath)
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle delete image
|
||||||
|
* @param {event} evt passed by on click event on delete image
|
||||||
|
* @param {integer} id is the image identifier which selected to delete
|
||||||
|
*/
|
||||||
|
handleDeleteImage = (event: any, id: string) => {
|
||||||
|
this.props.deleteImage!(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
window.addEventListener('onSendResizedImage', this.handleSendResizedImage)
|
||||||
|
}
|
||||||
|
componentWillUnmount () {
|
||||||
|
window.removeEventListener('onSendResizedImage', this.handleSendResizedImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle send image resize event that pass the resized image
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @memberof ImageGallery
|
||||||
|
*/
|
||||||
|
handleSendResizedImage = (event: any) => {
|
||||||
|
|
||||||
|
const { resizedImage, fileName } = event.detail
|
||||||
|
const {saveImageGallery, progressChange} = this.props
|
||||||
|
|
||||||
|
FileAPI.uploadImage(resizedImage, fileName, (percent: number, status: boolean) => {
|
||||||
|
progressChange!(percent,status)
|
||||||
|
}).then((result) => {
|
||||||
|
|
||||||
|
/* Add image to image gallery */
|
||||||
|
saveImageGallery!(result.downloadURL,result.metadata.fullPath)
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle on change file upload
|
||||||
|
*/
|
||||||
|
onFileChange = (event: any) => {
|
||||||
|
|
||||||
|
const extension = FileAPI.getExtension(event.target.files[0].name)
|
||||||
|
let fileName = (`${uuid()}.${extension}`)
|
||||||
|
let image = FileAPI.constraintImage(event.target.files[0], fileName)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide image gallery
|
||||||
|
*/
|
||||||
|
close = () => {
|
||||||
|
this.props.close!()
|
||||||
|
}
|
||||||
|
|
||||||
|
imageList = () => {
|
||||||
|
|
||||||
|
return this.props.images!.map((image: Image, index) => {
|
||||||
|
|
||||||
|
return (<GridTile
|
||||||
|
key={image.id!}
|
||||||
|
title={<SvgDelete hoverColor={grey200} color='white' style={this.styles.deleteImage as any} onClick={evt => this.handleDeleteImage(evt, image.id!)} />}
|
||||||
|
subtitle={<span></span>}
|
||||||
|
actionIcon={<SvgAddImage hoverColor={grey200} color='white' style={this.styles.addImage as any} onClick={evt => this.handleSetImage(evt, image.URL,image.fullPath)} />}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div style={{ overflowY: 'hidden', overflowX: 'auto' }}>
|
||||||
|
<ul style={{ whiteSpace: 'nowrap', padding: '0 6px', margin: '8px 0 0 0', verticalAlign: 'bottom', flexShrink: 0, listStyleType: 'none' }}>
|
||||||
|
<div style={{ display: 'block' }}>
|
||||||
|
<div style={{ display: 'block', marginRight: '8px', transition: 'transform .25s' }}>
|
||||||
|
<li style={{ width: '100%', margin: 0, verticalAlign: 'bottom', position: 'static', display: 'inline-block' }}>
|
||||||
|
<Img fileName={image.URL} style={{ width: '100%', height: 'auto' }} />
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</GridTile>)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the post text changed
|
||||||
|
* @param {event} evt is an event passed by change post text callback funciton
|
||||||
|
* @param {string} data is the post content which user writes
|
||||||
|
*/
|
||||||
|
render () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component styles
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={this.styles.root as any}>
|
||||||
|
<GridList
|
||||||
|
cellHeight={180}
|
||||||
|
style={this.styles.gridList as any}
|
||||||
|
>
|
||||||
|
<GridTile >
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', backgroundColor: 'rgba(222, 222, 222, 0.52)', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', height: '100%' }}>
|
||||||
|
|
||||||
|
<FlatButton
|
||||||
|
label='Upload Photo'
|
||||||
|
labelStyle={{ fontWeight: 100 }}
|
||||||
|
labelPosition='before'
|
||||||
|
style={this.styles.uploadButton}
|
||||||
|
containerElement='label'
|
||||||
|
>
|
||||||
|
<input type='file' onChange={this.onFileChange} accept='image/*' style={this.styles.uploadInput as any} />
|
||||||
|
</FlatButton>
|
||||||
|
</div>
|
||||||
|
</GridTile>
|
||||||
|
{this.imageList()}
|
||||||
|
</GridList>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map dispatch to props
|
||||||
|
* @param {func} dispatch is the function to dispatch action to reducers
|
||||||
|
* @param {object} ownProps is the props belong to component
|
||||||
|
* @return {object} props of component
|
||||||
|
*/
|
||||||
|
const mapDispatchToProps = (dispatch: any, ownProps: IImageGalleryComponentProps) => {
|
||||||
|
return {
|
||||||
|
saveImageGallery: (imageURL: string,imageFullPath: string) => dispatch(imageGalleryActions.dbSaveImage(imageURL,imageFullPath)),
|
||||||
|
deleteImage: (id: string) => dispatch(imageGalleryActions.dbDeleteImage(id)),
|
||||||
|
progressChange : (percent: number,status: boolean) => dispatch(globalActions.progressChange(percent, status))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map state to props
|
||||||
|
* @param {object} state is the obeject from redux store
|
||||||
|
* @param {object} ownProps is the props belong to component
|
||||||
|
* @return {object} props of component
|
||||||
|
*/
|
||||||
|
const mapStateToProps = (state: any) => {
|
||||||
|
return {
|
||||||
|
images: state.imageGallery.images,
|
||||||
|
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : ''
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Connect component to redux store
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ImageGalleryComponent as any)
|
||||||
2
src/components/imageGallery/index.ts
Normal file
2
src/components/imageGallery/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import ImageGalleryComponent from './ImageGalleryComponent'
|
||||||
|
export default ImageGalleryComponent
|
||||||
19
src/components/img/IImgComponentProps.ts
Normal file
19
src/components/img/IImgComponentProps.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export interface IImgComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image file name
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IImgComponentProps
|
||||||
|
*/
|
||||||
|
fileName: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image style sheet
|
||||||
|
*
|
||||||
|
* @type {{}}
|
||||||
|
* @memberof IImgComponentProps
|
||||||
|
*/
|
||||||
|
style?: {}
|
||||||
|
|
||||||
|
}
|
||||||
11
src/components/img/IImgComponentState.ts
Normal file
11
src/components/img/IImgComponentState.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
export interface IImgComponentState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image is loaded {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IImgComponentProps
|
||||||
|
*/
|
||||||
|
isImageLoaded?: boolean
|
||||||
|
}
|
||||||
@@ -4,40 +4,51 @@ import PropTypes from 'prop-types'
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import SvgImage from 'material-ui/svg-icons/image/image'
|
import SvgImage from 'material-ui/svg-icons/image/image'
|
||||||
|
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
|
|
||||||
|
|
||||||
// - Import API
|
// - Import API
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as imageGalleryActions from 'imageGalleryActions'
|
import * as imageGalleryActions from 'actions/imageGalleryActions'
|
||||||
|
import { IImgComponentProps } from './IImgComponentProps'
|
||||||
|
import { IImgComponentState } from './IImgComponentState'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create component class
|
* Create component class
|
||||||
*/
|
*/
|
||||||
export class Img extends Component {
|
export class ImgComponent extends Component<IImgComponentProps,IImgComponentState> {
|
||||||
|
|
||||||
static propTypes = {
|
styles = {
|
||||||
|
loding: {
|
||||||
/**
|
display: 'flex',
|
||||||
* Use for getting url address from server
|
justifyContent: 'center',
|
||||||
*/
|
alignItems: 'center',
|
||||||
fileName: PropTypes.string,
|
width: '100%',
|
||||||
/**
|
height: '100px',
|
||||||
* Avatar style
|
position: 'relative',
|
||||||
*/
|
color: '#cacecd',
|
||||||
style: PropTypes.object
|
fontWeight: 100
|
||||||
|
},
|
||||||
|
loadingContent: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
loadingImage: {
|
||||||
|
fill: 'aliceblue',
|
||||||
|
width: '50px',
|
||||||
|
height: '50px'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor (props: IImgComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
//Defaul state
|
// Defaul state
|
||||||
this.state = {
|
this.state = {
|
||||||
isImageLoaded: false
|
isImageLoaded: false
|
||||||
}
|
}
|
||||||
@@ -49,7 +60,7 @@ export class Img extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Will be called on loading image
|
* Will be called on loading image
|
||||||
*
|
*
|
||||||
* @memberof Img
|
* @memberof Img
|
||||||
*/
|
*/
|
||||||
handleLoadImage = () => {
|
handleLoadImage = () => {
|
||||||
@@ -62,38 +73,16 @@ export class Img extends Component {
|
|||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
const styles = {
|
|
||||||
loding: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
width: '100%',
|
|
||||||
height: '100px',
|
|
||||||
position: 'relative',
|
|
||||||
color: '#cacecd',
|
|
||||||
fontWeight: 100
|
|
||||||
},
|
|
||||||
loadingContent: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
loadingImage: {
|
|
||||||
fill: 'aliceblue',
|
|
||||||
width: '50px',
|
|
||||||
height: '50px'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let { fileName, style } = this.props
|
let { fileName, style } = this.props
|
||||||
let { isImageLoaded } = this.state
|
let { isImageLoaded } = this.state
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<img onLoad={this.handleLoadImage} src={fileName || ''} style={isImageLoaded ? style : { display: 'none' }} />
|
<img onLoad={this.handleLoadImage} src={fileName || ''} style={isImageLoaded ? style : { display: 'none' }} />
|
||||||
<div style={{ backgroundColor: 'blue' }} style={isImageLoaded ? { display: 'none' } : styles.loding}>
|
<div style={Object.assign({},{ backgroundColor: 'white' }, isImageLoaded ? { display: 'none' } : this.styles.loding) }>
|
||||||
<div style={styles.loadingContent}>
|
<div style={this.styles.loadingContent as any}>
|
||||||
<SvgImage style={styles.loadingImage} />
|
<SvgImage style={this.styles.loadingImage} />
|
||||||
<div>Image has not loaded</div>
|
<div>Image has not loaded</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,17 +91,15 @@ export class Img extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map dispatch to props
|
* Map dispatch to props
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
* @param {func} dispatch is the function to dispatch action to reducers
|
||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: IImgComponentProps) => {
|
||||||
return {
|
return {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +109,7 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state: any, ownProps: IImgComponentProps) => {
|
||||||
return {
|
return {
|
||||||
avatarURL: state.imageGallery.imageURLList,
|
avatarURL: state.imageGallery.imageURLList,
|
||||||
imageRequests: state.imageGallery.imageRequests
|
imageRequests: state.imageGallery.imageRequests
|
||||||
@@ -130,4 +117,4 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Img)
|
export default connect(mapStateToProps, mapDispatchToProps)(ImgComponent as any)
|
||||||
2
src/components/img/index.ts
Normal file
2
src/components/img/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import ImgComponent from './ImgComponent'
|
||||||
|
export default ImgComponent
|
||||||
42
src/components/imgCover/IImgCoverComponentProps.ts
Normal file
42
src/components/imgCover/IImgCoverComponentProps.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
export interface IImgCoverComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image file name
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IImgCoverComponentProps
|
||||||
|
*/
|
||||||
|
fileName: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image style sheet
|
||||||
|
*
|
||||||
|
* @type {{}}
|
||||||
|
* @memberof IImgCoverComponentProps
|
||||||
|
*/
|
||||||
|
style?: {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image with
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IImgCoverComponentProps
|
||||||
|
*/
|
||||||
|
width?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image height
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IImgCoverComponentProps
|
||||||
|
*/
|
||||||
|
height?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image border radius
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IImgCoverComponentProps
|
||||||
|
*/
|
||||||
|
borderRadius?: string
|
||||||
|
}
|
||||||
12
src/components/imgCover/IImgCoverComponentState.ts
Normal file
12
src/components/imgCover/IImgCoverComponentState.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
export interface IImgCoverComponentState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image is loaded {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IImgCoverComponentProps
|
||||||
|
*/
|
||||||
|
isImageLoaded: boolean
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,17 +6,17 @@ import SvgImage from 'material-ui/svg-icons/image/image'
|
|||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
|
|
||||||
|
|
||||||
// - Import API
|
// - Import API
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as imageGalleryActions from 'imageGalleryActions'
|
import * as imageGalleryActions from 'actions/imageGalleryActions'
|
||||||
|
import { IImgCoverComponentProps } from './IImgCoverComponentProps'
|
||||||
|
import { IImgCoverComponentState } from './IImgCoverComponentState'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create component class
|
* Create component class
|
||||||
*/
|
*/
|
||||||
export class ImgCover extends Component {
|
export class ImgCoverComponent extends Component<IImgCoverComponentProps,IImgCoverComponentState> {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
|
||||||
@@ -47,14 +47,42 @@ export class ImgCover extends Component {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
styles = {
|
||||||
|
cover: {
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
backgroundPosition: 'center'
|
||||||
|
},
|
||||||
|
loding: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
height: '100px',
|
||||||
|
position: 'relative',
|
||||||
|
color: '#cacecd',
|
||||||
|
fontWeight: 100
|
||||||
|
},
|
||||||
|
loadingContent: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
loadingImage: {
|
||||||
|
fill: 'aliceblue',
|
||||||
|
width: '50px',
|
||||||
|
height: '50px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor (props: IImgCoverComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
//Defaul state
|
// Defaul state
|
||||||
this.state = {
|
this.state = {
|
||||||
isImageLoaded: false
|
isImageLoaded: false
|
||||||
}
|
}
|
||||||
@@ -65,8 +93,8 @@ export class ImgCover extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Will be called on loading image
|
* Will be called on loading image
|
||||||
*
|
*
|
||||||
* @memberof Img
|
* @memberof ImgCoverComponent
|
||||||
*/
|
*/
|
||||||
handleLoadImage = () => {
|
handleLoadImage = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -78,54 +106,24 @@ export class ImgCover extends Component {
|
|||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
let { fileName, style } = this.props
|
let { fileName, style } = this.props
|
||||||
let { isImageLoaded } = this.state
|
let { isImageLoaded } = this.state
|
||||||
|
|
||||||
/**
|
|
||||||
* Styles
|
|
||||||
*/
|
|
||||||
const styles = {
|
|
||||||
cover: {
|
|
||||||
backgroundImage: 'url(' + (fileName || '') + ')',
|
|
||||||
backgroundSize: 'cover',
|
|
||||||
backgroundRepeat: 'no-repeat',
|
|
||||||
backgroundPosition: 'center',
|
|
||||||
width: this.props.width,
|
|
||||||
height: this.props.height,
|
|
||||||
borderRadius: this.props.borderRadius
|
|
||||||
|
|
||||||
},
|
|
||||||
loding: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
width: '100%',
|
|
||||||
height: '100px',
|
|
||||||
position: 'relative',
|
|
||||||
color: '#cacecd',
|
|
||||||
fontWeight: 100
|
|
||||||
},
|
|
||||||
loadingContent: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
loadingImage: {
|
|
||||||
fill: 'aliceblue',
|
|
||||||
width: '50px',
|
|
||||||
height: '50px'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div style={Object.assign({},styles.cover,style)}>
|
<div style={Object.assign({},this.styles.cover,{
|
||||||
|
backgroundImage: 'url(' + (fileName || '') + ')',
|
||||||
|
width: this.props.width,
|
||||||
|
height: this.props.height,
|
||||||
|
borderRadius: this.props.borderRadius
|
||||||
|
},style)}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ backgroundColor: 'blue' }} style={isImageLoaded ? { display: 'none' } : styles.loding}>
|
<div style={Object.assign({},{ backgroundColor: 'blue' },isImageLoaded ? { display: 'none' } : this.styles.loding)}>
|
||||||
<div style={styles.loadingContent}>
|
<div style={this.styles.loadingContent as any}>
|
||||||
<SvgImage style={styles.loadingImage} />
|
<SvgImage style={this.styles.loadingImage} />
|
||||||
<div>Image has not loaded</div>
|
<div>Image has not loaded</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -135,15 +133,13 @@ export class ImgCover extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map dispatch to props
|
* Map dispatch to props
|
||||||
* @param {func} dispatch is the function to dispatch action to reducers
|
* @param {func} dispatch is the function to dispatch action to reducers
|
||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: IImgCoverComponentProps) => {
|
||||||
return {
|
return {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +150,7 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state: any, ownProps: IImgCoverComponentProps) => {
|
||||||
return {
|
return {
|
||||||
avatarURL: state.imageGallery.imageURLList,
|
avatarURL: state.imageGallery.imageURLList,
|
||||||
imageRequests: state.imageGallery.imageRequests
|
imageRequests: state.imageGallery.imageRequests
|
||||||
@@ -163,4 +159,4 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ImgCover)
|
export default connect(mapStateToProps, mapDispatchToProps)(ImgCoverComponent as any)
|
||||||
2
src/components/imgCover/index.ts
Normal file
2
src/components/imgCover/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import ImgCoverComponent from './ImgCoverComponent'
|
||||||
|
export default ImgCoverComponent
|
||||||
16
src/components/login/ILoginComponentProps.ts
Normal file
16
src/components/login/ILoginComponentProps.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export interface ILoginComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login a user
|
||||||
|
*
|
||||||
|
* @memberof ILoginComponentProps
|
||||||
|
*/
|
||||||
|
login?: (email: string , password: string) => any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to signup page
|
||||||
|
*
|
||||||
|
* @memberof ILoginComponentProps
|
||||||
|
*/
|
||||||
|
signupPage?: () => any
|
||||||
|
}
|
||||||
43
src/components/login/ILoginComponentState.ts
Normal file
43
src/components/login/ILoginComponentState.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
export interface ILoginComponentState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email input value
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ILoginComponentState
|
||||||
|
*/
|
||||||
|
emailInput: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email input error text
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ILoginComponentState
|
||||||
|
*/
|
||||||
|
emailInputError: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password input value
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ILoginComponentState
|
||||||
|
*/
|
||||||
|
passwordInput: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password input error text
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ILoginComponentState
|
||||||
|
*/
|
||||||
|
passwordInputError: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm input error text
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ILoginComponentState
|
||||||
|
*/
|
||||||
|
confirmInputError: string
|
||||||
|
}
|
||||||
@@ -9,17 +9,18 @@ import RaisedButton from 'material-ui/RaisedButton'
|
|||||||
import FlatButton from 'material-ui/FlatButton'
|
import FlatButton from 'material-ui/FlatButton'
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as authorizeActions from 'authorizeActions'
|
import * as authorizeActions from 'actions/authorizeActions'
|
||||||
|
import { ILoginComponentProps } from './ILoginComponentProps'
|
||||||
|
import { ILoginComponentState } from './ILoginComponentState'
|
||||||
|
|
||||||
// - Create Login component class
|
// - Create Login component class
|
||||||
export class Login extends Component {
|
export class LoginComponent extends Component<ILoginComponentProps,ILoginComponentState> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor (props: ILoginComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -27,8 +28,7 @@ export class Login extends Component {
|
|||||||
emailInputError: '',
|
emailInputError: '',
|
||||||
passwordInput: '',
|
passwordInput: '',
|
||||||
passwordInputError: '',
|
passwordInputError: '',
|
||||||
|
confirmInputError: ''
|
||||||
|
|
||||||
}
|
}
|
||||||
// Binding function to `this`
|
// Binding function to `this`
|
||||||
this.handleForm = this.handleForm.bind(this)
|
this.handleForm = this.handleForm.bind(this)
|
||||||
@@ -39,15 +39,14 @@ export class Login extends Component {
|
|||||||
* Handle data on input change
|
* Handle data on input change
|
||||||
* @param {event} evt is an event of inputs of element on change
|
* @param {event} evt is an event of inputs of element on change
|
||||||
*/
|
*/
|
||||||
handleInputChange = (evt) => {
|
handleInputChange = (event: any) => {
|
||||||
const target = evt.target
|
const target = event.target
|
||||||
const value = target.type === 'checkbox' ? target.checked : target.value
|
const value = target.type === 'checkbox' ? target.checked : target.value
|
||||||
const name = target.name
|
const name = target.name
|
||||||
this.setState({
|
this.setState({
|
||||||
[name]: value
|
[name]: value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'emailInput':
|
case 'emailInput':
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -88,7 +87,7 @@ export class Login extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
this.props.login(
|
this.props.login!(
|
||||||
this.state.emailInput,
|
this.state.emailInput,
|
||||||
this.state.passwordInput
|
this.state.passwordInput
|
||||||
)
|
)
|
||||||
@@ -96,48 +95,46 @@ export class Login extends Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reneder component DOM
|
* Reneder component DOM
|
||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render() {
|
render () {
|
||||||
|
|
||||||
const paperStyle = {
|
const paperStyle = {
|
||||||
minHeight: 370,
|
minHeight: 370,
|
||||||
width: 450,
|
width: 450,
|
||||||
margin: 20,
|
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
margin: "auto"
|
margin: 'auto'
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<form>
|
<form>
|
||||||
|
|
||||||
<h1 style={{
|
<h1 style={{
|
||||||
textAlign: "center",
|
textAlign: 'center',
|
||||||
padding: "20px",
|
padding: '20px',
|
||||||
fontSize: "30px",
|
fontSize: '30px',
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
lineHeight: "32px",
|
lineHeight: '32px',
|
||||||
margin: "auto",
|
margin: 'auto',
|
||||||
color: "rgba(138, 148, 138, 0.2)"
|
color: 'rgba(138, 148, 138, 0.2)'
|
||||||
}}>Green</h1>
|
}}>Green</h1>
|
||||||
|
|
||||||
<div className="animate-bottom">
|
<div className='animate-bottom'>
|
||||||
<Paper style={paperStyle} zDepth={1} rounded={false} >
|
<Paper style={paperStyle} zDepth={1} rounded={false} >
|
||||||
<div style={{ padding: "48px 40px 36px" }}>
|
<div style={{ padding: '48px 40px 36px' }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
paddingLeft: "40px",
|
paddingLeft: '40px',
|
||||||
paddingRight: "40px"
|
paddingRight: '40px'
|
||||||
}}>
|
}}>
|
||||||
|
|
||||||
<h2 style={{
|
<h2 style={{
|
||||||
textAlign: "left",
|
textAlign: 'left',
|
||||||
paddingTop: "16px",
|
paddingTop: '16px',
|
||||||
fontSize: "24px",
|
fontSize: '24px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
lineHeight: "32px",
|
lineHeight: '32px',
|
||||||
margin: 0
|
margin: 0
|
||||||
}}>Sign in</h2>
|
}}>Sign in</h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -145,29 +142,29 @@ export class Login extends Component {
|
|||||||
<TextField
|
<TextField
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
errorText={this.state.emailInputError}
|
errorText={this.state.emailInputError}
|
||||||
name="emailInput"
|
name='emailInput'
|
||||||
floatingLabelStyle={{ fontSize: "15px" }}
|
floatingLabelStyle={{ fontSize: '15px' }}
|
||||||
floatingLabelText="Email"
|
floatingLabelText='Email'
|
||||||
type="email"
|
type='email'
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
/><br />
|
/><br />
|
||||||
<TextField
|
<TextField
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
errorText={this.state.passwordInputError}
|
errorText={this.state.passwordInputError}
|
||||||
name="passwordInput"
|
name='passwordInput'
|
||||||
floatingLabelStyle={{ fontSize: "15px" }}
|
floatingLabelStyle={{ fontSize: '15px' }}
|
||||||
floatingLabelText="Password"
|
floatingLabelText='Password'
|
||||||
type="password"
|
type='password'
|
||||||
tabIndex={2}
|
tabIndex={2}
|
||||||
/><br />
|
/><br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<div className="login__button-box">
|
<div className='login__button-box'>
|
||||||
<div>
|
<div>
|
||||||
<FlatButton label="Create an account" onClick={this.props.signupPage} tabIndex={4} />
|
<FlatButton label='Create an account' onClick={this.props.signupPage} tabIndex={4} />
|
||||||
</div>
|
</div>
|
||||||
<div >
|
<div >
|
||||||
<RaisedButton label="Login" primary={true} onClick={this.handleForm} tabIndex={3} />
|
<RaisedButton label='Login' primary={true} onClick={this.handleForm} tabIndex={3} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -185,13 +182,13 @@ export class Login extends Component {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: ILoginComponentProps) => {
|
||||||
return {
|
return {
|
||||||
login: (username, password) => {
|
login: (email: string, password: string) => {
|
||||||
dispatch(authorizeActions.dbLogin(username, password))
|
dispatch(authorizeActions.dbLogin(email, password))
|
||||||
},
|
},
|
||||||
signupPage: () => {
|
signupPage: () => {
|
||||||
dispatch(push("/signup"))
|
dispatch(push('/signup'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,11 +199,11 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
* @param {object} ownProps is the props belong to component
|
* @param {object} ownProps is the props belong to component
|
||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state: any, ownProps: ILoginComponentProps) => {
|
||||||
return {
|
return {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Connect component to redux store
|
// - Connect component to redux store
|
||||||
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Login))
|
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(LoginComponent as any))
|
||||||
2
src/components/login/index.ts
Normal file
2
src/components/login/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import LoginComponent from './LoginComponent'
|
||||||
|
export default LoginComponent
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface IMasterProps {
|
export interface IMasterComponentProps {
|
||||||
/**
|
/**
|
||||||
* Close gloal message
|
* Close gloal message
|
||||||
*
|
*
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
|
|
||||||
export interface IMasterState {
|
export interface IMasterComponentState {
|
||||||
/**
|
/**
|
||||||
* Loding will be appeared if it's true
|
* Loding will be appeared if it's true
|
||||||
*
|
*
|
||||||
* @type {Boolean}
|
* @type {boolean}
|
||||||
* @memberof IMasterState
|
* @memberof IMasterState
|
||||||
*/
|
*/
|
||||||
loading: Boolean,
|
loading: boolean,
|
||||||
/**
|
/**
|
||||||
* It's true if user is authorized
|
* It's true if user is authorized
|
||||||
*
|
*
|
||||||
* @type {Boolean}
|
* @type {boolean}
|
||||||
* @memberof IMasterState
|
* @memberof IMasterState
|
||||||
*/
|
*/
|
||||||
authed: Boolean
|
authed: boolean
|
||||||
/**
|
/**
|
||||||
* It's true if all default data loaded from database
|
* It's true if all default data loaded from database
|
||||||
*
|
*
|
||||||
* @type {Boolean}
|
* @type {boolean}
|
||||||
* @memberof IMasterState
|
* @memberof IMasterState
|
||||||
*/
|
*/
|
||||||
dataLoaded: Boolean
|
dataLoaded: boolean
|
||||||
}
|
}
|
||||||
@@ -3,22 +3,19 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { Route, Switch, NavLink, withRouter, Redirect } from 'react-router-dom'
|
import { Route, Switch, NavLink, withRouter, Redirect } from 'react-router-dom'
|
||||||
import { firebaseAuth, firebaseRef } from 'app/firebaseClient'
|
import { firebaseAuth, firebaseRef } from 'data/firebaseClient'
|
||||||
import { push } from 'react-router-redux'
|
import { push } from 'react-router-redux'
|
||||||
import Snackbar from 'material-ui/Snackbar'
|
import Snackbar from 'material-ui/Snackbar'
|
||||||
import LinearProgress from 'material-ui/LinearProgress'
|
import LinearProgress from 'material-ui/LinearProgress'
|
||||||
|
|
||||||
// - Import components
|
// - Import components
|
||||||
import Home from 'components/Home'
|
import Home from 'components/home'
|
||||||
import Signup from 'components/Signup'
|
import Signup from 'components/signup'
|
||||||
import Login from 'components/Login'
|
import Login from 'components/login'
|
||||||
import Settings from 'components/Settings'
|
import Setting from 'components/setting'
|
||||||
import MasterLoading from 'components/MasterLoading'
|
import MasterLoading from 'components/masterLoading'
|
||||||
import { IMasterProps } from './IMasterProps'
|
import { IMasterComponentProps } from './IMasterComponentProps'
|
||||||
import { IMasterState } from './IMasterState'
|
import { IMasterComponentState } from './IMasterComponentState'
|
||||||
|
|
||||||
// - Import API
|
|
||||||
import { PrivateRoute, PublicRoute } from 'api/AuthRouterAPI'
|
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import {
|
import {
|
||||||
@@ -36,11 +33,11 @@ import {
|
|||||||
/* ------------------------------------ */
|
/* ------------------------------------ */
|
||||||
|
|
||||||
// - Create Master component class
|
// - Create Master component class
|
||||||
export class Master extends Component<IMasterProps, IMasterState> {
|
export class MasterComponent extends Component<IMasterComponentProps, IMasterComponentState> {
|
||||||
|
|
||||||
static isPrivate = true
|
static isPrivate = true
|
||||||
// Constructor
|
// Constructor
|
||||||
constructor (props: IMasterProps) {
|
constructor (props: IMasterComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
@@ -68,8 +65,9 @@ export class Master extends Component<IMasterProps, IMasterState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidCatch (error: any, info: any) {
|
componentDidCatch (error: any, info: any) {
|
||||||
console.log('====================================')
|
console.log('===========Catched by React componentDidCatch==============')
|
||||||
console.log(error, info)
|
console.log(error, info)
|
||||||
|
alert({error, info})
|
||||||
console.log('====================================')
|
console.log('====================================')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +125,7 @@ export class Master extends Component<IMasterProps, IMasterState> {
|
|||||||
{(!this.state.loading && (this.props.loaded || this.props.guest))
|
{(!this.state.loading && (this.props.loaded || this.props.guest))
|
||||||
? (<Switch>
|
? (<Switch>
|
||||||
<Route path='/signup' component={Signup} />
|
<Route path='/signup' component={Signup} />
|
||||||
<Route path='/settings' component={Settings} />
|
<Route path='/settings' component={Setting} />
|
||||||
<Route path='/login' render={() => {
|
<Route path='/login' render={() => {
|
||||||
console.log('this.props.authed: ', this.props.authed, 'this.props: ', this.props)
|
console.log('this.props.authed: ', this.props.authed, 'this.props: ', this.props)
|
||||||
return (
|
return (
|
||||||
@@ -154,7 +152,7 @@ export class Master extends Component<IMasterProps, IMasterState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// - Map dispatch to props
|
// - Map dispatch to props
|
||||||
const mapDispatchToProps = (dispatch: any, ownProps: any) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: IMasterComponentProps) => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loadData: () => {
|
loadData: () => {
|
||||||
@@ -175,6 +173,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: any) => {
|
|||||||
dispatch(voteActions.clearAllvotes())
|
dispatch(voteActions.clearAllvotes())
|
||||||
dispatch(notifyActions.clearAllNotifications())
|
dispatch(notifyActions.clearAllNotifications())
|
||||||
dispatch(circleActions.clearAllCircles())
|
dispatch(circleActions.clearAllCircles())
|
||||||
|
dispatch(globalActions.clearTemp())
|
||||||
|
|
||||||
},
|
},
|
||||||
login: (user: any) => {
|
login: (user: any) => {
|
||||||
@@ -216,4 +215,4 @@ const mapStateToProps = (state: any) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
// - Connect commponent to redux store
|
// - Connect commponent to redux store
|
||||||
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Master as any))
|
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MasterComponent as any))
|
||||||
2
src/components/master/index.ts
Normal file
2
src/components/master/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import MasterComponent from './MasterComponent'
|
||||||
|
export default MasterComponent
|
||||||
12
src/components/masterLoading/IMasterLoadingComponentProps.ts
Normal file
12
src/components/masterLoading/IMasterLoadingComponentProps.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export interface IMasterLoadingComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loading is active {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IMasterLoadingComponentProps
|
||||||
|
*/
|
||||||
|
activeLoading: boolean
|
||||||
|
|
||||||
|
handleLoading: (status: boolean) => void
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export interface IMasterLoadingComponentState {
|
||||||
|
|
||||||
|
}
|
||||||
47
src/components/masterLoading/MasterLoadingComponent.tsx
Normal file
47
src/components/masterLoading/MasterLoadingComponent.tsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// - Import react components
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import CircularProgress from 'material-ui/CircularProgress'
|
||||||
|
import Dialog from 'material-ui/Dialog'
|
||||||
|
import { IMasterLoadingComponentProps } from './IMasterLoadingComponentProps'
|
||||||
|
import { IMasterLoadingComponentState } from './IMasterLoadingComponentState'
|
||||||
|
|
||||||
|
// - Import app components
|
||||||
|
|
||||||
|
// - Create MasterLoading component class
|
||||||
|
export default class MasterLoadingComponent extends Component<IMasterLoadingComponentProps,IMasterLoadingComponentState> {
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
constructor (props: IMasterLoadingComponentProps) {
|
||||||
|
super(props)
|
||||||
|
// Binding functions to `this`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render app DOM component
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
modal={true}
|
||||||
|
open={this.props.activeLoading}
|
||||||
|
autoDetectWindowHeight={false}
|
||||||
|
overlayStyle={{backgroundColor: 'white'}}
|
||||||
|
contentClassName='mLoading__content'
|
||||||
|
bodyStyle={{backgroundColor: ''}}
|
||||||
|
bodyClassName='mLoading__body'
|
||||||
|
>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className='mLoading__context'>
|
||||||
|
|
||||||
|
<CircularProgress color='white' size={80} thickness={7} />
|
||||||
|
<h1 style={{float: 'right', color: '#fff'}}>Green</h1>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
2
src/components/masterLoading/index.ts
Normal file
2
src/components/masterLoading/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import MasterLoadingComponent from './MasterLoadingComponent'
|
||||||
|
export default MasterLoadingComponent
|
||||||
45
src/components/notify/INotifyComponentProps.ts
Normal file
45
src/components/notify/INotifyComponentProps.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Profile } from 'core/domain/users'
|
||||||
|
import { Notification } from 'core/domain/notifications'
|
||||||
|
|
||||||
|
export interface INotifyComponentProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifications
|
||||||
|
*
|
||||||
|
* @type {{[notificationId: string]: Notification}}
|
||||||
|
* @memberof INotifyComponentProps
|
||||||
|
*/
|
||||||
|
notifications?: {[notificationId: string]: Notification}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Users' profile
|
||||||
|
*
|
||||||
|
* @type {{[userId: string]: Profile}}
|
||||||
|
* @memberof INotifyComponentProps
|
||||||
|
*/
|
||||||
|
info?: {[userId: string]: Profile}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close notification
|
||||||
|
*
|
||||||
|
* @memberof INotifyComponentProps
|
||||||
|
*/
|
||||||
|
onRequestClose: () => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User notifications popover is opem {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof INotifyComponentProps
|
||||||
|
*/
|
||||||
|
open: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep element
|
||||||
|
*
|
||||||
|
* @type {*}
|
||||||
|
* @memberof INotifyComponentProps
|
||||||
|
*/
|
||||||
|
anchorEl: any
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user