[New Feature] Add firestore to data layer
This commit is contained in:
5
.babelrc
5
.babelrc
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"react-hot-loader/babel",
|
"react-hot-loader/babel",
|
||||||
"transform-decorators-legacy"
|
"transform-decorators-legacy",
|
||||||
|
"transform-runtime"
|
||||||
],
|
],
|
||||||
"presets": [
|
"presets": [
|
||||||
"babel-polyfill", ["env", { "modules": false }],
|
["env", { "modules": false }],
|
||||||
"react",
|
"react",
|
||||||
"stage-0"
|
"stage-0"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -8,15 +8,17 @@
|
|||||||
"build": "NODE_ENV=production webpack -p",
|
"build": "NODE_ENV=production webpack -p",
|
||||||
"watch": "webpack -w",
|
"watch": "webpack -w",
|
||||||
"deploy:firebase": "npm run build && firebase deploy",
|
"deploy:firebase": "npm run build && firebase deploy",
|
||||||
"start": "npm run build && node server.js"
|
"start": "node server.js"
|
||||||
},
|
},
|
||||||
"author": "Amir Movahedi",
|
"author": "Amir Movahedi",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/react-hot-loader": "^3.0.5",
|
"@types/react-hot-loader": "^3.0.5",
|
||||||
|
"@types/react-infinite-scroller": "^1.0.4",
|
||||||
"amazon-cognito-identity-js": "^1.21.0",
|
"amazon-cognito-identity-js": "^1.21.0",
|
||||||
"aws-sdk": "^2.132.0",
|
"aws-sdk": "^2.132.0",
|
||||||
"axios": "^0.16.1",
|
"axios": "^0.16.1",
|
||||||
|
"babel-runtime": "^6.26.0",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"crypto-js": "^3.1.9-1",
|
"crypto-js": "^3.1.9-1",
|
||||||
"css-loader": "^0.28.0",
|
"css-loader": "^0.28.0",
|
||||||
@@ -25,7 +27,7 @@
|
|||||||
"express": "^4.15.2",
|
"express": "^4.15.2",
|
||||||
"faker": "^4.1.0",
|
"faker": "^4.1.0",
|
||||||
"file-loader": "^0.11.1",
|
"file-loader": "^0.11.1",
|
||||||
"firebase": "^3.9.0",
|
"firebase": "^4.6.2",
|
||||||
"inversify": "^4.3.0",
|
"inversify": "^4.3.0",
|
||||||
"keycode": "^2.1.9",
|
"keycode": "^2.1.9",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
@@ -41,6 +43,7 @@
|
|||||||
"react-dom": "^16.0.0",
|
"react-dom": "^16.0.0",
|
||||||
"react-event-listener": "^0.5.1",
|
"react-event-listener": "^0.5.1",
|
||||||
"react-hot-loader": "^3.1.3",
|
"react-hot-loader": "^3.1.3",
|
||||||
|
"react-infinite-scroller": "^1.1.1",
|
||||||
"react-linkify": "^0.2.1",
|
"react-linkify": "^0.2.1",
|
||||||
"react-parallax": "^1.4.4",
|
"react-parallax": "^1.4.4",
|
||||||
"react-redux": "^5.0.6",
|
"react-redux": "^5.0.6",
|
||||||
@@ -78,6 +81,7 @@
|
|||||||
"babel-core": "^6.24.1",
|
"babel-core": "^6.24.1",
|
||||||
"babel-loader": "^7.1.2",
|
"babel-loader": "^7.1.2",
|
||||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||||
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
"babel-polyfill": "^6.26.0",
|
"babel-polyfill": "^6.26.0",
|
||||||
"babel-preset-env": "^1.6.0",
|
"babel-preset-env": "^1.6.0",
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
@@ -91,6 +95,7 @@
|
|||||||
"karma-sourcemap-loader": "^0.3.7",
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
"karma-webpack": "^2.0.3",
|
"karma-webpack": "^2.0.3",
|
||||||
"mocha": "^3.2.0",
|
"mocha": "^3.2.0",
|
||||||
|
"open-browser-webpack-plugin": "0.0.5",
|
||||||
"redux-logger": "^3.0.1",
|
"redux-logger": "^3.0.1",
|
||||||
"redux-mock-store": "^1.2.3",
|
"redux-mock-store": "^1.2.3",
|
||||||
"source-map-loader": "^0.2.2",
|
"source-map-loader": "^0.2.2",
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/bundle-v0.1.js"></script>
|
<script src="/bundle-v0.3.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -3,6 +3,7 @@ import moment from 'moment'
|
|||||||
|
|
||||||
// - Import domain
|
// - Import domain
|
||||||
import { Comment } from 'core/domain/comments'
|
import { Comment } from 'core/domain/comments'
|
||||||
|
import { Post } from 'core/domain/posts'
|
||||||
import { SocialError } from 'core/domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
// - Import action types
|
// - Import action types
|
||||||
@@ -11,6 +12,7 @@ import { CommentActionType } from 'constants/commentActionType'
|
|||||||
// - 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 * as postActions from 'actions/postActions'
|
||||||
|
|
||||||
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
import { ICommentService } from 'core/services/comments'
|
import { ICommentService } from 'core/services/comments'
|
||||||
@@ -43,7 +45,7 @@ export const dbAddComment = (ownerPostUserId: string | null,newComment: Comment,
|
|||||||
text: newComment.text
|
text: newComment.text
|
||||||
}
|
}
|
||||||
|
|
||||||
return commentService.addComment(newComment.postId,comment)
|
return commentService.addComment(comment)
|
||||||
.then((commentKey: string) => {
|
.then((commentKey: string) => {
|
||||||
dispatch(addComment({id: commentKey! ,...comment}))
|
dispatch(addComment({id: commentKey! ,...comment}))
|
||||||
callBack()
|
callBack()
|
||||||
@@ -70,12 +72,42 @@ export const dbAddComment = (ownerPostUserId: string | null,newComment: Comment,
|
|||||||
/**
|
/**
|
||||||
* Get all comments from database
|
* Get all comments from database
|
||||||
*/
|
*/
|
||||||
export const dbGetComments = () => {
|
export const dbGetComments = (ownerUserId: string, postId: string) => {
|
||||||
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(postId, (comments: {[postId: string]: {[commentId: string]: Comment}}) => {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workout getting the number of post's comment and getting three last comments
|
||||||
|
*/
|
||||||
dispatch(addCommentList(comments))
|
dispatch(addCommentList(comments))
|
||||||
|
let commentsCount: number
|
||||||
|
const state = getState()
|
||||||
|
const post: Post = state.post.userPosts[ownerUserId][postId]
|
||||||
|
if (!post) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comments && Object.keys(comments).length > 0) {
|
||||||
|
commentsCount = Object.keys(comments).length
|
||||||
|
let sortedObjects = comments as any
|
||||||
|
// Sort posts with creation date
|
||||||
|
sortedObjects.sort((a: any, b: any) => {
|
||||||
|
return parseInt(b.creationDate, 10) - parseInt(a.creationDate, 10)
|
||||||
|
})
|
||||||
|
if (!post.comments) {
|
||||||
|
post.comments = {}
|
||||||
|
}
|
||||||
|
Object.keys(sortedObjects).slice(0, 3).forEach((commentId) => {
|
||||||
|
post.comments![commentId] = {
|
||||||
|
id: commentId,
|
||||||
|
...sortedObjects[commentId]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(postActions.updatePost(post.ownerUserId!,post))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,7 +139,7 @@ export const dbUpdateComment = (id: string, postId: string, text: string) => {
|
|||||||
userId: uid
|
userId: uid
|
||||||
}
|
}
|
||||||
|
|
||||||
return commentService.updateComment(id,postId,updatedComment)
|
return commentService.updateComment(updatedComment)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch(updateComment( id, postId, text))
|
dispatch(updateComment( id, postId, text))
|
||||||
dispatch(globalActions.hideTopLoading())
|
dispatch(globalActions.hideTopLoading())
|
||||||
@@ -118,7 +150,6 @@ export const dbUpdateComment = (id: string, postId: string, text: string) => {
|
|||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const defaultDataEnable = () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Default data loaded status will be false
|
* Default data loaded status will be false
|
||||||
* @param {boolean} status
|
* @param {boolean} status
|
||||||
*/
|
*/
|
||||||
export const defaultDataDisable = () => {
|
export const defaultDataDisable = () => {
|
||||||
return{
|
return{
|
||||||
@@ -56,6 +56,7 @@ export const showNotificationSuccess = () => {
|
|||||||
* Hide global message
|
* Hide global message
|
||||||
*/
|
*/
|
||||||
export const hideMessage = () => {
|
export const hideMessage = () => {
|
||||||
|
hideTopLoading()
|
||||||
return{
|
return{
|
||||||
type: GlobalActionType.HIDE_MESSAGE_GLOBAL
|
type: GlobalActionType.HIDE_MESSAGE_GLOBAL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Action } from 'redux'
|
|||||||
|
|
||||||
// - Import domain
|
// - Import domain
|
||||||
import { Post } from 'core/domain/posts'
|
import { Post } from 'core/domain/posts'
|
||||||
|
import { Comment } from 'core/domain/comments'
|
||||||
import { SocialError } from 'core/domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
// - Import utility components
|
// - Import utility components
|
||||||
@@ -27,7 +28,7 @@ const postService: IPostService = serviceProvider.createPostService()
|
|||||||
* @param {any} newPost
|
* @param {any} newPost
|
||||||
* @param {Function} callBack
|
* @param {Function} callBack
|
||||||
*/
|
*/
|
||||||
export let dbAddPost = (newPost: any, callBack: Function) => {
|
export let dbAddPost = (newPost: Post, callBack: Function) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
|
|
||||||
let uid: string = getState().authorize.uid
|
let uid: string = getState().authorize.uid
|
||||||
@@ -39,11 +40,13 @@ export let dbAddPost = (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,
|
||||||
|
comments: {},
|
||||||
|
votes: {},
|
||||||
image: '',
|
image: '',
|
||||||
imageFullPath: '',
|
imageFullPath: '',
|
||||||
video: '',
|
video: '',
|
||||||
@@ -52,14 +55,14 @@ export let dbAddPost = (newPost: any, callBack: Function) => {
|
|||||||
deleted: false
|
deleted: false
|
||||||
}
|
}
|
||||||
|
|
||||||
return postService.addPost(uid,post).then((postKey: string) => {
|
return postService.addPost(post).then((postKey: string) => {
|
||||||
dispatch(addPost(uid, {
|
dispatch(addPost(uid, {
|
||||||
...post,
|
...post,
|
||||||
id: postKey
|
id: postKey
|
||||||
}))
|
}))
|
||||||
callBack()
|
callBack()
|
||||||
})
|
})
|
||||||
.catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message)))
|
.catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +98,7 @@ export const dbAddImagePost = (newPost: Post, callBack: Function) => {
|
|||||||
deleted: false
|
deleted: false
|
||||||
}
|
}
|
||||||
|
|
||||||
return postService.addPost(uid,post).then((postKey: string) => {
|
return postService.addPost(post).then((postKey: string) => {
|
||||||
dispatch(addPost(uid, {
|
dispatch(addPost(uid, {
|
||||||
...post,
|
...post,
|
||||||
id: postKey
|
id: postKey
|
||||||
@@ -104,7 +107,7 @@ export const dbAddImagePost = (newPost: Post, callBack: Function) => {
|
|||||||
dispatch(globalActions.hideTopLoading())
|
dispatch(globalActions.hideTopLoading())
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message)))
|
.catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,51 +118,25 @@ export const dbAddImagePost = (newPost: Post, 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: Post, callBack: Function) => {
|
export const dbUpdatePost = (updatedPost: Post, callBack: Function) => {
|
||||||
console.log(newPost)
|
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
// Write the new data simultaneously in the list
|
return postService.updatePost(updatedPost).then(() => {
|
||||||
let updates: any = {}
|
|
||||||
let post: Post = getState().post.userPosts[uid][newPost.id!]
|
|
||||||
let updatedPost: Post = {
|
|
||||||
postTypeId: post.postTypeId,
|
|
||||||
creationDate: post.creationDate,
|
|
||||||
deleteDate: 0,
|
|
||||||
score: post.score,
|
|
||||||
viewCount: post.viewCount,
|
|
||||||
body: newPost.body ? newPost.body : post.body || '',
|
|
||||||
ownerUserId: uid,
|
|
||||||
ownerDisplayName: post.ownerDisplayName,
|
|
||||||
ownerAvatar: post.ownerAvatar,
|
|
||||||
lastEditDate: moment().unix(),
|
|
||||||
tags: newPost.tags ? newPost.tags : (post.tags || []),
|
|
||||||
commentCounter: post.commentCounter,
|
|
||||||
image: newPost.image ? newPost.image : post.image,
|
|
||||||
imageFullPath: newPost.imageFullPath!,
|
|
||||||
video: '',
|
|
||||||
disableComments: newPost.disableComments !== undefined ? newPost.disableComments : (post.disableComments ? post.disableComments : false),
|
|
||||||
disableSharing: newPost.disableSharing !== undefined ? newPost.disableSharing : (post.disableSharing ? post.disableSharing : false),
|
|
||||||
deleted: false
|
|
||||||
}
|
|
||||||
|
|
||||||
return postService.updatePost(uid,newPost.id,updatedPost).then(() => {
|
dispatch(updatePost(uid, { ...updatedPost }))
|
||||||
|
|
||||||
dispatch(updatePost(uid, { id: newPost.id, ...updatedPost }))
|
|
||||||
callBack()
|
callBack()
|
||||||
dispatch(globalActions.hideTopLoading())
|
dispatch(globalActions.hideTopLoading())
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((error: SocialError) => {
|
.catch((error: SocialError) => {
|
||||||
dispatch(globalActions.showErrorMessage(error.message))
|
dispatch(globalActions.showErrorMessage(error.message))
|
||||||
dispatch(globalActions.hideTopLoading())
|
dispatch(globalActions.hideTopLoading())
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -176,15 +153,15 @@ export const dbDeletePost = (id: string) => {
|
|||||||
// Get current user id
|
// Get current user id
|
||||||
let uid: string = getState().authorize.uid
|
let uid: string = getState().authorize.uid
|
||||||
|
|
||||||
return postService.deletePost(uid,id).then(() => {
|
return postService.deletePost(id).then(() => {
|
||||||
dispatch(deletePost(uid, id))
|
dispatch(deletePost(uid, id))
|
||||||
dispatch(globalActions.hideTopLoading())
|
dispatch(globalActions.hideTopLoading())
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((error: SocialError) => {
|
.catch((error: SocialError) => {
|
||||||
dispatch(globalActions.showErrorMessage(error.message))
|
dispatch(globalActions.showErrorMessage(error.message))
|
||||||
dispatch(globalActions.hideTopLoading())
|
dispatch(globalActions.hideTopLoading())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -200,9 +177,9 @@ export const dbGetPosts = () => {
|
|||||||
return postService.getPosts(uid).then((posts: { [postId: string]: Post }) => {
|
return postService.getPosts(uid).then((posts: { [postId: string]: Post }) => {
|
||||||
dispatch(addPosts(uid, posts))
|
dispatch(addPosts(uid, posts))
|
||||||
})
|
})
|
||||||
.catch((error: SocialError) => {
|
.catch((error: SocialError) => {
|
||||||
dispatch(globalActions.showErrorMessage(error.message))
|
dispatch(globalActions.showErrorMessage(error.message))
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,12 +194,12 @@ export const dbGetPostById = (uid: string, postId: string) => {
|
|||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
if (uid) {
|
if (uid) {
|
||||||
|
|
||||||
return postService.getPostById(uid,postId).then((post: Post) => {
|
return postService.getPostById(postId).then((post: Post) => {
|
||||||
dispatch(addPost(uid, post))
|
dispatch(addPost(uid, post))
|
||||||
})
|
})
|
||||||
.catch((error: SocialError) => {
|
.catch((error: SocialError) => {
|
||||||
dispatch(globalActions.showErrorMessage(error.message))
|
dispatch(globalActions.showErrorMessage(error.message))
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,7 +210,7 @@ export const dbGetPostById = (uid: string, postId: string) => {
|
|||||||
* @param uid posts owner identifier
|
* @param uid posts owner identifier
|
||||||
*/
|
*/
|
||||||
export const dbGetPostsByUserId = (uid: string) => {
|
export const dbGetPostsByUserId = (uid: string) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: Function, getState: Function) => {
|
||||||
|
|
||||||
if (uid) {
|
if (uid) {
|
||||||
return postService.getPosts(uid).then((posts: { [postId: string]: Post }) => {
|
return postService.getPosts(uid).then((posts: { [postId: string]: Post }) => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// - Import react components
|
// - Import react components
|
||||||
|
import _ from 'lodash'
|
||||||
// - Import domain
|
// - Import domain
|
||||||
import { Profile } from 'core/domain/users'
|
import { Profile } from 'core/domain/users'
|
||||||
import { SocialError } from 'core/domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
@@ -26,14 +26,14 @@ export const dbGetUserInfo = () => {
|
|||||||
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 userService.getUserProfile(uid).then((userProfile: Profile) => {
|
return userService.getUserProfile(uid).then((userProfile: Profile) => {
|
||||||
dispatch(addUserInfo(uid, {
|
dispatch(addUserInfo(uid, {
|
||||||
avatar: userProfile.avatar,
|
avatar: userProfile.avatar,
|
||||||
email: userProfile.email,
|
email: userProfile.email,
|
||||||
fullName: userProfile.fullName,
|
fullName: userProfile.fullName,
|
||||||
banner: userProfile.banner,
|
banner: userProfile.banner,
|
||||||
tagLine: userProfile.tagLine
|
tagLine: userProfile.tagLine,
|
||||||
|
creationDate: userProfile.creationDate
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
.catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message)))
|
.catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message)))
|
||||||
@@ -63,7 +63,8 @@ export const dbGetUserInfoByUserId = (uid: string, callerKey: string) => {
|
|||||||
email: userProfile.email,
|
email: userProfile.email,
|
||||||
fullName: userProfile.fullName,
|
fullName: userProfile.fullName,
|
||||||
banner: userProfile.banner,
|
banner: userProfile.banner,
|
||||||
tagLine: userProfile.tagLine
|
tagLine: userProfile.tagLine,
|
||||||
|
creationDate: userProfile.creationDate
|
||||||
}))
|
}))
|
||||||
|
|
||||||
switch (callerKey) {
|
switch (callerKey) {
|
||||||
@@ -97,7 +98,8 @@ export const dbUpdateUserInfo = (newProfile: Profile) => {
|
|||||||
banner: newProfile.banner || profile.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: newProfile.banner || profile.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',
|
||||||
email: newProfile.email || profile.email || '',
|
email: newProfile.email || profile.email || '',
|
||||||
fullName: newProfile.fullName || profile.fullName || '',
|
fullName: newProfile.fullName || profile.fullName || '',
|
||||||
tagLine: newProfile.tagLine || profile.tagLine || ''
|
tagLine: newProfile.tagLine || profile.tagLine || '',
|
||||||
|
creationDate: newProfile.creationDate
|
||||||
}
|
}
|
||||||
|
|
||||||
return userService.updateUserProfile(uid,updatedProfie).then(() => {
|
return userService.updateUserProfile(uid,updatedProfie).then(() => {
|
||||||
@@ -112,11 +114,13 @@ export const dbUpdateUserInfo = (newProfile: Profile) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// - Get people info from database
|
// - Get people info from database
|
||||||
export const dbGetPeopleInfo = () => {
|
export const dbGetPeopleInfo = (page?: number) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
let uid: string = getState().authorize.uid
|
const {authorize, user} = getState()
|
||||||
|
let uid: string = authorize.uid
|
||||||
if (uid) {
|
if (uid) {
|
||||||
return userService.getUsersProfile(uid)
|
const lastKey = ''
|
||||||
|
return userService.getUsersProfile(uid, lastKey)
|
||||||
.then((usersProfile: {[userId: string]: Profile}) => {
|
.then((usersProfile: {[userId: string]: Profile}) => {
|
||||||
dispatch(addPeopleInfo(usersProfile))
|
dispatch(addPeopleInfo(usersProfile))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ 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 * as postActions from 'actions/postActions'
|
||||||
|
|
||||||
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
import { IVoteService } from 'core/services/votes'
|
import { IVoteService } from 'core/services/votes'
|
||||||
|
import { Post } from 'core/domain/posts'
|
||||||
|
|
||||||
const serviceProvider: IServiceProvider = new ServiceProvide()
|
const serviceProvider: IServiceProvider = new ServiceProvide()
|
||||||
const voteService: IVoteService = serviceProvider.createVoteService()
|
const voteService: IVoteService = serviceProvider.createVoteService()
|
||||||
@@ -26,7 +28,8 @@ const voteService: IVoteService = serviceProvider.createVoteService()
|
|||||||
export const dbAddVote = (postId: string,ownerPostUserId: string) => {
|
export const dbAddVote = (postId: string,ownerPostUserId: string) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
|
|
||||||
let uid: string = getState().authorize.uid
|
const state = getState()
|
||||||
|
let uid: string = state.authorize.uid
|
||||||
let vote: Vote = {
|
let vote: Vote = {
|
||||||
postId: postId,
|
postId: postId,
|
||||||
creationDate: moment().unix(),
|
creationDate: moment().unix(),
|
||||||
@@ -34,41 +37,52 @@ export const dbAddVote = (postId: string,ownerPostUserId: string) => {
|
|||||||
userAvatar: getState().user.info[uid].avatar,
|
userAvatar: getState().user.info[uid].avatar,
|
||||||
userId: uid
|
userId: uid
|
||||||
}
|
}
|
||||||
|
const post: Post = state.post.userPosts[ownerPostUserId][postId]
|
||||||
|
|
||||||
|
post.score! += 1
|
||||||
|
dispatch(postActions.updatePost(ownerPostUserId,post))
|
||||||
|
|
||||||
return voteService.addVote(vote).then((voteKey: string) => {
|
return voteService.addVote(vote).then((voteKey: string) => {
|
||||||
dispatch(addVote(
|
|
||||||
{
|
|
||||||
...vote,
|
|
||||||
id: voteKey
|
|
||||||
}))
|
|
||||||
if (uid !== ownerPostUserId) {
|
if (uid !== ownerPostUserId) {
|
||||||
dispatch(notifyActions.dbAddNotification(
|
dispatch(notifyActions.dbAddNotification(
|
||||||
{
|
{
|
||||||
description: 'Vote on your post.',
|
description: 'Vote on your post.',
|
||||||
url: `/${ownerPostUserId}/posts/${postId}`,
|
url: `/${ownerPostUserId}/posts/${postId}`,
|
||||||
notifyRecieverUserId: ownerPostUserId,notifierUserId:uid,
|
notifyRecieverUserId: ownerPostUserId,notifierUserId: uid,
|
||||||
isSeen: false
|
isSeen: false
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((error) => dispatch(globalActions.showErrorMessage(error.message)))
|
.catch((error) => {
|
||||||
|
post.score! -= 1
|
||||||
|
dispatch(postActions.updatePost(ownerPostUserId,post))
|
||||||
|
dispatch(globalActions.showErrorMessage(error.message))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all votes from database
|
* Get all votes from database
|
||||||
*/
|
*/
|
||||||
export const dbGetVotes = () => {
|
export const dbGetVotes = (userId: string, postId: string) => {
|
||||||
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 voteService
|
return voteService
|
||||||
.getVotes()
|
.getVotes(postId)
|
||||||
.then((postVotes: { [postId: string]: { [voteId: string]: Vote } }) => {
|
.then((postVotes: { [postId: string]: { [voteId: string]: Vote } }) => {
|
||||||
dispatch(addVoteList(postVotes))
|
dispatch(addVoteList(postVotes))
|
||||||
|
const state = getState()
|
||||||
|
const post: Post = state.post.userPosts[userId][postId]
|
||||||
|
if (!post) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const votes = postVotes[postId]
|
||||||
|
if (votes && Object.keys(votes).length > 0) {
|
||||||
|
post.score = Object.keys(votes).length
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -80,19 +94,24 @@ export const dbGetVotes = () => {
|
|||||||
* @param {string} id of vote
|
* @param {string} id of vote
|
||||||
* @param {string} postId is the identifier of the post which vote belong to
|
* @param {string} postId is the identifier of the post which vote belong to
|
||||||
*/
|
*/
|
||||||
export const dbDeleteVote = (postId: string) => {
|
export const dbDeleteVote = (postId: string, ownerPostUserId: string) => {
|
||||||
return (dispatch: any, getState: Function) => {
|
return (dispatch: any, getState: Function) => {
|
||||||
|
const state = getState()
|
||||||
// Get current user id
|
// Get current user id
|
||||||
let uid: string = getState().authorize.uid
|
let uid: string = state.authorize.uid
|
||||||
|
|
||||||
let votes: {[voteId: string]: Vote} = getState().vote.postVotes[postId]
|
let votes: {[voteId: string]: Vote} = getState().vote.postVotes[postId]
|
||||||
let id: string = Object.keys(votes).filter((key) => votes[key].userId === uid)[0]
|
let id: string = Object.keys(votes).filter((key) => votes[key].userId === uid)[0]
|
||||||
|
const vote = votes[id]
|
||||||
return voteService.deleteVote(id,postId).then(() => {
|
const post: Post = state.post.userPosts[ownerPostUserId][postId]
|
||||||
dispatch(deleteVote(id, postId))
|
post.score! -= 1
|
||||||
|
dispatch(postActions.updatePost(ownerPostUserId,post))
|
||||||
|
return voteService.deleteVote(vote).then(x => x)
|
||||||
|
.catch((error: any) => {
|
||||||
|
post.score! += 1
|
||||||
|
dispatch(postActions.updatePost(ownerPostUserId,post))
|
||||||
|
dispatch(globalActions.showErrorMessage(error.message))
|
||||||
})
|
})
|
||||||
.catch((error: any) => dispatch(globalActions.showErrorMessage(error.message)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +136,7 @@ export const deleteVote = (id: string, postId: string) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Ad a list of vote
|
* Ad a list of vote
|
||||||
* @param {[postId:string]: {[voteId: string]: Vote}} votes a list of vote
|
* @param {[postId:string]: {[voteId: string]: Vote}} votes a list of vote
|
||||||
*/
|
*/
|
||||||
export const addVoteList = (votes: {[postId: string]: {[voteId: string]: Vote}}) => {
|
export const addVoteList = (votes: {[postId: string]: {[voteId: string]: Vote}}) => {
|
||||||
return { type: VoteActionType.ADD_VOTE_LIST, payload: votes }
|
return { type: VoteActionType.ADD_VOTE_LIST, payload: votes }
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// - Import react component
|
|
||||||
import { storageRef } from 'data/firebaseClient'
|
|
||||||
|
|
||||||
// - Interface declaration
|
// - Interface declaration
|
||||||
interface FileReaderEventTarget extends EventTarget {
|
interface FileReaderEventTarget extends EventTarget {
|
||||||
@@ -27,41 +25,6 @@ const convertImageToCanvas = (image: HTMLImageElement | HTMLCanvasElement | HTML
|
|||||||
return canvas
|
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
|
* Constraint image size
|
||||||
* @param {file} file
|
* @param {file} file
|
||||||
@@ -141,7 +104,6 @@ export default {
|
|||||||
dataURLToBlob,
|
dataURLToBlob,
|
||||||
convertImageToCanvas,
|
convertImageToCanvas,
|
||||||
getExtension,
|
getExtension,
|
||||||
constraintImage,
|
constraintImage
|
||||||
uploadImage
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import FlatButton from 'material-ui/FlatButton'
|
|||||||
import TextField from 'material-ui/TextField'
|
import TextField from 'material-ui/TextField'
|
||||||
import Divider from 'material-ui/Divider'
|
import Divider from 'material-ui/Divider'
|
||||||
import { ListItem } from 'material-ui/List'
|
import { ListItem } from 'material-ui/List'
|
||||||
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors'
|
import { grey400, darkBlack, lightBlack, tealA400 } from 'material-ui/styles/colors'
|
||||||
|
import LinearProgress from 'material-ui/LinearProgress'
|
||||||
|
|
||||||
// - Import actions
|
// - Import actions
|
||||||
import * as commentActions from 'actions/commentActions'
|
import * as commentActions from 'actions/commentActions'
|
||||||
@@ -65,6 +66,10 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
|
|||||||
},
|
},
|
||||||
writeCommentTextField: {
|
writeCommentTextField: {
|
||||||
width: '100%'
|
width: '100%'
|
||||||
|
},
|
||||||
|
progressbar: {
|
||||||
|
height: '1.5px',
|
||||||
|
backgroundColor: 'rgb(245, 243, 243)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +138,7 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
|
|||||||
* @return {DOM} list of comments' DOM
|
* @return {DOM} list of comments' DOM
|
||||||
*/
|
*/
|
||||||
commentList = () => {
|
commentList = () => {
|
||||||
let comments = this.props.comments
|
let comments = this.props.commentSlides
|
||||||
if (comments) {
|
if (comments) {
|
||||||
|
|
||||||
let parsedComments: Comment[] = []
|
let parsedComments: Comment[] = []
|
||||||
@@ -178,11 +183,8 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
|
|||||||
</div>}
|
</div>}
|
||||||
secondaryTextLines={2}
|
secondaryTextLines={2}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,9 +193,41 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
|
|||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render () {
|
render () {
|
||||||
|
const {comments} = this.props
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment list box
|
||||||
|
*/
|
||||||
|
const commentWriteBox = (<div>
|
||||||
|
<Divider />
|
||||||
|
<Paper zDepth={0} className='animate2-top10' style={{ position: 'relative', overflowY: 'auto', padding: '12px 16px', display: (this.props.open ? 'block' : 'none') }}>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<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 }}>
|
||||||
|
<TextField
|
||||||
|
value={this.state.commentText}
|
||||||
|
onChange={this.handleOnChange}
|
||||||
|
hintText='Add a comment...'
|
||||||
|
underlineShow={false}
|
||||||
|
multiLine={true}
|
||||||
|
rows={1}
|
||||||
|
hintStyle={{ fontWeight: 100, fontSize: '14px' }}
|
||||||
|
rowsMax={4}
|
||||||
|
textareaStyle={{ fontWeight: 100, fontSize: '14px' }}
|
||||||
|
style={this.styles.writeCommentTextField}
|
||||||
|
/>
|
||||||
|
</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} />
|
||||||
|
</Paper>
|
||||||
|
</div>)
|
||||||
|
/**
|
||||||
|
* Return Elements
|
||||||
|
*/
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div style={this.props.comments && Object.keys(this.props.comments).length > 0 ? { display: 'block' } : { display: 'none' }}>
|
<div style={this.props.commentSlides && Object.keys(this.props.commentSlides).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' }}>
|
||||||
|
|
||||||
@@ -206,37 +240,22 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
{(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' }}>
|
!comments
|
||||||
<CommentListComponent comments={this.props.comments} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments}/>
|
? this.props.open ? <LinearProgress style={this.styles.progressbar} mode='indeterminate' color={tealA400} /> : ''
|
||||||
|
: (Object.keys(comments).length > 0
|
||||||
</Paper>) : ''}
|
? (<Paper zDepth={0} style={this.props.open ? { display: 'block', padding: '0px 0px' } : { display: 'none', padding: '12px 16px' }}>
|
||||||
|
<CommentListComponent comments={comments} isPostOwner={this.props.isPostOwner} disableComments={this.props.disableComments}/>
|
||||||
|
</Paper>)
|
||||||
|
: '')
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{!this.props.disableComments ? (<div>
|
{
|
||||||
<Divider />
|
!this.props.disableComments
|
||||||
<Paper zDepth={0} className='animate2-top10' style={{ position: 'relative', overflowY: 'auto', padding: '12px 16px', display: (this.props.open ? 'block' : 'none') }}>
|
? commentWriteBox
|
||||||
|
: ''
|
||||||
<div style={{ display: 'flex' }}>
|
}
|
||||||
<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 }}>
|
|
||||||
<TextField
|
|
||||||
value={this.state.commentText}
|
|
||||||
onChange={this.handleOnChange}
|
|
||||||
hintText='Add a comment...'
|
|
||||||
underlineShow={false}
|
|
||||||
multiLine={true}
|
|
||||||
rows={1}
|
|
||||||
hintStyle={{ fontWeight: 100, fontSize: '14px' }}
|
|
||||||
rowsMax={4}
|
|
||||||
textareaStyle={{ fontWeight: 100, fontSize: '14px' }}
|
|
||||||
style={this.styles.writeCommentTextField}
|
|
||||||
/>
|
|
||||||
</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} />
|
|
||||||
</Paper>
|
|
||||||
</div>) : ''}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -266,11 +285,15 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICommentGroupComponentProps
|
|||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state: any, ownProps: ICommentGroupComponentProps) => {
|
const mapStateToProps = (state: any, ownProps: ICommentGroupComponentProps) => {
|
||||||
|
const {post, user, authorize} = state
|
||||||
|
const {ownerPostUserId, postId} = ownProps
|
||||||
|
const commentSlides = post.userPosts[ownerPostUserId] && post.userPosts[ownerPostUserId][postId] ? post.userPosts[ownerPostUserId][postId].comments : {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
comments: state.comment.postComments[ownProps.postId],
|
commentSlides,
|
||||||
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar || '' : '',
|
avatar: user.info && user.info[state.authorize.uid] ? user.info[authorize.uid].avatar || '' : '',
|
||||||
fullName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName || '' : '',
|
fullName: user.info && user.info[state.authorize.uid] ? user.info[authorize.uid].fullName || '' : '',
|
||||||
userInfo: state.user.info
|
userInfo: user.info
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ export interface ICommentGroupComponentProps {
|
|||||||
*/
|
*/
|
||||||
comments?: {[commentId: string]: Comment}
|
comments?: {[commentId: string]: Comment}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commnets show on slide preview
|
||||||
|
*
|
||||||
|
* @type {{[commentId: string]: Comment}}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
commentSlides?: {[commentId: string]: Comment}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The post identifier which comment belong to
|
* The post identifier which comment belong to
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ 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 Paper from 'material-ui/Paper'
|
import Paper from 'material-ui/Paper'
|
||||||
|
import InfiniteScroll from 'react-infinite-scroller'
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import UserBoxList from 'components/userBoxList'
|
import UserBoxList from 'components/userBoxList'
|
||||||
@@ -39,6 +40,12 @@ export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IF
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadItems (page: number) {
|
||||||
|
console.log('------------------------')
|
||||||
|
console.log(page)
|
||||||
|
console.log('------------------------')
|
||||||
|
}
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
this.props.loadPeople!()
|
this.props.loadPeople!()
|
||||||
}
|
}
|
||||||
@@ -63,9 +70,18 @@ export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IF
|
|||||||
right: 0
|
right: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const loader = <div className='loader'>Loading ...</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<InfiniteScroll
|
||||||
|
pageStart={0}
|
||||||
|
loadMore={this.loadItems.bind(this)}
|
||||||
|
hasMore={false}
|
||||||
|
loader={loader}>
|
||||||
|
|
||||||
|
<div className='tracks'>
|
||||||
|
|
||||||
{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
|
||||||
@@ -75,6 +91,8 @@ export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IF
|
|||||||
</div>) : (<div className='g__title-center'>
|
</div>) : (<div className='g__title-center'>
|
||||||
Nothing to show! :(
|
Nothing to show! :(
|
||||||
</div>)}
|
</div>)}
|
||||||
|
</div>
|
||||||
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// - Import react components
|
// - Import react components
|
||||||
|
import { HomeRouter } from 'routes'
|
||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
|
import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
|
||||||
@@ -111,8 +112,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
|
|||||||
goTo!('/login')
|
goTo!('/login')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// if (!isVerifide) {
|
if (!isVerifide) {
|
||||||
if (false) {
|
|
||||||
goTo!('/emailVerification')
|
goTo!('/emailVerification')
|
||||||
|
|
||||||
} else if (!global.defaultLoadDataStatus) {
|
} else if (!global.defaultLoadDataStatus) {
|
||||||
@@ -131,7 +131,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
|
|||||||
* @memberof Home
|
* @memberof Home
|
||||||
*/
|
*/
|
||||||
render () {
|
render () {
|
||||||
const {loaded} = this.props
|
const {loaded, authed, mergedPosts} = this.props
|
||||||
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} />
|
||||||
@@ -153,35 +153,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
|
|||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
|
|
||||||
<SidebarMain>
|
<SidebarMain>
|
||||||
{loaded ? (<Switch>
|
<HomeRouter enabled={loaded!} data={{mergedPosts}} />
|
||||||
<Route path='/people/:tab?' render={() => {
|
|
||||||
return (
|
|
||||||
this.props.authed
|
|
||||||
? <People />
|
|
||||||
: <Redirect to='/login' />
|
|
||||||
)
|
|
||||||
}} />
|
|
||||||
<Route path='/tag/:tag' render={({match}) => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
this.props.authed
|
|
||||||
? <div className='blog'><StreamComponent displayWriting={false} homeTitle={`#${match.params.tag}`} posts={this.props.mergedPosts} /></div>
|
|
||||||
: <Redirect to='/login' />
|
|
||||||
)
|
|
||||||
}} />
|
|
||||||
<Route path='/:userId/posts/:postId/:tag?' component={PostPage} />
|
|
||||||
<Route path='/:userId' component={Profile} />
|
|
||||||
|
|
||||||
<Route path='/' render={() => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
this.props.authed
|
|
||||||
? <div className='blog'><StreamComponent homeTitle='Home' posts={this.props.mergedPosts} displayWriting={true} /></div>
|
|
||||||
: <Redirect to='/login' />
|
|
||||||
)
|
|
||||||
}} />
|
|
||||||
</Switch>)
|
|
||||||
: ''}
|
|
||||||
</SidebarMain>
|
</SidebarMain>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
|
|
||||||
@@ -196,11 +168,9 @@ const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
loadData: () => {
|
loadData: () => {
|
||||||
dispatch(commentActions.dbGetComments())
|
|
||||||
dispatch(imageGalleryActions.dbGetImageGallery())
|
dispatch(imageGalleryActions.dbGetImageGallery())
|
||||||
dispatch(postActions.dbGetPosts())
|
dispatch(postActions.dbGetPosts())
|
||||||
dispatch(userActions.dbGetUserInfo())
|
dispatch(userActions.dbGetUserInfo())
|
||||||
dispatch(voteActions.dbGetVotes())
|
|
||||||
dispatch(notifyActions.dbGetNotifications())
|
dispatch(notifyActions.dbGetNotifications())
|
||||||
dispatch(circleActions.dbGetCircles())
|
dispatch(circleActions.dbGetCircles())
|
||||||
|
|
||||||
@@ -209,8 +179,6 @@ const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
|
|||||||
dispatch(imageGalleryActions.clearAllData())
|
dispatch(imageGalleryActions.clearAllData())
|
||||||
dispatch(postActions.clearAllData())
|
dispatch(postActions.clearAllData())
|
||||||
dispatch(userActions.clearAllData())
|
dispatch(userActions.clearAllData())
|
||||||
dispatch(commentActions.clearAllData())
|
|
||||||
dispatch(voteActions.clearAllvotes())
|
|
||||||
dispatch(notifyActions.clearAllNotifications())
|
dispatch(notifyActions.clearAllNotifications())
|
||||||
dispatch(circleActions.clearAllCircles())
|
dispatch(circleActions.clearAllCircles())
|
||||||
dispatch(globalActions.clearTemp())
|
dispatch(globalActions.clearTemp())
|
||||||
@@ -234,7 +202,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
|
|||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
|
const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
|
||||||
const { authorize, global, user, post, comment, imageGallery, vote, notify, circle } = state
|
const { authorize, global, user, post, imageGallery, notify, circle } = state
|
||||||
const { uid } = authorize
|
const { uid } = authorize
|
||||||
let mergedPosts = {}
|
let mergedPosts = {}
|
||||||
const circles = circle ? (circle.userCircles[uid] || {}) : {}
|
const circles = circle ? (circle.userCircles[uid] || {}) : {}
|
||||||
@@ -251,7 +219,7 @@ const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
|
|||||||
mainStyle: global.sidebarMainStyle,
|
mainStyle: global.sidebarMainStyle,
|
||||||
mergedPosts,
|
mergedPosts,
|
||||||
global,
|
global,
|
||||||
loaded: user.loaded && post.loaded && comment.loaded && imageGallery.loaded && vote.loaded && notify.loaded && circle.loaded
|
loaded: user.loaded && post.loaded && imageGallery.loaded && notify.loaded && circle.loaded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export interface ILoginComponentProps {
|
|||||||
*
|
*
|
||||||
* @memberof ILoginComponentProps
|
* @memberof ILoginComponentProps
|
||||||
*/
|
*/
|
||||||
loginWithOAuth: (type: OAuthType) => any
|
loginWithOAuth?: (type: OAuthType) => any
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirect to signup page
|
* Redirect to signup page
|
||||||
|
|||||||
@@ -8,13 +8,9 @@ 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 Signup from 'components/signup'
|
|
||||||
import EmailVerification from 'components/emailVerification'
|
|
||||||
import Login from 'components/login'
|
|
||||||
import ResetPassword from 'components/resetPassword'
|
|
||||||
import Setting from 'components/setting'
|
|
||||||
import MasterLoading from 'components/masterLoading'
|
import MasterLoading from 'components/masterLoading'
|
||||||
|
import MasterRouter from 'routes/MasterRouter'
|
||||||
import { IMasterComponentProps } from './IMasterComponentProps'
|
import { IMasterComponentProps } from './IMasterComponentProps'
|
||||||
import { IMasterComponentState } from './IMasterComponentState'
|
import { IMasterComponentState } from './IMasterComponentState'
|
||||||
import { ServiceProvide, IServiceProvider } from 'core/factories'
|
import { ServiceProvide, IServiceProvider } from 'core/factories'
|
||||||
@@ -80,7 +76,7 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
|
|||||||
console.log('====================================')
|
console.log('====================================')
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount () {
|
componentDidMount () {
|
||||||
|
|
||||||
this._authourizeService.onAuthStateChanged((isVerifide: boolean, user: any) => {
|
this._authourizeService.onAuthStateChanged((isVerifide: boolean, user: any) => {
|
||||||
const {global, clearData, loadDataGuest, defaultDataDisable, defaultDataEnable, login, logout } = this.props
|
const {global, clearData, loadDataGuest, defaultDataDisable, defaultDataEnable, login, logout } = this.props
|
||||||
@@ -114,9 +110,9 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
|
|||||||
*
|
*
|
||||||
* @memberof Master
|
* @memberof Master
|
||||||
*/
|
*/
|
||||||
public render (): React.ReactElement<{}> {
|
public render () {
|
||||||
|
|
||||||
const { progress, global, loaded, guest } = this.props
|
const { progress, global, loaded, guest, uid } = this.props
|
||||||
const { loading, isVerifide } = this.state
|
const { loading, isVerifide } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -129,25 +125,7 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
|
|||||||
<div className='title'>Loading ... </div>
|
<div className='title'>Loading ... </div>
|
||||||
</div>
|
</div>
|
||||||
<MasterLoading activeLoading={loading} handleLoading={this.handleLoading} />
|
<MasterLoading activeLoading={loading} handleLoading={this.handleLoading} />
|
||||||
|
<MasterRouter enabled={!loading} data={{uid}} />
|
||||||
{(!loading) ? (<Switch>
|
|
||||||
<Route path='/signup' component={Signup} />
|
|
||||||
<Route path='/emailVerification' component={EmailVerification} />
|
|
||||||
<Route path='/settings' component={Setting} />
|
|
||||||
<Route path='/resetPassword' component={ResetPassword} />
|
|
||||||
<Route path='/login' render={() => {
|
|
||||||
return (
|
|
||||||
this.props.authed
|
|
||||||
? <Redirect to='/' />
|
|
||||||
: <Login />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} />
|
|
||||||
<Route render={() => <Home uid={this.props.uid} />} />
|
|
||||||
|
|
||||||
</Switch>)
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
<Snackbar
|
<Snackbar
|
||||||
open={this.props.global.messageOpen}
|
open={this.props.global.messageOpen}
|
||||||
message={this.props.global.message}
|
message={this.props.global.message}
|
||||||
|
|||||||
@@ -1,74 +1,23 @@
|
|||||||
|
import { Comment } from 'core/domain/comments'
|
||||||
|
import { Post } from 'core/domain/posts/post'
|
||||||
|
|
||||||
export interface IPostComponentProps {
|
export interface IPostComponentProps {
|
||||||
|
|
||||||
/**
|
|
||||||
* The context of a post
|
|
||||||
*/
|
|
||||||
body: string
|
|
||||||
/**
|
|
||||||
* The number of comment on a post
|
|
||||||
*/
|
|
||||||
commentCounter: number
|
|
||||||
/**
|
|
||||||
* Creation post date
|
|
||||||
*/
|
|
||||||
creationDate: number
|
|
||||||
/**
|
|
||||||
* Post identifier
|
|
||||||
*/
|
|
||||||
id: string
|
|
||||||
/**
|
|
||||||
* Post image address
|
|
||||||
*/
|
|
||||||
image: string
|
|
||||||
/**
|
|
||||||
* The last time date when post has was edited
|
|
||||||
*/
|
|
||||||
lastEditDate: number
|
|
||||||
/**
|
|
||||||
* The name of the user who created the post
|
|
||||||
*/
|
|
||||||
ownerDisplayName: string
|
|
||||||
/**
|
|
||||||
* The identifier of the user who created the post
|
|
||||||
*/
|
|
||||||
ownerUserId: string
|
|
||||||
/**
|
|
||||||
* The avatar address of the user who created the post
|
|
||||||
* //TODO: User avatar should be as an attribute and [avatar] should be deleted
|
|
||||||
*/
|
|
||||||
ownerAvatar: string
|
|
||||||
/**
|
/**
|
||||||
* The avatar address of the user who created the post
|
* Post object
|
||||||
|
*
|
||||||
|
* @type {Post}
|
||||||
|
* @memberof IPostComponentProps
|
||||||
*/
|
*/
|
||||||
avatar?: string
|
post: Post
|
||||||
/**
|
|
||||||
* If post is only [0]text, [1]whith picture, ...
|
/**
|
||||||
*/
|
* Owner's post avatar
|
||||||
postTypeId: string
|
*
|
||||||
/**
|
* @type {string}
|
||||||
* The number votes on a post
|
* @memberof IPostComponentProps
|
||||||
*/
|
*/
|
||||||
score: number
|
avatar: string
|
||||||
/**
|
|
||||||
* Array of tags on a post
|
|
||||||
*/
|
|
||||||
tags: string[]
|
|
||||||
/**
|
|
||||||
* The video address of a post
|
|
||||||
*/
|
|
||||||
video: string
|
|
||||||
/**
|
|
||||||
* If it's true comment will be disabled on a post
|
|
||||||
*/
|
|
||||||
disableComments: boolean
|
|
||||||
/**
|
|
||||||
* If it's true sharing will be disabled on a post
|
|
||||||
*/
|
|
||||||
disableSharing: boolean
|
|
||||||
/**
|
|
||||||
* The number of users who has visited the post
|
|
||||||
*/
|
|
||||||
viewCount: boolean
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User full name
|
* User full name
|
||||||
@@ -78,14 +27,6 @@ export interface IPostComponentProps {
|
|||||||
*/
|
*/
|
||||||
fullName?: string
|
fullName?: string
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of comments on the post
|
|
||||||
*
|
|
||||||
* @type {number}
|
|
||||||
* @memberof IPostComponentProps
|
|
||||||
*/
|
|
||||||
commentCount?: number
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of vote on a post
|
* Number of vote on a post
|
||||||
*
|
*
|
||||||
@@ -100,7 +41,7 @@ export interface IPostComponentProps {
|
|||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
* @memberof IPostComponentProps
|
* @memberof IPostComponentProps
|
||||||
*/
|
*/
|
||||||
userVoteStatus?: boolean
|
currentUserVote?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current user is the owner of the post {true} or not {false}
|
* Current user is the owner of the post {true} or not {false}
|
||||||
@@ -158,4 +99,19 @@ export interface IPostComponentProps {
|
|||||||
* @memberof IPostComponentProps
|
* @memberof IPostComponentProps
|
||||||
*/
|
*/
|
||||||
setHomeTitle?: (title: string) => any
|
setHomeTitle?: (title: string) => any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the comments of a post
|
||||||
|
*
|
||||||
|
* @memberof IPostComponentProps
|
||||||
|
*/
|
||||||
|
getPostComments: (ownerUserId: string, postId: string) => any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commnets
|
||||||
|
*
|
||||||
|
* @type {{[commentId: string]: Comment}}
|
||||||
|
* @memberof ICommentGroupComponentProps
|
||||||
|
*/
|
||||||
|
commentList?: {[commentId: string]: Comment}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import UserAvatar from 'components/userAvatar'
|
|||||||
// - Import actions
|
// - Import actions
|
||||||
import * as voteActions from 'actions/voteActions'
|
import * as voteActions from 'actions/voteActions'
|
||||||
import * as postActions from 'actions/postActions'
|
import * as postActions from 'actions/postActions'
|
||||||
|
import * as commentActions from 'actions/commentActions'
|
||||||
import * as globalActions from 'actions/globalActions'
|
import * as globalActions from 'actions/globalActions'
|
||||||
import { IPostComponentProps } from './IPostComponentProps'
|
import { IPostComponentProps } from './IPostComponentProps'
|
||||||
import { IPostComponentState } from './IPostComponentState'
|
import { IPostComponentState } from './IPostComponentState'
|
||||||
@@ -47,80 +48,6 @@ import { IPostComponentState } from './IPostComponentState'
|
|||||||
// - Create component class
|
// - Create component class
|
||||||
export class PostComponent extends Component<IPostComponentProps,IPostComponentState> {
|
export class PostComponent extends Component<IPostComponentProps,IPostComponentState> {
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The context of a post
|
|
||||||
*/
|
|
||||||
body: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* The number of comment on a post
|
|
||||||
*/
|
|
||||||
commentCounter: PropTypes.number,
|
|
||||||
/**
|
|
||||||
* Creation post date
|
|
||||||
*/
|
|
||||||
creationDate: PropTypes.oneOfType([
|
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.number
|
|
||||||
]),
|
|
||||||
/**
|
|
||||||
* Post identifier
|
|
||||||
*/
|
|
||||||
id: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* Post image address
|
|
||||||
*/
|
|
||||||
image: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* The last time date when post has was edited
|
|
||||||
*/
|
|
||||||
lastEditDate: PropTypes.oneOfType([
|
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.number
|
|
||||||
]),
|
|
||||||
/**
|
|
||||||
* The name of the user who created the post
|
|
||||||
*/
|
|
||||||
ownerDisplayName: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* The identifier of the user who created the post
|
|
||||||
*/
|
|
||||||
ownerUserId: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* The avatar address of the user who created the post
|
|
||||||
*/
|
|
||||||
ownerAvatar: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* If post is only [0]text, [1]whith picture, ...
|
|
||||||
*/
|
|
||||||
postTypeId: PropTypes.number,
|
|
||||||
/**
|
|
||||||
* The number votes on a post
|
|
||||||
*/
|
|
||||||
score: PropTypes.number,
|
|
||||||
/**
|
|
||||||
* Array of tags on a post
|
|
||||||
*/
|
|
||||||
tags: PropTypes.array,
|
|
||||||
/**
|
|
||||||
* The video address of a post
|
|
||||||
*/
|
|
||||||
video: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* If it's true comment will be disabled on a post
|
|
||||||
*/
|
|
||||||
disableComments: PropTypes.bool,
|
|
||||||
/**
|
|
||||||
* If it's true sharing will be disabled on a post
|
|
||||||
*/
|
|
||||||
disableSharing: PropTypes.bool,
|
|
||||||
/**
|
|
||||||
* The number of users who has visited the post
|
|
||||||
*/
|
|
||||||
viewCount: PropTypes.number
|
|
||||||
}
|
|
||||||
|
|
||||||
styles = {
|
styles = {
|
||||||
counter: {
|
counter: {
|
||||||
lineHeight: '36px',
|
lineHeight: '36px',
|
||||||
@@ -155,11 +82,12 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
*/
|
*/
|
||||||
constructor (props: IPostComponentProps) {
|
constructor (props: IPostComponentProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
const {post} = props
|
||||||
this.state = {
|
this.state = {
|
||||||
/**
|
/**
|
||||||
* Post text
|
* Post text
|
||||||
*/
|
*/
|
||||||
text: this.props.body,
|
text: post.body!,
|
||||||
/**
|
/**
|
||||||
* It's true if whole the text post is visible
|
* It's true if whole the text post is visible
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +103,11 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
/**
|
/**
|
||||||
* If it's true comment will be disabled on post
|
* If it's true comment will be disabled on post
|
||||||
*/
|
*/
|
||||||
disableComments: this.props.disableComments,
|
disableComments: post.disableComments!,
|
||||||
/**
|
/**
|
||||||
* If it's true share will be disabled on post
|
* If it's true share will be disabled on post
|
||||||
*/
|
*/
|
||||||
disableSharing: this.props.disableSharing,
|
disableSharing: post.disableSharing!,
|
||||||
/**
|
/**
|
||||||
* Title of share post
|
* Title of share post
|
||||||
*/
|
*/
|
||||||
@@ -212,6 +140,11 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
* @param {event} evt passed by clicking on comment slide show
|
* @param {event} evt passed by clicking on comment slide show
|
||||||
*/
|
*/
|
||||||
handleOpenComments = () => {
|
handleOpenComments = () => {
|
||||||
|
const { getPostComments, commentList, post} = this.props
|
||||||
|
const {id, ownerUserId} = post
|
||||||
|
if (!commentList) {
|
||||||
|
getPostComments(ownerUserId!, id!)
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
openComments: !this.state.openComments
|
openComments: !this.state.openComments
|
||||||
})
|
})
|
||||||
@@ -248,7 +181,8 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
* @memberof Post
|
* @memberof Post
|
||||||
*/
|
*/
|
||||||
handleDelete = () => {
|
handleDelete = () => {
|
||||||
this.props.delete!(this.props.id)
|
const {post} = this.props
|
||||||
|
this.props.delete!(post.id!)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -297,7 +231,7 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
* @memberof Post
|
* @memberof Post
|
||||||
*/
|
*/
|
||||||
handleVote = () => {
|
handleVote = () => {
|
||||||
if (this.props.userVoteStatus) {
|
if (this.props.currentUserVote) {
|
||||||
this.props.unvote!()
|
this.props.unvote!()
|
||||||
} else {
|
} else {
|
||||||
this.props.vote!()
|
this.props.vote!()
|
||||||
@@ -330,23 +264,24 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
* @return {react element} return the DOM which rendered by component
|
* @return {react element} return the DOM which rendered by component
|
||||||
*/
|
*/
|
||||||
render () {
|
render () {
|
||||||
|
const {post ,setHomeTitle, goTo, fullName, isPostOwner, commentList, avatar} = this.props
|
||||||
|
|
||||||
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 primaryText='Edit' onClick={this.handleOpenPostWrite} />
|
<MenuItem primaryText='Edit' onClick={this.handleOpenPostWrite} />
|
||||||
<MenuItem primaryText='Delete' onClick={this.handleDelete} />
|
<MenuItem primaryText='Delete' onClick={this.handleDelete} />
|
||||||
<MenuItem primaryText={this.props.disableComments ? 'Enable comments' : 'Disable comments'} onClick={() => this.props.toggleDisableComments!(!this.props.disableComments)} />
|
<MenuItem primaryText={post.disableComments ? 'Enable comments' : 'Disable comments'} onClick={() => this.props.toggleDisableComments!(!post.disableComments)} />
|
||||||
<MenuItem primaryText={this.props.disableSharing ? 'Enable sharing' : 'Disable sharing'} onClick={() => this.props.toggleSharingComments!(!this.props.disableSharing)} />
|
<MenuItem primaryText={post.disableSharing ? 'Enable sharing' : 'Disable sharing'} onClick={() => this.props.toggleSharingComments!(!post.disableSharing)} />
|
||||||
</IconMenu>
|
</IconMenu>
|
||||||
)
|
)
|
||||||
|
|
||||||
const {ownerUserId,setHomeTitle, goTo, ownerDisplayName,creationDate, avatar, fullName, isPostOwner,image, body} = this.props
|
const {ownerUserId, ownerDisplayName, creationDate, image, body} = post
|
||||||
// Define variables
|
// Define variables
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader
|
<CardHeader
|
||||||
title={<NavLink to={`/${ownerUserId}`}>{ownerDisplayName}</NavLink>}
|
title={<NavLink to={`/${ownerUserId}`}>{ownerDisplayName}</NavLink>}
|
||||||
subtitle={moment.unix(creationDate).fromNow() + ' | public'}
|
subtitle={moment.unix(creationDate!).fromNow() + ' | public'}
|
||||||
avatar={<NavLink to={`/${ownerUserId}`}><UserAvatar fullName={fullName!} fileName={avatar!} size={36} /></NavLink>}
|
avatar={<NavLink to={`/${ownerUserId}`}><UserAvatar fullName={fullName!} fileName={avatar!} size={36} /></NavLink>}
|
||||||
>
|
>
|
||||||
{isPostOwner ? ( <div style={this.styles.rightIconMenu as any}><RightIconMenu /></div>) : ''}
|
{isPostOwner ? ( <div style={this.styles.rightIconMenu as any}><RightIconMenu /></div>) : ''}
|
||||||
@@ -384,25 +319,25 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
checkedIcon={<SvgFavorite style={{fill: '#4CAF50'}}/>}
|
checkedIcon={<SvgFavorite style={{fill: '#4CAF50'}}/>}
|
||||||
uncheckedIcon={<SvgFavoriteBorder style={{fill: '#757575'}} />}
|
uncheckedIcon={<SvgFavoriteBorder style={{fill: '#757575'}} />}
|
||||||
defaultChecked={this.props.userVoteStatus}
|
checked={this.props.currentUserVote}
|
||||||
style={{transform: 'translate(6px, 6px)'}}
|
style={{transform: 'translate(6px, 6px)'}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div style={this.styles.counter}> {this.props.voteCount! > 0 ? this.props.voteCount : ''} </div>
|
<div style={this.styles.counter}> {this.props.voteCount! > 0 ? this.props.voteCount : ''} </div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex' }}>
|
<div style={{ display: 'flex' }}>
|
||||||
{!this.props.disableComments ? (<div style={{display: 'inherit'}}><FloatingActionButton onClick={this.handleOpenComments} style={{ margin: '0 8px' }} backgroundColor={grey200} iconStyle={{ color: grey600, fill: grey600, height: '36px', width: '36px' }} zDepth={1} secondary={false}>
|
{!post.disableComments ? (<div style={{display: 'inherit'}}><FloatingActionButton onClick={this.handleOpenComments} style={{ margin: '0 8px' }} backgroundColor={grey200} iconStyle={{ color: grey600, fill: grey600, height: '36px', width: '36px' }} zDepth={1} secondary={false}>
|
||||||
<SvgComment viewBox='0 -9 24 34' style={{ height: '30px', width: '30px' }} /> 3
|
<SvgComment viewBox='0 -9 24 34' style={{ height: '30px', width: '30px' }} /> 3
|
||||||
</FloatingActionButton>
|
</FloatingActionButton>
|
||||||
<div style={this.styles.counter}>{this.props.commentCount! > 0 ? this.props.commentCount : ''} </div></div>) : ''}
|
<div style={this.styles.counter}>{post.commentCounter! > 0 ? post.commentCounter : ''} </div></div>) : ''}
|
||||||
{!this.props.disableSharing ? (<FloatingActionButton onClick={this.handleOpenShare} style={{ margin: '0 8px' }} backgroundColor={grey200} iconStyle={{ color: grey600, fill: grey600, height: '36px', width: '36px' }} zDepth={1} secondary={false}>
|
{!post.disableSharing ? (<FloatingActionButton onClick={this.handleOpenShare} style={{ margin: '0 8px' }} backgroundColor={grey200} iconStyle={{ color: grey600, fill: grey600, height: '36px', width: '36px' }} zDepth={1} secondary={false}>
|
||||||
<SvgShare viewBox='0 -9 24 34' style={{ height: '30px', width: '30px' }} />
|
<SvgShare viewBox='0 -9 24 34' style={{ height: '30px', width: '30px' }} />
|
||||||
</FloatingActionButton>) : ''}
|
</FloatingActionButton>) : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardActions>
|
</CardActions>
|
||||||
|
|
||||||
<CommentGroup open={this.state.openComments} ownerPostUserId={this.props.ownerUserId} onToggleRequest={this.handleOpenComments} isPostOwner={this.props.isPostOwner!} disableComments={this.props.disableComments} postId={this.props.id} />
|
<CommentGroup open={this.state.openComments} comments={commentList} ownerPostUserId={post.ownerUserId!} onToggleRequest={this.handleOpenComments} isPostOwner={this.props.isPostOwner!} disableComments={post.disableComments!} postId={post.id!} />
|
||||||
|
|
||||||
{/* Copy link dialog*/}
|
{/* Copy link dialog*/}
|
||||||
<Dialog
|
<Dialog
|
||||||
@@ -421,7 +356,7 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
<MenuItem primaryText='Copy Link' leftIcon={<SvgLink />} onClick={this.handleCopyLink} />
|
<MenuItem primaryText='Copy Link' leftIcon={<SvgLink />} onClick={this.handleCopyLink} />
|
||||||
</Menu>
|
</Menu>
|
||||||
</Paper>)
|
</Paper>)
|
||||||
: <TextField fullWidth={true} id='text-field-default' defaultValue={`${location.origin}/${this.props.ownerUserId}/posts/${this.props.id}`} />
|
: <TextField fullWidth={true} id='text-field-default' defaultValue={`${location.origin}/${post.ownerUserId}/posts/${post.id}`} />
|
||||||
}
|
}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
@@ -429,11 +364,7 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
open={this.state.openPostWrite}
|
open={this.state.openPostWrite}
|
||||||
onRequestClose={this.handleClosePostWrite}
|
onRequestClose={this.handleClosePostWrite}
|
||||||
edit={true}
|
edit={true}
|
||||||
text= {this.props.body}
|
postModel= {post}
|
||||||
image= {this.props.image ? this.props.image : ''}
|
|
||||||
id= {this.props.id}
|
|
||||||
disableComments= {this.props.disableComments}
|
|
||||||
disableSharing= {this.props.disableSharing}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Card>
|
</Card>
|
||||||
@@ -449,14 +380,22 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
|
|||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
|
const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
|
||||||
|
const {post} = ownProps
|
||||||
return {
|
return {
|
||||||
vote: () => dispatch(voteActions.dbAddVote(ownProps.id,ownProps.ownerUserId)),
|
vote: () => dispatch(voteActions.dbAddVote(post.id!,post.ownerUserId!)),
|
||||||
unvote: () => dispatch(voteActions.dbDeleteVote(ownProps.id)) ,
|
unvote: () => dispatch(voteActions.dbDeleteVote(post.id!, post.ownerUserId!)) ,
|
||||||
delete: (id: string) => dispatch(postActions.dbDeletePost(id)),
|
delete: (id: string) => dispatch(postActions.dbDeletePost(id)),
|
||||||
toggleDisableComments: (status: boolean) => dispatch(postActions.dbUpdatePost({id: ownProps.id, disableComments: status}, (x: any) => x)),
|
toggleDisableComments: (status: boolean) => {
|
||||||
toggleSharingComments: (status: boolean) => dispatch(postActions.dbUpdatePost({id: ownProps.id, disableSharing: status},(x: any) => x)),
|
post.disableComments = status
|
||||||
|
dispatch(postActions.dbUpdatePost(post, (x: any) => x))
|
||||||
|
},
|
||||||
|
toggleSharingComments: (status: boolean) => {
|
||||||
|
post.disableSharing = status
|
||||||
|
dispatch(postActions.dbUpdatePost({id: post.id!, disableSharing: status},(x: any) => x))
|
||||||
|
},
|
||||||
goTo: (url: string) => dispatch(push(url)),
|
goTo: (url: string) => dispatch(push(url)),
|
||||||
setHomeTitle: (title: string) => dispatch(globalActions.setHeaderTitle(title || ''))
|
setHomeTitle: (title: string) => dispatch(globalActions.setHeaderTitle(title || '')),
|
||||||
|
getPostComments: (ownerUserId: string, postId: string) => dispatch(commentActions.dbGetComments(ownerUserId,postId))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -468,17 +407,20 @@ const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
|
|||||||
* @return {object} props of component
|
* @return {object} props of component
|
||||||
*/
|
*/
|
||||||
const mapStateToProps = (state: any, ownProps: IPostComponentProps) => {
|
const mapStateToProps = (state: any, ownProps: IPostComponentProps) => {
|
||||||
const {uid} = state.authorize
|
const {post, vote, authorize, comment} = state
|
||||||
let votes = state.vote.postVotes[ownProps.id]
|
const {uid} = authorize
|
||||||
const post = (state.post.userPosts[uid] ? Object.keys(state.post.userPosts[uid]).filter((key) => { return ownProps.id === key }).length : 0)
|
let currentUserVote = post.votes ? post.votes[uid] : false
|
||||||
|
const postModel = post.userPosts[ownProps.post.ownerUserId!][ownProps.post.id!]
|
||||||
|
const postOwner = (post.userPosts[uid] ? Object.keys(post.userPosts[uid]).filter((key) => { return ownProps.post.id === key }).length : 0)
|
||||||
|
const commentList: { [commentId: string]: Comment } = comment.postComments[ownProps.post.id!]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
avatar: state.user.info && state.user.info[ownProps.ownerUserId] ? state.user.info[ownProps.ownerUserId].avatar || '' : '',
|
commentList,
|
||||||
fullName: state.user.info && state.user.info[ownProps.ownerUserId] ? state.user.info[ownProps.ownerUserId].fullName || '' : '',
|
avatar: state.user.info && state.user.info[ownProps.post.ownerUserId!] ? state.user.info[ownProps.post.ownerUserId!].avatar || '' : '',
|
||||||
commentCount: state.comment.postComments[ownProps.id] ? Object.keys(state.comment.postComments[ownProps.id]).length : 0,
|
fullName: state.user.info && state.user.info[ownProps.post.ownerUserId!] ? state.user.info[ownProps.post.ownerUserId!].fullName || '' : '',
|
||||||
voteCount: state.vote.postVotes[ownProps.id] ? Object.keys(state.vote.postVotes[ownProps.id]).length : 0,
|
voteCount: postModel.score,
|
||||||
userVoteStatus: votes && Object.keys(votes).filter((key) => votes[key].userId === state.authorize.uid)[0] ? true : false,
|
currentUserVote,
|
||||||
isPostOwner: post > 0
|
isPostOwner: postOwner > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,39 +45,17 @@ export interface IPostWriteComponentProps {
|
|||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof IPostWriteComponentProps
|
* @memberof IPostWriteComponentProps
|
||||||
*/
|
*/
|
||||||
avatar?: string
|
ownerAvatar?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User name
|
* The post owner name
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof IPostWriteComponentProps
|
|
||||||
*/
|
*/
|
||||||
name?: string
|
ownerDisplayName: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post image full path
|
* Post model
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof IPostWriteComponentProps
|
|
||||||
*/
|
*/
|
||||||
imageFullPath?: string
|
postModel: Post
|
||||||
|
|
||||||
/**
|
|
||||||
* Comment on the post is disabled {true} or not {false}
|
|
||||||
*
|
|
||||||
* @type {boolean}
|
|
||||||
* @memberof IPostWriteComponentProps
|
|
||||||
*/
|
|
||||||
disableComments?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sharing on a post is disabled {true} or not {false}
|
|
||||||
*
|
|
||||||
* @type {boolean}
|
|
||||||
* @memberof IPostWriteComponentProps
|
|
||||||
*/
|
|
||||||
disableSharing?: boolean
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a post
|
* Save a post
|
||||||
|
|||||||
@@ -34,37 +34,6 @@ import { Post } from 'core/domain/posts'
|
|||||||
// - Create PostWrite component class
|
// - Create PostWrite component class
|
||||||
export class PostWriteComponent extends Component<IPostWriteComponentProps,IPostWriteComponentState> {
|
export class PostWriteComponent extends Component<IPostWriteComponentProps,IPostWriteComponentState> {
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
/**
|
|
||||||
* If it's true post writing page will be open
|
|
||||||
*/
|
|
||||||
open: PropTypes.bool,
|
|
||||||
/**
|
|
||||||
* Recieve request close function
|
|
||||||
*/
|
|
||||||
onRequestClose: PropTypes.func,
|
|
||||||
/**
|
|
||||||
* Post write style
|
|
||||||
*/
|
|
||||||
style: PropTypes.object,
|
|
||||||
/**
|
|
||||||
* If it's true, post will be in edit view
|
|
||||||
*/
|
|
||||||
edit: PropTypes.bool.isRequired,
|
|
||||||
/**
|
|
||||||
* The text of post in editing state
|
|
||||||
*/
|
|
||||||
text: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* The image of post in editing state
|
|
||||||
*/
|
|
||||||
image: PropTypes.string,
|
|
||||||
/**
|
|
||||||
* If post state is editing this id sould be filled with post identifier
|
|
||||||
*/
|
|
||||||
id: PropTypes.string
|
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Component constructor
|
* Component constructor
|
||||||
* @param {object} props is an object properties of component
|
* @param {object} props is an object properties of component
|
||||||
@@ -73,6 +42,8 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
|
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
|
const {postModel} = props
|
||||||
|
|
||||||
// Default state
|
// Default state
|
||||||
this.state = {
|
this.state = {
|
||||||
/**
|
/**
|
||||||
@@ -86,7 +57,7 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
/**
|
/**
|
||||||
* The path identifier of image on the server
|
* The path identifier of image on the server
|
||||||
*/
|
*/
|
||||||
imageFullPath: this.props.edit ? this.props.imageFullPath! : '',
|
imageFullPath: this.props.edit ? (postModel.imageFullPath ? postModel.imageFullPath! : '' ) : '',
|
||||||
/**
|
/**
|
||||||
* If it's true gallery will be open
|
* If it's true gallery will be open
|
||||||
*/
|
*/
|
||||||
@@ -98,11 +69,11 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
/**
|
/**
|
||||||
* If it's true comment will be disabled on post
|
* If it's true comment will be disabled on post
|
||||||
*/
|
*/
|
||||||
disableComments: this.props.edit ? this.props.disableComments! : false,
|
disableComments: this.props.edit ? postModel.disableComments! : false,
|
||||||
/**
|
/**
|
||||||
* If it's true share will be disabled on post
|
* If it's true share will be disabled on post
|
||||||
*/
|
*/
|
||||||
disableSharing: this.props.edit ? this.props.disableSharing! : false
|
disableSharing: this.props.edit ? postModel.disableSharing! : false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +125,7 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
this.setState({
|
this.setState({
|
||||||
image: '',
|
image: '',
|
||||||
imageFullPath: '',
|
imageFullPath: '',
|
||||||
disabledPost: false
|
disabledPost: this.state.postText.trim() === ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +134,6 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
* @param {event} evt passed by clicking on the post button
|
* @param {event} evt passed by clicking on the post button
|
||||||
*/
|
*/
|
||||||
handlePost = () => {
|
handlePost = () => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
image,
|
image,
|
||||||
imageFullPath,
|
imageFullPath,
|
||||||
@@ -172,13 +142,21 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
postText } = this.state
|
postText } = this.state
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
avatar,
|
ownerAvatar,
|
||||||
name,
|
ownerDisplayName,
|
||||||
edit,
|
edit,
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
post,
|
post,
|
||||||
update } = this.props
|
update,
|
||||||
|
postModel
|
||||||
|
} = this.props
|
||||||
|
if (image === '' && postText.trim() === '') {
|
||||||
|
this.setState({
|
||||||
|
disabledPost: false
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let tags = PostAPI.getContentTags(postText!)
|
let tags = PostAPI.getContentTags(postText!)
|
||||||
|
|
||||||
@@ -190,8 +168,8 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
tags: tags,
|
tags: tags,
|
||||||
image: image,
|
image: image,
|
||||||
imageFullPath: imageFullPath,
|
imageFullPath: imageFullPath,
|
||||||
ownerAvatar: avatar,
|
ownerAvatar: ownerAvatar,
|
||||||
ownerDisplayName: name,
|
ownerDisplayName: ownerDisplayName,
|
||||||
disableComments: disableComments,
|
disableComments: disableComments,
|
||||||
disableSharing: disableSharing,
|
disableSharing: disableSharing,
|
||||||
postTypeId: 1,
|
postTypeId: 1,
|
||||||
@@ -202,8 +180,8 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
post!({
|
post!({
|
||||||
body: postText,
|
body: postText,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
ownerAvatar: avatar,
|
ownerAvatar: ownerAvatar,
|
||||||
ownerDisplayName: name,
|
ownerDisplayName: ownerDisplayName,
|
||||||
disableComments: disableComments,
|
disableComments: disableComments,
|
||||||
disableSharing: disableSharing,
|
disableSharing: disableSharing,
|
||||||
postTypeId: 0,
|
postTypeId: 0,
|
||||||
@@ -212,15 +190,14 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
}, onRequestClose)
|
}, onRequestClose)
|
||||||
}
|
}
|
||||||
} else { // In edit status we pass post to update functions
|
} else { // In edit status we pass post to update functions
|
||||||
update!({
|
postModel.body = postText
|
||||||
id: id,
|
postModel.tags = tags
|
||||||
body: postText,
|
postModel.image = image
|
||||||
tags: tags,
|
postModel.imageFullPath = imageFullPath
|
||||||
image: image,
|
postModel.disableComments = disableComments
|
||||||
imageFullPath: imageFullPath,
|
postModel.disableSharing = disableSharing
|
||||||
disableComments: disableComments,
|
|
||||||
disableSharing: disableSharing
|
update!(postModel, onRequestClose)
|
||||||
}, onRequestClose)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,6 +253,7 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
|
|
||||||
componentWillReceiveProps (nextProps: IPostWriteComponentProps) {
|
componentWillReceiveProps (nextProps: IPostWriteComponentProps) {
|
||||||
if (!nextProps.open) {
|
if (!nextProps.open) {
|
||||||
|
const {postModel} = this.props
|
||||||
this.setState({
|
this.setState({
|
||||||
/**
|
/**
|
||||||
* Post text
|
* Post text
|
||||||
@@ -296,11 +274,11 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
/**
|
/**
|
||||||
* If it's true comment will be disabled on post
|
* If it's true comment will be disabled on post
|
||||||
*/
|
*/
|
||||||
disableComments: this.props.edit ? this.props.disableComments! : false,
|
disableComments: this.props.edit ? postModel.disableComments! : false,
|
||||||
/**
|
/**
|
||||||
* If it's true share will be disabled on post
|
* If it's true share will be disabled on post
|
||||||
*/
|
*/
|
||||||
disableSharing: this.props.edit ? this.props.disableSharing! : false
|
disableSharing: this.props.edit ? postModel.disableSharing! : false
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -328,7 +306,7 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
<MenuItem onClick={this.handleToggleSharing} style={{ fontSize: '14px' }}>{!this.state.disableSharing ? 'Disable sharing' : 'Enable sharing'}</MenuItem>
|
<MenuItem onClick={this.handleToggleSharing} style={{ fontSize: '14px' }}>{!this.state.disableSharing ? 'Disable sharing' : 'Enable sharing'}</MenuItem>
|
||||||
</IconMenu>
|
</IconMenu>
|
||||||
)
|
)
|
||||||
let postAvatar = <UserAvatarComponent fullName={this.props.name!} fileName={this.props.avatar!} style={{ top: '8px' }} size={40} />
|
let postAvatar = <UserAvatarComponent fullName={this.props.ownerDisplayName!} fileName={this.props.ownerAvatar!} style={{ top: '8px' }} size={40} />
|
||||||
|
|
||||||
let author = (
|
let author = (
|
||||||
<div>
|
<div>
|
||||||
@@ -341,7 +319,7 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
paddingLeft: '50px',
|
paddingLeft: '50px',
|
||||||
lineHeight: '25px'
|
lineHeight: '25px'
|
||||||
}}>{this.props.name}</span><span style={{
|
}}>{this.props.ownerDisplayName}</span><span style={{
|
||||||
fontWeight: 100,
|
fontWeight: 100,
|
||||||
fontSize: '10px'
|
fontSize: '10px'
|
||||||
}}> | Public</span>
|
}}> | Public</span>
|
||||||
|
|||||||
@@ -161,23 +161,7 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
|
|||||||
<div key={post.id}>
|
<div key={post.id}>
|
||||||
|
|
||||||
{index > 1 || (!postBack.divided && index > 0) ? <div style={{ height: '16px' }}></div> : ''}
|
{index > 1 || (!postBack.divided && index > 0) ? <div style={{ height: '16px' }}></div> : ''}
|
||||||
<PostComponent
|
<PostComponent post={post} />
|
||||||
body={post.body}
|
|
||||||
commentCounter={post.commentCounter}
|
|
||||||
creationDate={post.creationDate}
|
|
||||||
id={post.id}
|
|
||||||
image={post.image}
|
|
||||||
lastEditDate={post.lastEditDate}
|
|
||||||
ownerDisplayName={post.ownerDisplayName}
|
|
||||||
ownerUserId={post.ownerUserId}
|
|
||||||
ownerAvatar={post.ownerAvatar}
|
|
||||||
postTypeId={post.postTypeId}
|
|
||||||
score={post.score}
|
|
||||||
tags={post.tags}
|
|
||||||
video={post.video}
|
|
||||||
disableComments={post.disableComments}
|
|
||||||
disableSharing={post.disableSharing}
|
|
||||||
viewCount={posts.viewCount} />
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ export class UserAvatarComponent extends Component<IUserAvatarComponentProps,IUs
|
|||||||
/**
|
/**
|
||||||
* Use for getting url address from server
|
* Use for getting url address from server
|
||||||
*/
|
*/
|
||||||
fileName: PropTypes.string.isRequired,
|
fileName: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* User full name
|
* User full name
|
||||||
*/
|
*/
|
||||||
fullName: PropTypes.string.isRequired,
|
fullName: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* Avatar style
|
* Avatar style
|
||||||
*/
|
*/
|
||||||
@@ -70,7 +70,7 @@ export class UserAvatarComponent extends Component<IUserAvatarComponentProps,IUs
|
|||||||
<div style={{display: 'inherit'}}>
|
<div style={{display: 'inherit'}}>
|
||||||
{(fileName && fileName !== '' && fileName !== 'noImage' )
|
{(fileName && fileName !== '' && fileName !== 'noImage' )
|
||||||
? ( <Avatar backgroundColor='#ffffff' src={fileName} size={size || 36} style={style} onTouchTap={onTouchTap} />)
|
? ( <Avatar backgroundColor='#ffffff' src={fileName} size={size || 36} style={style} onTouchTap={onTouchTap} />)
|
||||||
: (<Avatar backgroundColor='#00bcd4' size={size || 36} style={style} onTouchTap={onTouchTap}>{fullName.slice(0, 1)}</Avatar>) }
|
: (<Avatar backgroundColor='#00bcd4' size={size || 36} style={style} onTouchTap={onTouchTap}>{fullName ? fullName.slice(0, 1) : ''}</Avatar>) }
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,14 @@ import { BaseDomain } from 'core/domain/common'
|
|||||||
|
|
||||||
export class Notification extends BaseDomain {
|
export class Notification extends BaseDomain {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification identifier
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Notification
|
||||||
|
*/
|
||||||
|
public id?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description of notification
|
* Description of notification
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BaseDomain } from 'core/domain/common'
|
import { BaseDomain } from 'core/domain/common'
|
||||||
|
import { Comment } from 'core/domain/comments'
|
||||||
export class Post extends BaseDomain {
|
export class Post extends BaseDomain {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,6 +42,14 @@ export class Post extends BaseDomain {
|
|||||||
*/
|
*/
|
||||||
public score?: number
|
public score?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of voter identifier
|
||||||
|
*
|
||||||
|
* @type {{[voterId: string]: boolean}}
|
||||||
|
* @memberof Post
|
||||||
|
*/
|
||||||
|
votes?: {[voterId: string]: boolean}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post view count
|
* Post view count
|
||||||
*
|
*
|
||||||
@@ -50,6 +58,14 @@ export class Post extends BaseDomain {
|
|||||||
*/
|
*/
|
||||||
public viewCount?: number
|
public viewCount?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store three last comments to show in slide preview comment
|
||||||
|
*
|
||||||
|
* @type {{[commentId: string]: Comment}}
|
||||||
|
* @memberof Post
|
||||||
|
*/
|
||||||
|
comments?: {[commentId: string]: Comment}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The text of post
|
* The text of post
|
||||||
*
|
*
|
||||||
@@ -136,7 +152,7 @@ export class Post extends BaseDomain {
|
|||||||
* @type {Boolean}
|
* @type {Boolean}
|
||||||
* @memberof Post
|
* @memberof Post
|
||||||
*/
|
*/
|
||||||
public disableComments?: Boolean
|
public disableComments?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If sharing post is disabled {true} or not {false}
|
* If sharing post is disabled {true} or not {false}
|
||||||
@@ -144,7 +160,7 @@ export class Post extends BaseDomain {
|
|||||||
* @type {Boolean}
|
* @type {Boolean}
|
||||||
* @memberof Post
|
* @memberof Post
|
||||||
*/
|
*/
|
||||||
public disableSharing?: Boolean
|
public disableSharing?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the post is deleted {true} or not false
|
* If the post is deleted {true} or not false
|
||||||
@@ -152,6 +168,6 @@ export class Post extends BaseDomain {
|
|||||||
* @type {Boolean}
|
* @type {Boolean}
|
||||||
* @memberof Post
|
* @memberof Post
|
||||||
*/
|
*/
|
||||||
public deleted?: Boolean
|
public deleted?: boolean
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ export class Profile extends BaseDomain {
|
|||||||
public fullName: string,
|
public fullName: string,
|
||||||
public banner: string,
|
public banner: string,
|
||||||
public tagLine: string,
|
public tagLine: string,
|
||||||
public email?: string | null) {
|
public creationDate: number,
|
||||||
|
public email?: string | null
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
UserService,
|
UserService,
|
||||||
VoteService,
|
VoteService,
|
||||||
StorageService
|
StorageService
|
||||||
} from 'data/firebaseClient/services'
|
} from 'data/firestoreClient/services'
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import { Comment } from 'core/domain/comments'
|
|||||||
*/
|
*/
|
||||||
export interface ICommentService {
|
export interface ICommentService {
|
||||||
|
|
||||||
addComment: (postId: string, comment: Comment) => Promise<string>
|
addComment: (comment: Comment) => Promise<string>
|
||||||
getComments: (callback: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void) => void
|
getComments: (postId: string, callback: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void) => void
|
||||||
updateComment: (commentId: string, postId: string, comment: Comment) => Promise<void>
|
updateComment: (comment: Comment) => Promise<void>
|
||||||
deleteComment: (commentId: string, postId: string) => Promise<void>
|
deleteComment: (commentId: string, postId: string) => Promise<void>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import { Post } from 'core/domain/posts'
|
|||||||
* @interface IPostService
|
* @interface IPostService
|
||||||
*/
|
*/
|
||||||
export interface IPostService {
|
export interface IPostService {
|
||||||
addPost: (userId: string, post: Post) => Promise<string>
|
addPost: (post: Post) => Promise<string>
|
||||||
updatePost: (userId: string, postId: string, post: Post) => Promise<void>
|
updatePost: (post: Post) => Promise<void>
|
||||||
deletePost: (userId: string,postId: string) => Promise<void>
|
deletePost: (postId: string) => Promise<void>
|
||||||
getPosts: (userId: string) => Promise<{ [postId: string]: Post }>
|
getPosts: (userId: string) => Promise<{ [postId: string]: Post }>
|
||||||
getPostById: (userId: string, postId: string) => Promise<Post>
|
getPostById: (postId: string) => Promise<Post>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ import { User, Profile } from 'core/domain/users'
|
|||||||
export interface IUserService {
|
export interface IUserService {
|
||||||
getUserProfile: (userId: string) => Promise<Profile>
|
getUserProfile: (userId: string) => Promise<Profile>
|
||||||
updateUserProfile: (userId: string, profile: Profile) => Promise<void>
|
updateUserProfile: (userId: string, profile: Profile) => Promise<void>
|
||||||
getUsersProfile: (userId: string) => Promise<{[userId: string]: Profile}>
|
getUsersProfile: (userId: string, lastKey?: string, numberOfItems?: number) => Promise<{[userId: string]: Profile}>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ import { Vote } from 'core/domain/votes'
|
|||||||
*/
|
*/
|
||||||
export interface IVoteService {
|
export interface IVoteService {
|
||||||
addVote: (vote: Vote) => Promise<string>
|
addVote: (vote: Vote) => Promise<string>
|
||||||
getVotes: () => Promise<{[postId: string]: {[voteId: string]: Vote}}>
|
getVotes: (postId: string) => Promise<{[postId: string]: {[voteId: string]: Vote}}>
|
||||||
deleteVote: (voteId: string, postId: string) => Promise<void>
|
deleteVote: (vote: Vote) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
declare const process: any
|
|
||||||
|
|
||||||
import firebase from 'firebase'
|
import firebase from 'firebase'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let config = {
|
let config = {
|
||||||
apiKey: process.env.API_KEY,
|
apiKey: process.env.API_KEY,
|
||||||
@@ -12,7 +9,6 @@ try {
|
|||||||
messagingSenderId: process.env.MESSAGING_SENDER_ID
|
messagingSenderId: process.env.MESSAGING_SENDER_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(firebase)
|
|
||||||
firebase.initializeApp(config)
|
firebase.initializeApp(config)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('=========Firebase initializer==============')
|
console.log('=========Firebase initializer==============')
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
import { Profile } from 'core/domain/users'
|
||||||
|
|
||||||
// - Import react components
|
// - Import react components
|
||||||
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
|
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
|
||||||
@@ -68,8 +71,8 @@ export class AuthorizeService implements IAuthorizeService {
|
|||||||
firebaseAuth()
|
firebaseAuth()
|
||||||
.createUserWithEmailAndPassword(user.email as string, user.password as string)
|
.createUserWithEmailAndPassword(user.email as string, user.password as string)
|
||||||
.then((signupResult) => {
|
.then((signupResult) => {
|
||||||
const {uid, email, displayName, photoURL} = signupResult
|
const {uid, email} = signupResult
|
||||||
this.storeUserInformation(uid,email,displayName,photoURL).then(resolve)
|
this.storeUserInformation(uid,email,user.fullName,'').then(resolve)
|
||||||
})
|
})
|
||||||
.catch((error: any) => reject(new SocialError(error.code, error.message)))
|
.catch((error: any) => reject(new SocialError(error.code, error.message)))
|
||||||
})
|
})
|
||||||
@@ -107,7 +110,7 @@ export class AuthorizeService implements IAuthorizeService {
|
|||||||
firebaseAuth().onAuthStateChanged( (user: any) => {
|
firebaseAuth().onAuthStateChanged( (user: any) => {
|
||||||
let isVerifide = false
|
let isVerifide = false
|
||||||
if (user) {
|
if (user) {
|
||||||
if (user.emailVerified) {
|
if (user.emailVerified || user.providerData[0].providerId.trim() !== 'password') {
|
||||||
isVerifide = true
|
isVerifide = true
|
||||||
} else {
|
} else {
|
||||||
isVerifide = false
|
isVerifide = false
|
||||||
@@ -210,15 +213,17 @@ export class AuthorizeService implements IAuthorizeService {
|
|||||||
* @private
|
* @private
|
||||||
* @memberof AuthorizeService
|
* @memberof AuthorizeService
|
||||||
*/
|
*/
|
||||||
private storeUserInformation = (userId: string, email: string, fullName: string, avatar?: string) => {
|
private storeUserInformation = (userId: string, email: string, fullName: string, avatar: string) => {
|
||||||
return new Promise<RegisterUserResult>((resolve,reject) => {
|
return new Promise<RegisterUserResult>((resolve,reject) => {
|
||||||
firebaseRef.child(`users/${userId}/info`)
|
firebaseRef.child(`users/${userId}/info`)
|
||||||
.set({
|
.set(new Profile(
|
||||||
userId,
|
|
||||||
avatar,
|
avatar,
|
||||||
email,
|
fullName,
|
||||||
fullName
|
'',
|
||||||
})
|
'',
|
||||||
|
moment().unix(),
|
||||||
|
email
|
||||||
|
))
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
resolve(new RegisterUserResult(userId))
|
resolve(new RegisterUserResult(userId))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// - Import react components
|
// - Import react components
|
||||||
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
|
import { firebaseRef, firebaseAuth, db } from 'data/firebaseClient'
|
||||||
|
|
||||||
import { SocialError } from 'core/domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
import { ICircleService } from 'core/services/circles'
|
import { ICircleService } from 'core/services/circles'
|
||||||
@@ -18,13 +18,11 @@ export class CircleService implements ICircleService {
|
|||||||
public addCircle: (userId: string, circle: Circle)
|
public addCircle: (userId: string, circle: Circle)
|
||||||
=> Promise<string> = (userId, circle) => {
|
=> Promise<string> = (userId, circle) => {
|
||||||
return new Promise<string>((resolve,reject) => {
|
return new Promise<string>((resolve,reject) => {
|
||||||
|
|
||||||
let circleRef = firebaseRef.child(`users/${userId}/circles`).push(circle)
|
let circleRef = firebaseRef.child(`users/${userId}/circles`).push(circle)
|
||||||
circleRef.then(() => {
|
circleRef.then(() => {
|
||||||
resolve(circleRef.key as string)
|
resolve(circleRef.key as string)
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
|
||||||
reject(new SocialError(error.code, error.message))
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// - Import react components
|
// - Import react components
|
||||||
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
|
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
import { SocialError } from 'core/domain/common'
|
import { SocialError } from 'core/domain/common'
|
||||||
import { Profile, UserProvider } from 'core/domain/users'
|
import { Profile, UserProvider } from 'core/domain/users'
|
||||||
@@ -13,6 +14,7 @@ import { IUserService } from 'core/services/users'
|
|||||||
* @implements {IUserService}
|
* @implements {IUserService}
|
||||||
*/
|
*/
|
||||||
export class UserService implements IUserService {
|
export class UserService implements IUserService {
|
||||||
|
|
||||||
public getUserProfile: (userId: string)
|
public getUserProfile: (userId: string)
|
||||||
=> Promise<Profile> = (userId) => {
|
=> Promise<Profile> = (userId) => {
|
||||||
return new Promise<Profile>((resolve, reject) => {
|
return new Promise<Profile>((resolve, reject) => {
|
||||||
@@ -23,7 +25,7 @@ export class UserService implements IUserService {
|
|||||||
if (Object.keys(userProfile).length === 0 && userProfile.constructor === Object) {
|
if (Object.keys(userProfile).length === 0 && userProfile.constructor === Object) {
|
||||||
this.getUserProviderData(userId).then((providerData: UserProvider) => {
|
this.getUserProviderData(userId).then((providerData: UserProvider) => {
|
||||||
const {avatar,fullName, email} = providerData
|
const {avatar,fullName, email} = providerData
|
||||||
const userProfile = new Profile(avatar,fullName,'','',email)
|
const userProfile = new Profile(avatar,fullName,'','', moment().unix(),email)
|
||||||
resolve(userProfile)
|
resolve(userProfile)
|
||||||
this.updateUserProfile(userId,userProfile)
|
this.updateUserProfile(userId,userProfile)
|
||||||
})
|
})
|
||||||
@@ -52,10 +54,16 @@ export class UserService implements IUserService {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
public getUsersProfile: (userId: string)
|
public getUsersProfile: (userId: string, page?: number, lastKey?: string)
|
||||||
=> Promise<{ [userId: string]: Profile }> = (userId) => {
|
=> Promise<{ [userId: string]: Profile }> = (userId, page, lastKey) => {
|
||||||
return new Promise<{ [userId: string]: Profile }>((resolve, reject) => {
|
return new Promise<{ [userId: string]: Profile }>((resolve, reject) => {
|
||||||
let usersProfileRef: any = firebaseRef.child(`users`)
|
let usersProfileRef: any
|
||||||
|
if (page) {
|
||||||
|
const numberOfItems = (page * 12) + 12
|
||||||
|
usersProfileRef = firebaseRef.child(`users`).orderByKey().startAt(lastKey!).limitToFirst(numberOfItems)
|
||||||
|
} else {
|
||||||
|
usersProfileRef = firebaseRef.child(`users`).orderByKey()
|
||||||
|
}
|
||||||
|
|
||||||
usersProfileRef.once('value').then((snapshot: any) => {
|
usersProfileRef.once('value').then((snapshot: any) => {
|
||||||
let usersProfile: any = snapshot.val() || {}
|
let usersProfile: any = snapshot.val() || {}
|
||||||
|
|||||||
31
src/data/firestoreClient/index.ts
Normal file
31
src/data/firestoreClient/index.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import firebase from 'firebase'
|
||||||
|
import 'firebase/firestore'
|
||||||
|
try {
|
||||||
|
let config = {
|
||||||
|
apiKey: process.env.API_KEY,
|
||||||
|
authDomain: process.env.AUTH_DOMAIN,
|
||||||
|
databaseURL: process.env.DATABASE_URL,
|
||||||
|
projectId: process.env.PROJECT_ID,
|
||||||
|
storageBucket: process.env.STORAGE_BUCKET,
|
||||||
|
messagingSenderId: process.env.MESSAGING_SENDER_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
firebase.initializeApp(config)
|
||||||
|
} catch (error) {
|
||||||
|
console.log('=========Firebase firestore initializer==============')
|
||||||
|
console.log(error)
|
||||||
|
console.log('====================================')
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Storage reference
|
||||||
|
export let storageRef = firebase.storage().ref()
|
||||||
|
|
||||||
|
// Initialize Cloud Firestore through Firebase
|
||||||
|
export const db = firebase.firestore()
|
||||||
|
|
||||||
|
// - Database authorize
|
||||||
|
export let firebaseAuth = firebase.auth
|
||||||
|
export let firebaseRef = firebase.database().ref()
|
||||||
|
|
||||||
|
// - Firebase default
|
||||||
|
export default firebase
|
||||||
264
src/data/firestoreClient/services/authorize/AuthorizeService.ts
Normal file
264
src/data/firestoreClient/services/authorize/AuthorizeService.ts
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
import { Profile } from 'core/domain/users'
|
||||||
|
|
||||||
|
// - Import react components
|
||||||
|
import { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
|
||||||
|
|
||||||
|
import { IAuthorizeService } from 'core/services/authorize'
|
||||||
|
import { User, UserProvider } from 'core/domain/users'
|
||||||
|
import { LoginUser, RegisterUserResult } from 'core/domain/authorize'
|
||||||
|
import { SocialError } from 'core/domain/common'
|
||||||
|
|
||||||
|
import { OAuthType } from 'core/domain/authorize/oauthType'
|
||||||
|
import moment from 'moment'
|
||||||
|
/**
|
||||||
|
* Firbase authorize service
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class AuthorizeService
|
||||||
|
* @implements {IAuthorizeService}
|
||||||
|
*/
|
||||||
|
export class AuthorizeService implements IAuthorizeService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login the user
|
||||||
|
*
|
||||||
|
* @returns {Promise<LoginUser>}
|
||||||
|
* @memberof IAuthorizeService
|
||||||
|
*/
|
||||||
|
public login: (email: string, password: string) => Promise<LoginUser> = (email, password) => {
|
||||||
|
|
||||||
|
return new Promise<LoginUser>((resolve, reject) => {
|
||||||
|
firebaseAuth()
|
||||||
|
.signInWithEmailAndPassword(email, password)
|
||||||
|
.then((result) => {
|
||||||
|
resolve(new LoginUser(result.uid, result.emailVerified))
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs out the user
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
* @memberof IAuthorizeService
|
||||||
|
*/
|
||||||
|
public logout: () => Promise<void> = () => {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
firebaseAuth()
|
||||||
|
.signOut()
|
||||||
|
.then((result) => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a user
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
* @memberof IAuthorizeService
|
||||||
|
*/
|
||||||
|
public registerUser: (user: User) => Promise<RegisterUserResult> = (user) => {
|
||||||
|
return new Promise<RegisterUserResult>((resolve, reject) => {
|
||||||
|
firebaseAuth()
|
||||||
|
.createUserWithEmailAndPassword(user.email as string, user.password as string)
|
||||||
|
.then((signupResult) => {
|
||||||
|
const {uid, email} = signupResult
|
||||||
|
this.storeUserInformation(uid,email,user.fullName,'').then(resolve)
|
||||||
|
})
|
||||||
|
.catch((error: any) => reject(new SocialError(error.code, error.message)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user password
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
* @memberof IAuthorizeService
|
||||||
|
*/
|
||||||
|
public updatePassword: (newPassword: string) => Promise<void> = (newPassword) => {
|
||||||
|
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
let user = firebaseAuth().currentUser
|
||||||
|
if (user) {
|
||||||
|
user.updatePassword(newPassword).then(() => {
|
||||||
|
// Update successful.
|
||||||
|
resolve()
|
||||||
|
}).catch((error: any) => {
|
||||||
|
// An error happened.
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On user authorization changed event
|
||||||
|
*
|
||||||
|
* @memberof IAuthorizeService
|
||||||
|
*/
|
||||||
|
public onAuthStateChanged: (callBack: (isVerifide: boolean, user: User) => void) => any = (callBack) => {
|
||||||
|
firebaseAuth().onAuthStateChanged( (user: any) => {
|
||||||
|
let isVerifide = false
|
||||||
|
if (user) {
|
||||||
|
if (user.emailVerified || user.providerData[0].providerId.trim() !== 'password') {
|
||||||
|
isVerifide = true
|
||||||
|
} else {
|
||||||
|
isVerifide = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callBack(isVerifide,user)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset user password
|
||||||
|
*
|
||||||
|
* @memberof AuthorizeService
|
||||||
|
*/
|
||||||
|
public resetPassword: (email: string) => Promise<void> = (email) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
let auth = firebaseAuth()
|
||||||
|
|
||||||
|
auth.sendPasswordResetEmail(email).then(function () {
|
||||||
|
resolve()
|
||||||
|
}).catch((error: any) => {
|
||||||
|
// An error happened.
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send verfication email to user email
|
||||||
|
*
|
||||||
|
* @memberof AuthorizeService
|
||||||
|
*/
|
||||||
|
public sendEmailVerification: () => Promise<void> = () => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
let auth = firebaseAuth()
|
||||||
|
const user = auth.currentUser
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
user.sendEmailVerification().then(() => {
|
||||||
|
resolve()
|
||||||
|
}).catch((error: any) => {
|
||||||
|
// An error happened.
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
reject(new SocialError('authorizeService/nullException', 'User was null!'))
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public loginWithOAuth: (type: OAuthType) => Promise<LoginUser> = (type) => {
|
||||||
|
return new Promise<LoginUser>((resolve,reject) => {
|
||||||
|
|
||||||
|
let provider: any
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case OAuthType.GITHUB:
|
||||||
|
provider = new firebaseAuth.GithubAuthProvider()
|
||||||
|
break
|
||||||
|
case OAuthType.FACEBOOK:
|
||||||
|
provider = new firebaseAuth.FacebookAuthProvider()
|
||||||
|
break
|
||||||
|
case OAuthType.GOOGLE:
|
||||||
|
provider = new firebaseAuth.GoogleAuthProvider()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new SocialError('authorizeService/loginWithOAuth','None of OAuth type is matched!')
|
||||||
|
}
|
||||||
|
firebaseAuth().signInWithPopup(provider).then((result) => {
|
||||||
|
// This gives you a GitHub Access Token. You can use it to access the GitHub API.
|
||||||
|
let token = result.credential.accessToken
|
||||||
|
// The signed-in user info.
|
||||||
|
const {user} = result
|
||||||
|
const {credential} = result
|
||||||
|
const {uid, displayName, email, photoURL} = user
|
||||||
|
const {accessToken, providerId} = credential
|
||||||
|
this.storeUserProviderData(uid,email,displayName,photoURL,providerId,accessToken)
|
||||||
|
// this.storeUserInformation(uid,email,displayName,photoURL).then(resolve)
|
||||||
|
resolve(new LoginUser(user.uid,true,providerId,displayName,email,photoURL))
|
||||||
|
|
||||||
|
}).catch(function (error: any) {
|
||||||
|
// Handle Errors here.
|
||||||
|
let errorCode = error.code
|
||||||
|
let errorMessage = error.message
|
||||||
|
// The email of the user's account used.
|
||||||
|
let email = error.email
|
||||||
|
// The firebase.auth.AuthCredential type that was used.
|
||||||
|
let credential = error.credential
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store user information
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @memberof AuthorizeService
|
||||||
|
*/
|
||||||
|
private storeUserInformation = (userId: string, email: string, fullName: string, avatar: string) => {
|
||||||
|
return new Promise<RegisterUserResult>((resolve,reject) => {
|
||||||
|
db.doc(`userInfo/${userId}`).set(
|
||||||
|
{
|
||||||
|
avatar,
|
||||||
|
fullName,
|
||||||
|
creationDate: moment().unix(),
|
||||||
|
email
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
alert(userId)
|
||||||
|
resolve(new RegisterUserResult(userId))
|
||||||
|
})
|
||||||
|
.catch((error: any) => reject(new SocialError(error.name, 'firestore/storeUserInformation : ' + error.message)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store user provider information
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @memberof AuthorizeService
|
||||||
|
*/
|
||||||
|
private storeUserProviderData = (
|
||||||
|
userId: string,
|
||||||
|
email: string,
|
||||||
|
fullName: string,
|
||||||
|
avatar: string,
|
||||||
|
providerId: string,
|
||||||
|
accessToken: string
|
||||||
|
) => {
|
||||||
|
return new Promise<RegisterUserResult>((resolve,reject) => {
|
||||||
|
db.doc(`userProviderInfo/${userId}`)
|
||||||
|
.set(
|
||||||
|
{
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
fullName,
|
||||||
|
avatar,
|
||||||
|
providerId,
|
||||||
|
accessToken
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
resolve(new RegisterUserResult(userId))
|
||||||
|
})
|
||||||
|
.catch((error: any) => reject(new SocialError(error.name, error.message)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/authorize/index.ts
Normal file
5
src/data/firestoreClient/services/authorize/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { AuthorizeService } from './AuthorizeService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
AuthorizeService
|
||||||
|
}
|
||||||
119
src/data/firestoreClient/services/circles/CircleService.ts
Normal file
119
src/data/firestoreClient/services/circles/CircleService.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
// - Import react components
|
||||||
|
import { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
|
||||||
|
|
||||||
|
import { SocialError } from 'core/domain/common'
|
||||||
|
import { ICircleService } from 'core/services/circles'
|
||||||
|
import { Circle, UserFollower } from 'core/domain/circles'
|
||||||
|
import { User } from 'core/domain/users'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firbase circle service
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class CircleService
|
||||||
|
* @implements {ICircleService}
|
||||||
|
*/
|
||||||
|
export class CircleService implements ICircleService {
|
||||||
|
|
||||||
|
public addCircle: (userId: string, circle: Circle)
|
||||||
|
=> Promise<string> = (userId, circle) => {
|
||||||
|
return new Promise<string>((resolve,reject) => {
|
||||||
|
let circleRef = db.doc(`users/${userId}`).collection(`circles`).add(circle)
|
||||||
|
circleRef.then((result) => {
|
||||||
|
resolve(result.id as string)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public addFollowingUser: (userId: string, circleId: string, userCircle: User, userFollower: UserFollower, userFollowingId: string)
|
||||||
|
=> Promise<void> = (userId, circleId, userCircle, userFollower, userFollowingId) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const followerRef = db.doc(`users/${userId}/circles/${circleId}/users/${userFollowingId}`)
|
||||||
|
const followingRef = db.doc(`users/${userFollowingId}/circles/-Followers/users/${userId}`)
|
||||||
|
|
||||||
|
batch.update(followerRef, userCircle)
|
||||||
|
batch.update(followingRef, userFollower)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
public deleteFollowingUser: (userId: string, circleId: string, userFollowingId: string)
|
||||||
|
=> Promise<void> = (userId, circleId, userFollowingId) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
|
||||||
|
const batch = db.batch()
|
||||||
|
const followerRef = db.doc(`users/${userId}/circles/${circleId}/users/${userFollowingId}`)
|
||||||
|
const followingRef = db.doc(`users/${userFollowingId}/circles/-Followers/users/${userId}`)
|
||||||
|
|
||||||
|
batch.delete(followerRef)
|
||||||
|
batch.delete(followingRef)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
public updateCircle: (userId: string, circleId: string, circle: Circle)
|
||||||
|
=> Promise<void> = (userId, circleId, circle) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
|
||||||
|
const batch = db.batch()
|
||||||
|
const circleRef = db.doc(`users/${userId}/circles/${circleId}`)
|
||||||
|
|
||||||
|
batch.update(circleRef,circle)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteCircle: (userId: string, circleId: string)
|
||||||
|
=> Promise<void> = (userId, circleId) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
|
||||||
|
const batch = db.batch()
|
||||||
|
const circleRef = db.doc(`users/${userId}/circles/${circleId}`)
|
||||||
|
|
||||||
|
batch.delete(circleRef)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
public getCircles: (userId: string) => Promise<{ [circleId: string]: Circle }> = (userId) => {
|
||||||
|
return new Promise<{ [circleId: string]: Circle }>((resolve,reject) => {
|
||||||
|
let circlesRef = db.doc(`users/${userId}`).collection(`circles`)
|
||||||
|
|
||||||
|
circlesRef.onSnapshot((snapshot) => {
|
||||||
|
let parsedData: { [circleId: string]: Circle } = {}
|
||||||
|
snapshot.forEach((result) => {
|
||||||
|
parsedData[result.id] = {
|
||||||
|
id: result.id,
|
||||||
|
...result.data() as Circle
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resolve(parsedData)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/circles/index.ts
Normal file
5
src/data/firestoreClient/services/circles/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { CircleService } from './CircleService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
CircleService
|
||||||
|
}
|
||||||
133
src/data/firestoreClient/services/comments/CommentService.ts
Normal file
133
src/data/firestoreClient/services/comments/CommentService.ts
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// - Import react components
|
||||||
|
import { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import { SocialError } from 'core/domain/common'
|
||||||
|
import { ICommentService } from 'core/services/comments'
|
||||||
|
import { Comment } from 'core/domain/comments'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firbase comment service
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class CommentService
|
||||||
|
* @implements {ICommentService}
|
||||||
|
*/
|
||||||
|
export class CommentService implements ICommentService {
|
||||||
|
public addComment: (comment: Comment)
|
||||||
|
=> Promise<string> = (comment) => {
|
||||||
|
return new Promise<string>((resolve,reject) => {
|
||||||
|
const postRef = db.doc(`posts/${comment.postId}`)
|
||||||
|
let commentRef = postRef.collection('comments')
|
||||||
|
commentRef.add(comment).then((result) => {
|
||||||
|
resolve(result.id)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add comment counter and three comments' slide preview
|
||||||
|
*/
|
||||||
|
db.runTransaction((transaction) => {
|
||||||
|
return transaction.get(postRef).then((postDoc) => {
|
||||||
|
if (postDoc.exists) {
|
||||||
|
const commentCount = postDoc.data().commentCounter + 1
|
||||||
|
transaction.update(postRef, { commentCounter: commentCount })
|
||||||
|
let comments = postDoc.data()
|
||||||
|
if (!comments) {
|
||||||
|
comments = {}
|
||||||
|
}
|
||||||
|
if (commentCount < 4) {
|
||||||
|
transaction.update(postRef, { comments: { ...comments, [result.id]: comment } })
|
||||||
|
} else {
|
||||||
|
let sortedObjects = comments
|
||||||
|
// Sort posts with creation date
|
||||||
|
sortedObjects.sort((a: any, b: any) => {
|
||||||
|
return parseInt(b.creationDate,10) - parseInt(a.creationDate,10)
|
||||||
|
})
|
||||||
|
const lastCommentId = Object.keys(sortedObjects)[2]
|
||||||
|
comments[lastCommentId] = {... comment}
|
||||||
|
transaction.update(postRef, { comments: { ...comments} })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public getComments: (postId: string, callback: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void)
|
||||||
|
=> void = (postId, callback) => {
|
||||||
|
let commentsRef = db.doc(`posts/${postId}`).collection(`comments`)
|
||||||
|
commentsRef.onSnapshot((snapshot) => {
|
||||||
|
let parsedData: {[postId: string]: {[commentId: string]: Comment}} = {[postId]: {}}
|
||||||
|
snapshot.forEach((result) => {
|
||||||
|
parsedData[postId][result.id] = {
|
||||||
|
id: result.id,
|
||||||
|
...result.data() as Comment
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (callback) {
|
||||||
|
callback(parsedData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateComment: (comment: Comment)
|
||||||
|
=> Promise<void> = (comment) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const commentRef = db.doc(`posts/${comment.postId}/comments/${comment.id}`)
|
||||||
|
|
||||||
|
batch.update(commentRef, comment)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteComment: (commentId: string, postId: string)
|
||||||
|
=> Promise<void> = (commentId, postId) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const postRef = db.doc(`posts/${postId}`)
|
||||||
|
const commentRef = postRef.collection(`comments`).doc(commentId)
|
||||||
|
|
||||||
|
batch.delete(commentRef)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete comment counter and comments' slide preview
|
||||||
|
*/
|
||||||
|
db.runTransaction((transaction) => {
|
||||||
|
return transaction.get(postRef).then((postDoc) => {
|
||||||
|
if (postDoc.exists) {
|
||||||
|
const commentCount = postDoc.data().commentCounter - 1
|
||||||
|
transaction.update(postRef, { commentCounter: commentCount })
|
||||||
|
if (commentCount > 3) {
|
||||||
|
let comments = postDoc.data().comments
|
||||||
|
if (!comments) {
|
||||||
|
comments = {}
|
||||||
|
}
|
||||||
|
let parsedComments = {}
|
||||||
|
Object.keys(postDoc.data().comments).map((id) => {
|
||||||
|
if (id !== commentId) {
|
||||||
|
_.merge(parsedComments, { [id]: { ...comments[id] } })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
transaction.update(postRef, { comments: { ...parsedComments}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/comments/index.ts
Normal file
5
src/data/firestoreClient/services/comments/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { CommentService } from './CommentService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
CommentService
|
||||||
|
}
|
||||||
16
src/data/firestoreClient/services/common/CommonService.ts
Normal file
16
src/data/firestoreClient/services/common/CommonService.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// - Import react components
|
||||||
|
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
|
||||||
|
|
||||||
|
import { SocialError } from 'core/domain/common'
|
||||||
|
import { ICommonService } from 'core/services/common'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firbase common service
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class CommonService
|
||||||
|
* @implements {ICommonService}
|
||||||
|
*/
|
||||||
|
export class CommonService implements ICommonService {
|
||||||
|
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/common/index.ts
Normal file
5
src/data/firestoreClient/services/common/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { CommonService } from './CommonService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
CommonService
|
||||||
|
}
|
||||||
41
src/data/firestoreClient/services/files/StorageService.ts
Normal file
41
src/data/firestoreClient/services/files/StorageService.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { storageRef } from 'data/firestoreClient'
|
||||||
|
import { IStorageService } from 'core/services/files'
|
||||||
|
import { FileResult } from 'models/files/fileResult'
|
||||||
|
|
||||||
|
export class StorageService implements IStorageService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload image on the server
|
||||||
|
* @param {file} file
|
||||||
|
* @param {string} fileName
|
||||||
|
*/
|
||||||
|
public uploadFile = (file: any, fileName: string, progress: (percentage: number, status: boolean) => void) => {
|
||||||
|
|
||||||
|
return new Promise<FileResult>((resolve, reject) => {
|
||||||
|
// Create a storage refrence
|
||||||
|
let storegeFile = storageRef.child(`images/${fileName}`)
|
||||||
|
|
||||||
|
// Upload file
|
||||||
|
let task = storegeFile.put(file)
|
||||||
|
task.then((result) => {
|
||||||
|
resolve(new FileResult(result.downloadURL!,result.metadata.fullPath))
|
||||||
|
}).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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/files/index.ts
Normal file
5
src/data/firestoreClient/services/files/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { StorageService } from './StorageService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
StorageService
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { FileResult } from 'models/files/fileResult'
|
||||||
|
// - Import react components
|
||||||
|
import { firebaseRef, firebaseAuth, storageRef, db } from 'data/firestoreClient'
|
||||||
|
|
||||||
|
import { SocialError } from 'core/domain/common'
|
||||||
|
import { IImageGalleryService } from 'core/services/imageGallery'
|
||||||
|
import { Image } from 'core/domain/imageGallery'
|
||||||
|
import { IStorageService } from 'core/services/files'
|
||||||
|
import { IServiceProvider, ServiceProvide } from 'core/factories'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firbase image gallery service
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class ImageGalleryService
|
||||||
|
* @implements {IImageGalleryService}
|
||||||
|
*/
|
||||||
|
export class ImageGalleryService implements IImageGalleryService {
|
||||||
|
|
||||||
|
private readonly storageService: IStorageService
|
||||||
|
private readonly serviceProvider: IServiceProvider
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
this.serviceProvider = new ServiceProvide()
|
||||||
|
this.storageService = this.serviceProvider.createStorageService()
|
||||||
|
}
|
||||||
|
|
||||||
|
public getImageGallery: (userId: string)
|
||||||
|
=> Promise<Image[]> = (userId) => {
|
||||||
|
return new Promise<Image[]>((resolve,reject) => {
|
||||||
|
let imagesRef = db.doc(`users/${userId}`).collection(`images`)
|
||||||
|
|
||||||
|
imagesRef.get().then((snapshot) => {
|
||||||
|
let parsedData: Image[] = []
|
||||||
|
snapshot.forEach((result) => {
|
||||||
|
parsedData.push({
|
||||||
|
id: result.id,
|
||||||
|
...result.data() as Image
|
||||||
|
})
|
||||||
|
})
|
||||||
|
resolve(parsedData)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveImage: (userId: string, image: Image)
|
||||||
|
=> Promise<string> = (userId, image) => {
|
||||||
|
return new Promise<string>((resolve,reject) => {
|
||||||
|
|
||||||
|
let imageRef = db.doc(`users/${userId}`).collection(`images`).add(image)
|
||||||
|
imageRef.then((result) => {
|
||||||
|
resolve(result.id!)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteImage: (userId: string, imageId: string)
|
||||||
|
=> Promise<void> = (userId, imageId) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const imageRef = db.doc(`users/${userId}/images/${imageId}`)
|
||||||
|
|
||||||
|
batch.delete(imageRef)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public uploadImage: (image: any, imageName: string, progressCallback: (percentage: number, status: boolean) => void)
|
||||||
|
=> Promise<FileResult> = (image, imageName, progressCallback) => {
|
||||||
|
return new Promise<FileResult>((resolve,reject) => {
|
||||||
|
this.storageService.uploadFile(image,imageName,progressCallback)
|
||||||
|
.then((result: FileResult) => {
|
||||||
|
resolve(result)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public downloadImage: (fileName: string)
|
||||||
|
=> Promise<string> = (fileName) => {
|
||||||
|
return new Promise<string>((resolve,reject) => {
|
||||||
|
|
||||||
|
// Create a reference to the file we want to download
|
||||||
|
let starsRef: any = storageRef.child(`images/${fileName}`)
|
||||||
|
|
||||||
|
// Get the download URL
|
||||||
|
starsRef.getDownloadURL().then((url: string) => {
|
||||||
|
resolve(url)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/imageGallery/index.ts
Normal file
5
src/data/firestoreClient/services/imageGallery/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { ImageGalleryService } from './ImageGalleryService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
ImageGalleryService
|
||||||
|
}
|
||||||
24
src/data/firestoreClient/services/index.ts
Normal file
24
src/data/firestoreClient/services/index.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { AuthorizeService } from './authorize'
|
||||||
|
import { CircleService } from './circles'
|
||||||
|
import { CommentService } from './comments'
|
||||||
|
import { CommonService } from './common'
|
||||||
|
import { ImageGalleryService } from './imageGallery'
|
||||||
|
import { NotificationService } from './notifications'
|
||||||
|
import { PostService } from './posts'
|
||||||
|
import { UserService } from './users'
|
||||||
|
import { VoteService } from './votes'
|
||||||
|
import { StorageService } from './files'
|
||||||
|
|
||||||
|
export {
|
||||||
|
AuthorizeService,
|
||||||
|
CircleService,
|
||||||
|
CommentService,
|
||||||
|
CommonService,
|
||||||
|
ImageGalleryService,
|
||||||
|
NotificationService,
|
||||||
|
PostService,
|
||||||
|
UserService,
|
||||||
|
VoteService,
|
||||||
|
StorageService
|
||||||
|
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/notifications/index.ts
Normal file
5
src/data/firestoreClient/services/notifications/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { NotificationService } from './NotificationService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
NotificationService
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// - Import react components
|
||||||
|
import { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
|
||||||
|
|
||||||
|
import { SocialError } from 'core/domain/common'
|
||||||
|
import { Notification } from 'core/domain/notifications'
|
||||||
|
import { INotificationService } from 'core/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) => {
|
||||||
|
db.doc(`users/${notification.notifyRecieverUserId}`).collection(`notifications`)
|
||||||
|
.add(notification)
|
||||||
|
.then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNotifications: (userId: string, callback: (resultNotifications: {[notifyId: string]: Notification}) => void)
|
||||||
|
=> void = (userId,callback) => {
|
||||||
|
let notificationsRef = db.doc(`users/${userId}`).collection('notifications')
|
||||||
|
notificationsRef.onSnapshot((snapshot) => {
|
||||||
|
let parsedData: { [notifyId: string]: Notification } = {}
|
||||||
|
snapshot.forEach((result) => {
|
||||||
|
parsedData[result.id] = {
|
||||||
|
id: result.id,
|
||||||
|
...result.data() as Notification
|
||||||
|
}
|
||||||
|
})
|
||||||
|
callback(parsedData)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteNotification: (notificationId: string, userId: string)
|
||||||
|
=> Promise < void > = (notificationId, userId) => {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const notificationRef = db.doc(`users/${userId}/notifications/${notificationId}`)
|
||||||
|
|
||||||
|
batch.delete(notificationRef)
|
||||||
|
batch.commit().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) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const notificationRef = db.doc(`users/${userId}/notifications/${notificationId}`)
|
||||||
|
|
||||||
|
batch.update(notificationRef,notification)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
105
src/data/firestoreClient/services/posts/PostService.ts
Normal file
105
src/data/firestoreClient/services/posts/PostService.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
// - Import react components
|
||||||
|
import { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
|
||||||
|
|
||||||
|
import { SocialError } from 'core/domain/common'
|
||||||
|
import { Post } from 'core/domain/posts'
|
||||||
|
import { IPostService } from 'core/services/posts'
|
||||||
|
import { IServiceProvider } from 'core/factories'
|
||||||
|
import { ICommentService } from 'core/services/comments'
|
||||||
|
import { ServiceProvide } from 'core/factories/serviceProvide'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firbase post service
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class PostService
|
||||||
|
* @implements {IPostService}
|
||||||
|
*/
|
||||||
|
export class PostService implements IPostService {
|
||||||
|
|
||||||
|
public addPost: (post: Post)
|
||||||
|
=> Promise<string> = (post) => {
|
||||||
|
return new Promise<string>((resolve,reject) => {
|
||||||
|
let postRef = db.collection(`posts`).add(post)
|
||||||
|
postRef.then((result) => {
|
||||||
|
resolve(result.id)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public updatePost: (post: Post)
|
||||||
|
=> Promise<void> = (post) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const notificationRef = db.doc(`posts/${post.id}`)
|
||||||
|
|
||||||
|
batch.update(notificationRef, post)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public deletePost: (postId: string)
|
||||||
|
=> Promise<void> = (postId) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const notificationRef = db.doc(`posts/${postId}`)
|
||||||
|
|
||||||
|
batch.delete(notificationRef)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPosts: (userId: string)
|
||||||
|
=> Promise<{ [postId: string]: Post }> = (userId) => {
|
||||||
|
return new Promise<{ [postId: string]: Post }>((resolve,reject) => {
|
||||||
|
|
||||||
|
let postsRef = db.collection(`posts`).where('ownerUserId', '==', userId)
|
||||||
|
postsRef.get().then((snapshot) => {
|
||||||
|
let parsedData: { [postId: string]: Post } = {}
|
||||||
|
snapshot.forEach((result) => {
|
||||||
|
parsedData[result.id] = {
|
||||||
|
id: result.id,
|
||||||
|
...result.data() as Post
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resolve(parsedData)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPostById: (postId: string)
|
||||||
|
=> Promise<Post> = (postId) => {
|
||||||
|
return new Promise<Post>((resolve,reject) => {
|
||||||
|
|
||||||
|
let postsRef = db.doc(`posts/${postId}`)
|
||||||
|
postsRef.get().then((snapshot) => {
|
||||||
|
let newPost = snapshot.data() || {}
|
||||||
|
let post: Post = {
|
||||||
|
id: postId,
|
||||||
|
...newPost
|
||||||
|
}
|
||||||
|
resolve(post)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/posts/index.ts
Normal file
5
src/data/firestoreClient/services/posts/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { PostService } from './PostService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
PostService
|
||||||
|
}
|
||||||
92
src/data/firestoreClient/services/users/UserService.ts
Normal file
92
src/data/firestoreClient/services/users/UserService.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// - Import react components
|
||||||
|
import { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
|
||||||
|
import firebase from 'firebase'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
import { SocialError } from 'core/domain/common'
|
||||||
|
import { Profile, UserProvider } from 'core/domain/users'
|
||||||
|
import { IUserService } from 'core/services/users'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firbase user service
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class UserService
|
||||||
|
* @implements {IUserService}
|
||||||
|
*/
|
||||||
|
export class UserService implements IUserService {
|
||||||
|
|
||||||
|
public getUserProfile: (userId: string)
|
||||||
|
=> Promise<Profile> = (userId) => {
|
||||||
|
return new Promise<Profile>((resolve, reject) => {
|
||||||
|
let userProfileRef = db.doc(`userInfo/${userId}`)
|
||||||
|
userProfileRef.get().then((snapshot) => {
|
||||||
|
if (!snapshot.exists) {
|
||||||
|
this.getUserProviderData(userId).then((providerData: UserProvider) => {
|
||||||
|
const {avatar,fullName, email} = providerData
|
||||||
|
const userProfile = new Profile(avatar,fullName,'','',moment().unix(),email)
|
||||||
|
resolve(userProfile)
|
||||||
|
this.updateUserProfile(userId,userProfile)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
resolve(snapshot.data() as Profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch((error: any) => reject(new SocialError(error.code, 'firestore/getUserProfile :' + error.message)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateUserProfile: (userId: string, profile: Profile)
|
||||||
|
=> Promise<void> = (userId, profile) => {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const profileRef = db.doc(`userInfo/${userId}`)
|
||||||
|
|
||||||
|
batch.set(profileRef,{...profile})
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch((error: any) => reject(new SocialError(error.code, 'firestore/updateUserProfile' + error.message)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
public getUsersProfile: (userId: string, lastKey?: string, numberOfItems?: number)
|
||||||
|
=> Promise<{ [userId: string]: Profile }> = (userId, lastKey, numberOfItems = 15) => {
|
||||||
|
return new Promise<{ [userId: string]: Profile }>((resolve, reject) => {
|
||||||
|
let usersProfileRef: firebase.firestore.Query
|
||||||
|
if (lastKey) {
|
||||||
|
usersProfileRef = db.collection(`userInfo`).orderBy('creationDate', 'desc').startAfter(lastKey!).limit(numberOfItems)
|
||||||
|
} else {
|
||||||
|
usersProfileRef = db.collection(`userInfo`).orderBy('creationDate', 'desc')
|
||||||
|
}
|
||||||
|
|
||||||
|
usersProfileRef.get().then((snapshot) => {
|
||||||
|
let parsedData: { [userId: string]: Profile } = {}
|
||||||
|
snapshot.forEach((result) => {
|
||||||
|
parsedData[result.id] = {
|
||||||
|
...result.data() as Profile
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resolve(parsedData)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private getUserProviderData = (userId: string) => {
|
||||||
|
return new Promise<UserProvider>((resolve,reject) => {
|
||||||
|
let userProviderRef = db.doc(`userProviderInfo/${userId}`)
|
||||||
|
userProviderRef.get().then((snapshot) => {
|
||||||
|
let userProvider: UserProvider = snapshot.data() as UserProvider || {}
|
||||||
|
resolve(userProvider)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code, 'firestore/getUserProviderData' + error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/users/index.ts
Normal file
5
src/data/firestoreClient/services/users/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { UserService } from './UserService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
UserService
|
||||||
|
}
|
||||||
106
src/data/firestoreClient/services/votes/VoteService.ts
Normal file
106
src/data/firestoreClient/services/votes/VoteService.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
// - Import react components
|
||||||
|
import { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
|
||||||
|
|
||||||
|
import { SocialError } from 'core/domain/common'
|
||||||
|
import { Vote } from 'core/domain/votes'
|
||||||
|
import { IVoteService } from 'core/services/votes'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firbase vote service
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class VoteService
|
||||||
|
* @implements {IVoteService}
|
||||||
|
*/
|
||||||
|
export class VoteService implements IVoteService {
|
||||||
|
|
||||||
|
public addVote: (vote: Vote)
|
||||||
|
=> Promise<string> = (vote) => {
|
||||||
|
return new Promise<string>((resolve,reject) => {
|
||||||
|
const postRef = db.doc(`posts/${vote.postId}`)
|
||||||
|
let voteRef = postRef.collection(`votes`)
|
||||||
|
.add(vote)
|
||||||
|
voteRef.then((result) => {
|
||||||
|
resolve(result.id)
|
||||||
|
/**
|
||||||
|
* Add score
|
||||||
|
*/
|
||||||
|
db.runTransaction((transaction) => {
|
||||||
|
return transaction.get(postRef).then((postDoc) => {
|
||||||
|
if (postDoc.exists) {
|
||||||
|
const post = postDoc.data()
|
||||||
|
let {votes, score} = post
|
||||||
|
if (!votes) {
|
||||||
|
votes = {}
|
||||||
|
}
|
||||||
|
if (!score) {
|
||||||
|
score = 0
|
||||||
|
}
|
||||||
|
const newScore = score + 1
|
||||||
|
votes[vote.userId] = true
|
||||||
|
transaction.update(postRef, { votes: { ...votes}, score: newScore })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
public getVotes: (postId: string)
|
||||||
|
=> Promise<{ [postId: string]: { [voteId: string]: Vote } }> = (postId) => {
|
||||||
|
return new Promise<{ [postId: string]: { [voteId: string]: Vote } }>((resolve,reject) => {
|
||||||
|
let votesRef = db.doc(`posts/${postId}`).collection(`votes`)
|
||||||
|
|
||||||
|
votesRef.onSnapshot((snapshot) => {
|
||||||
|
let parsedData: {[postId: string]: {[voteId: string]: Vote}} = {[postId]: {}}
|
||||||
|
snapshot.forEach((result) => {
|
||||||
|
parsedData[postId][result.id] = {
|
||||||
|
id: result.id,
|
||||||
|
...result.data() as Vote
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resolve(parsedData)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteVote: (vote: Vote)
|
||||||
|
=> Promise<void> = (vote) => {
|
||||||
|
return new Promise<void>((resolve,reject) => {
|
||||||
|
const batch = db.batch()
|
||||||
|
const postRef = db.doc(`posts/${vote.postId}`)
|
||||||
|
let voteRef = postRef.collection(`votes`).doc(vote.id!)
|
||||||
|
|
||||||
|
batch.delete(voteRef)
|
||||||
|
batch.commit().then(() => {
|
||||||
|
resolve()
|
||||||
|
/**
|
||||||
|
* Remove score
|
||||||
|
*/
|
||||||
|
db.runTransaction((transaction) => {
|
||||||
|
return transaction.get(postRef).then((postDoc) => {
|
||||||
|
if (postDoc.exists) {
|
||||||
|
const post = postDoc.data()
|
||||||
|
let {votes, score} = post
|
||||||
|
if (!votes) {
|
||||||
|
votes = {}
|
||||||
|
}
|
||||||
|
if (!score) {
|
||||||
|
score = 0
|
||||||
|
}
|
||||||
|
const newScore = score + 1
|
||||||
|
votes[vote.userId] = true
|
||||||
|
transaction.update(postRef, { votes: { ...votes}, score: newScore })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(new SocialError(error.code,error.message))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/data/firestoreClient/services/votes/index.ts
Normal file
5
src/data/firestoreClient/services/votes/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { VoteService } from './VoteService'
|
||||||
|
|
||||||
|
export {
|
||||||
|
VoteService
|
||||||
|
}
|
||||||
@@ -12,9 +12,6 @@ import { Provider } from 'react-redux'
|
|||||||
import store, { history } from 'store/configureStore'
|
import store, { history } from 'store/configureStore'
|
||||||
import { ConnectedRouter } from 'react-router-redux'
|
import { ConnectedRouter } from 'react-router-redux'
|
||||||
|
|
||||||
import 'babel-core/register'
|
|
||||||
import 'babel-polyfill'
|
|
||||||
|
|
||||||
// - Import app components
|
// - Import app components
|
||||||
import Master from 'components/master'
|
import Master from 'components/master'
|
||||||
// import { App } from 'components/AWS'
|
// import { App } from 'components/AWS'
|
||||||
@@ -23,9 +20,10 @@ import Master from 'components/master'
|
|||||||
// tslint:disable-next-line:no-empty
|
// tslint:disable-next-line:no-empty
|
||||||
store.subscribe(() => { })
|
store.subscribe(() => { })
|
||||||
|
|
||||||
|
|
||||||
// Needed for onTouchTap
|
// Needed for onTouchTap
|
||||||
// http://stackoverflow.com/a/34015469/988941
|
// http://stackoverflow.com/a/34015469/988941
|
||||||
injectTapEventPlugin()
|
try { injectTapEventPlugin() } catch (e) { }
|
||||||
|
|
||||||
// This replaces the textColor value on the palette
|
// This replaces the textColor value on the palette
|
||||||
// and then update the keys for each component that depends on it.
|
// and then update the keys for each component that depends on it.
|
||||||
@@ -37,17 +35,6 @@ const muiTheme = getMuiTheme({
|
|||||||
// App css
|
// App css
|
||||||
import 'applicationStyles'
|
import 'applicationStyles'
|
||||||
const supportsHistory = 'pushState' in window.history
|
const supportsHistory = 'pushState' in window.history
|
||||||
|
|
||||||
// ReactDOM.render(
|
|
||||||
// <Provider store={store}>
|
|
||||||
// <ConnectedRouter history={history}>
|
|
||||||
// <MuiThemeProvider muiTheme={getMuiTheme(lightBaseTheme)}>
|
|
||||||
// <Master />
|
|
||||||
// </MuiThemeProvider>
|
|
||||||
// </ConnectedRouter>
|
|
||||||
// </Provider>,
|
|
||||||
// document.getElementById('app')
|
|
||||||
// )
|
|
||||||
const render = (Component: any) => {
|
const render = (Component: any) => {
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<AppContainer warnings={false}>
|
<AppContainer warnings={false}>
|
||||||
@@ -68,5 +55,5 @@ render(Master)
|
|||||||
|
|
||||||
// Webpack Hot Module Replacement API
|
// Webpack Hot Module Replacement API
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
module.hot.accept('components/master', () => { render(Master) })
|
module.hot.accept()
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ export class AuthorizeState {
|
|||||||
* @type {Boolean}
|
* @type {Boolean}
|
||||||
* @memberof AuthorizeState
|
* @memberof AuthorizeState
|
||||||
*/
|
*/
|
||||||
isVerfide: Boolean = false
|
isVerifide: Boolean = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If user password is updated {true} or not {false}
|
* If user password is updated {true} or not {false}
|
||||||
|
|||||||
@@ -61,10 +61,10 @@ export let commentReducer = (state: CommentState = new CommentState(), action: I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case CommentActionType.DELETE_COMMENT:
|
case CommentActionType.DELETE_COMMENT:
|
||||||
let parsedComments = {}
|
|
||||||
if (!state.postComments![payload.postId]) {
|
if (!state.postComments![payload.postId]) {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
let parsedComments = {}
|
||||||
Object.keys(state.postComments![payload.postId]).map((id) => {
|
Object.keys(state.postComments![payload.postId]).map((id) => {
|
||||||
if (id !== payload.id) {
|
if (id !== payload.id) {
|
||||||
_.merge(parsedComments, { [id]: { ...state.postComments![payload.postId][id] } })
|
_.merge(parsedComments, { [id]: { ...state.postComments![payload.postId][id] } })
|
||||||
|
|||||||
40
src/routes/HomeRouter.tsx
Normal file
40
src/routes/HomeRouter.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// - Import react components
|
||||||
|
import PrivateRoute from './PrivateRoute'
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
|
||||||
|
|
||||||
|
// - Import app components
|
||||||
|
import StreamComponent from 'components/stream'
|
||||||
|
import Profile from 'components/profile'
|
||||||
|
import PostPage from 'components/postPage'
|
||||||
|
import People from 'components/people'
|
||||||
|
|
||||||
|
import { IRouterProps } from './IRouterProps'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Home Router
|
||||||
|
*/
|
||||||
|
export class HomeRouter extends Component<IRouterProps, any> {
|
||||||
|
render () {
|
||||||
|
const { enabled, match, data } = this.props
|
||||||
|
return (
|
||||||
|
enabled ? (<Switch>
|
||||||
|
<PrivateRoute path='/people/:tab?' component={<People />} />
|
||||||
|
|
||||||
|
<PrivateRoute path='/tag/:tag' component={(
|
||||||
|
<div className='blog'><StreamComponent displayWriting={false} homeTitle={`#${match.params.tag}`} posts={data.mergedPosts} /></div>
|
||||||
|
)} />
|
||||||
|
<Route path='/:userId/posts/:postId/:tag?' component={PostPage} />
|
||||||
|
<Route path='/:userId' component={Profile} />
|
||||||
|
<PrivateRoute path='/' component={(
|
||||||
|
<div className='blog'><StreamComponent homeTitle='Home' posts={data.mergedPosts} displayWriting={true} /></div>
|
||||||
|
)} />
|
||||||
|
</Switch>)
|
||||||
|
: ''
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default withRouter(connect(null, null)(HomeRouter as any))
|
||||||
35
src/routes/IRoute.ts
Normal file
35
src/routes/IRoute.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Component } from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route interface
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface IRoute
|
||||||
|
*/
|
||||||
|
export interface IRoute {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React component that would be rendered in routing
|
||||||
|
*
|
||||||
|
* @type {Component}
|
||||||
|
* @memberof IRoute
|
||||||
|
*/
|
||||||
|
component: any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route path
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IRoute
|
||||||
|
*/
|
||||||
|
path: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If user is authorized {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IRoute
|
||||||
|
*/
|
||||||
|
authed?: boolean
|
||||||
|
|
||||||
|
}
|
||||||
26
src/routes/IRouterProps.ts
Normal file
26
src/routes/IRouterProps.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export interface IRouterProps {
|
||||||
|
/**
|
||||||
|
* Enable routing {true} or not {false}
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof IRouterProps
|
||||||
|
*/
|
||||||
|
enabled: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router data for the components in routing
|
||||||
|
*
|
||||||
|
* @type {*}
|
||||||
|
* @memberof IRouterProps
|
||||||
|
*/
|
||||||
|
data?: any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routing match
|
||||||
|
*
|
||||||
|
* @type {*}
|
||||||
|
* @memberof IRouterProps
|
||||||
|
*/
|
||||||
|
match: any
|
||||||
|
|
||||||
|
}
|
||||||
39
src/routes/MasterRouter.tsx
Normal file
39
src/routes/MasterRouter.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// - Import react components
|
||||||
|
import PublicRoute from './PublicRoute'
|
||||||
|
import PrivateRoute from './PrivateRoute'
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { Route, Switch, withRouter, Redirect, NavLink } from 'react-router-dom'
|
||||||
|
|
||||||
|
// - Import app components
|
||||||
|
import Home from 'components/home'
|
||||||
|
import Signup from 'components/signup'
|
||||||
|
import EmailVerification from 'components/emailVerification'
|
||||||
|
import Login from 'components/login'
|
||||||
|
import ResetPassword from 'components/resetPassword'
|
||||||
|
import Setting from 'components/setting'
|
||||||
|
|
||||||
|
import { IRouterProps } from './IRouterProps'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Master router
|
||||||
|
*/
|
||||||
|
export class MasterRouter extends Component<IRouterProps, any> {
|
||||||
|
render () {
|
||||||
|
const { enabled, match, data } = this.props
|
||||||
|
return (
|
||||||
|
enabled ? (<Switch>
|
||||||
|
<Route path='/signup' component={Signup} />
|
||||||
|
<Route path='/emailVerification' component={EmailVerification} />
|
||||||
|
<Route path='/settings' component={Setting} />
|
||||||
|
<Route path='/resetPassword' component={ResetPassword} />
|
||||||
|
<PublicRoute path='/login' component={<Login />} />
|
||||||
|
<Route render={() => <Home uid={data.uid} />} />
|
||||||
|
</Switch>)
|
||||||
|
: ''
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default withRouter(connect(null, null)(MasterRouter as any))
|
||||||
29
src/routes/PrivateRoute.tsx
Normal file
29
src/routes/PrivateRoute.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { Route, Redirect } from 'react-router-dom'
|
||||||
|
import { IRoute } from './IRoute'
|
||||||
|
|
||||||
|
export class PrivateRoute extends Component<IRoute, any> {
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {authed, path, component} = this.props
|
||||||
|
return (
|
||||||
|
<Route path={path} render={() => {
|
||||||
|
return (
|
||||||
|
authed
|
||||||
|
? (() => component)()
|
||||||
|
: <Redirect to='/login' />
|
||||||
|
)
|
||||||
|
}} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state: any, nexProps: IRoute) => {
|
||||||
|
const { authorize } = state
|
||||||
|
return {
|
||||||
|
authed: authorize.authed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(PrivateRoute as any)
|
||||||
29
src/routes/PublicRoute.tsx
Normal file
29
src/routes/PublicRoute.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { Route, Redirect } from 'react-router-dom'
|
||||||
|
import { IRoute } from './IRoute'
|
||||||
|
|
||||||
|
export class PublicRoute extends Component<IRoute, any> {
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {authed, path, component} = this.props
|
||||||
|
return (
|
||||||
|
<Route path={path} render={() => {
|
||||||
|
return (
|
||||||
|
authed
|
||||||
|
? <Redirect to='/' />
|
||||||
|
: (() => component)()
|
||||||
|
)
|
||||||
|
}} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state: any, nexProps: IRoute) => {
|
||||||
|
const { authorize } = state
|
||||||
|
return {
|
||||||
|
authed: authorize.authed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(PublicRoute as any)
|
||||||
7
src/routes/index.ts
Normal file
7
src/routes/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import MasterRouter from './MasterRouter'
|
||||||
|
import HomeRouter from './HomeRouter'
|
||||||
|
|
||||||
|
export {
|
||||||
|
MasterRouter,
|
||||||
|
HomeRouter
|
||||||
|
}
|
||||||
@@ -1,36 +1,37 @@
|
|||||||
{
|
{
|
||||||
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "es6",
|
"module": "es6",
|
||||||
"target": "es6",
|
"target": "es6",
|
||||||
"lib": ["es6","dom"],
|
"lib": ["es6", "dom"],
|
||||||
"types": ["reflect-metadata"],
|
"types": ["reflect-metadata"],
|
||||||
"allowSyntheticDefaultImports": true, // see below
|
"allowSyntheticDefaultImports": true, // see below
|
||||||
"baseUrl": "./src/", // enables you to import relative to this folder
|
"baseUrl": "./src/", // enables you to import relative to this folder
|
||||||
"paths": {
|
"paths": {
|
||||||
"src/*" : ["*"],
|
"src/*": ["*"],
|
||||||
"core/*" : ["core/*"],
|
"core/*": ["core/*"],
|
||||||
"data/*" : ["data/*"],
|
"routes/*": ["routes/*"],
|
||||||
"components/*" : ["components/*"],
|
"data/*": ["data/*"],
|
||||||
"store/*" : ["store/*"],
|
"components/*": ["components/*"],
|
||||||
"api/*" : ["api/*"],
|
"store/*": ["store/*"],
|
||||||
"layouts/*" : ["layouts/*"],
|
"api/*": ["api/*"],
|
||||||
"models/*" : ["models/*"]
|
"layouts/*": ["layouts/*"],
|
||||||
|
"models/*": ["models/*"]
|
||||||
|
},
|
||||||
|
"sourceMap": true, // make TypeScript generate sourcemaps
|
||||||
|
"outDir": "public", // output directory to build to (irrelevant because we use Webpack most of the time)
|
||||||
|
"jsx": "preserve", // enable JSX mode, but "preserve" tells TypeScript to not transform it (we'll use Babel)
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"allowJs": true
|
||||||
|
|
||||||
},
|
},
|
||||||
"sourceMap": true, // make TypeScript generate sourcemaps
|
"include": [
|
||||||
"outDir": "public", // output directory to build to (irrelevant because we use Webpack most of the time)
|
"src/**/*"
|
||||||
"jsx": "preserve", // enable JSX mode, but "preserve" tells TypeScript to not transform it (we'll use Babel)
|
],
|
||||||
"strict": true,
|
"exclude": [
|
||||||
"moduleResolution": "node",
|
"node_modules"
|
||||||
"experimentalDecorators": true,
|
]
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"allowJs": true
|
|
||||||
|
|
||||||
},
|
|
||||||
"include":[
|
|
||||||
"src/**/*"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
var webpack = require('webpack');
|
var webpack = require('webpack');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var envFile = require('node-env-file');
|
var envFile = require('node-env-file');
|
||||||
|
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
] : [
|
] : [
|
||||||
new webpack.HotModuleReplacementPlugin(),
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
new OpenBrowserPlugin({ url: `http://localhost:${process.env.PORT}` }),
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'process.env': {
|
'process.env': {
|
||||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
|
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
|
||||||
@@ -50,14 +52,15 @@ module.exports = {
|
|||||||
STORAGE_BUCKET: JSON.stringify(process.env.STORAGE_BUCKET),
|
STORAGE_BUCKET: JSON.stringify(process.env.STORAGE_BUCKET),
|
||||||
PROJECT_ID: JSON.stringify(process.env.PROJECT_ID),
|
PROJECT_ID: JSON.stringify(process.env.PROJECT_ID),
|
||||||
MESSAGING_SENDER_ID: JSON.stringify(process.env.MESSAGING_SENDER_ID),
|
MESSAGING_SENDER_ID: JSON.stringify(process.env.MESSAGING_SENDER_ID),
|
||||||
HOST_URL: JSON.stringify(process.env.HOST_URL)
|
HOST_URL: JSON.stringify(process.env.HOST_URL),
|
||||||
|
PORT: JSON.stringify(process.env.PORT)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
output: {
|
output: {
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
path: path.resolve(__dirname, './public'),
|
path: path.resolve(__dirname, './public'),
|
||||||
filename: 'bundle-v0.1.js',
|
filename: 'bundle-v0.3.js',
|
||||||
|
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -75,6 +78,7 @@ module.exports = {
|
|||||||
data: 'src/data',
|
data: 'src/data',
|
||||||
api: 'src/api',
|
api: 'src/api',
|
||||||
layouts: 'src/layouts',
|
layouts: 'src/layouts',
|
||||||
|
routes: 'src/routes',
|
||||||
models: 'src/models',
|
models: 'src/models',
|
||||||
store: 'src/store',
|
store: 'src/store',
|
||||||
applicationStyles: 'src/styles/app.scss',
|
applicationStyles: 'src/styles/app.scss',
|
||||||
|
|||||||
Reference in New Issue
Block a user